Merge branch 'feat/python' into main
This commit is contained in:
@@ -34,23 +34,30 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export let settings;
|
||||
import '../tailwind.css';
|
||||
import { SvelteToast, toast } from '@zerodevx/svelte-toast';
|
||||
import { page, session } from '$app/stores';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { onMount } from 'svelte';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { asyncSleep } from '$lib/components/common';
|
||||
import { del, get, post } from '$lib/api';
|
||||
import { dev } from '$app/env';
|
||||
import { features } from '$lib/store';
|
||||
let isUpdateAvailable = false;
|
||||
import { features, isTraefikUsed } from '$lib/store';
|
||||
import { navigating } from '$app/stores';
|
||||
import PageLoader from '$lib/components/PageLoader.svelte';
|
||||
|
||||
$isTraefikUsed = settings?.isTraefikUsed || false;
|
||||
|
||||
let isUpdateAvailable = false;
|
||||
let updateStatus = {
|
||||
found: false,
|
||||
loading: false,
|
||||
success: null
|
||||
};
|
||||
let latestVersion = 'latest';
|
||||
|
||||
onMount(async () => {
|
||||
if ($session.userId) {
|
||||
const overrideVersion = $features.latestVersion;
|
||||
@@ -129,9 +136,16 @@
|
||||
<title>Coolify</title>
|
||||
{#if !$session.whiteLabeled}
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
{:else if $session.whiteLabelDetails.icon}
|
||||
<link rel="icon" href={$session.whiteLabelDetails.icon} />
|
||||
{/if}
|
||||
</svelte:head>
|
||||
<SvelteToast options={{ intro: { y: -64 }, duration: 3000, pausable: true }} />
|
||||
{#if $navigating}
|
||||
<div out:fade={{ delay: 100 }}>
|
||||
<PageLoader />
|
||||
</div>
|
||||
{/if}
|
||||
{#if $session.userId}
|
||||
<nav class="nav-main">
|
||||
<div class="flex h-screen w-full flex-col items-center transition-all duration-100">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
const endpoint = `/applications/${params.id}.json`;
|
||||
const res = await fetch(endpoint);
|
||||
if (res.ok) {
|
||||
let { application, isRunning, isExited, appId, githubToken, gitlabToken } = await res.json();
|
||||
let { application, appId, githubToken, gitlabToken } = await res.json();
|
||||
if (!application || Object.entries(application).length === 0) {
|
||||
return {
|
||||
status: 302,
|
||||
@@ -45,13 +45,10 @@
|
||||
return {
|
||||
props: {
|
||||
application,
|
||||
isRunning,
|
||||
isExited,
|
||||
githubToken,
|
||||
gitlabToken
|
||||
},
|
||||
stuff: {
|
||||
isRunning,
|
||||
application,
|
||||
appId
|
||||
}
|
||||
@@ -67,8 +64,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
export let application;
|
||||
export let isRunning;
|
||||
export let isExited;
|
||||
export let githubToken;
|
||||
export let gitlabToken;
|
||||
import { page, session } from '$app/stores';
|
||||
@@ -77,7 +72,7 @@
|
||||
import Loading from '$lib/components/Loading.svelte';
|
||||
import { del, get, post } from '$lib/api';
|
||||
import { goto } from '$app/navigation';
|
||||
import { gitTokens } from '$lib/store';
|
||||
import { gitTokens, status } from '$lib/store';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { disabledButton } from '$lib/store';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
@@ -135,17 +130,31 @@
|
||||
}
|
||||
}
|
||||
async function getStatus() {
|
||||
statusInterval = setInterval(async () => {
|
||||
const data = await get(`/applications/${id}.json`);
|
||||
isRunning = data.isRunning;
|
||||
isExited = data.isExited;
|
||||
}, 1000);
|
||||
if ($status.application.loading) return;
|
||||
$status.application.loading = true;
|
||||
const data = await get(`/applications/${id}/status.json`);
|
||||
$status.application.isRunning = data.isRunning;
|
||||
$status.application.isExited = data.isExited;
|
||||
$status.application.loading = false;
|
||||
$status.application.initialLoading = false;
|
||||
}
|
||||
onDestroy(() => {
|
||||
$status.application.initialLoading = true;
|
||||
clearInterval(statusInterval);
|
||||
});
|
||||
onMount(async () => {
|
||||
await getStatus();
|
||||
if (!application.gitSourceId || !application.destinationDockerId || !application.fqdn) {
|
||||
$status.application.initialLoading = false;
|
||||
$status.application.isRunning = false;
|
||||
$status.application.isExited = false;
|
||||
$status.application.loading = false;
|
||||
return;
|
||||
} else {
|
||||
await getStatus();
|
||||
statusInterval = setInterval(async () => {
|
||||
await getStatus();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -153,16 +162,16 @@
|
||||
{#if loading}
|
||||
<Loading fullscreen cover />
|
||||
{:else}
|
||||
{#if isExited}
|
||||
{#if $status.application.isExited}
|
||||
<a
|
||||
href={!$disabledButton ? `/applications/${id}/logs` : null}
|
||||
class=" icons bg-transparent tooltip-bottom text-sm flex items-center text-red-500 tooltip-red-500"
|
||||
class=" icons tooltip-bottom tooltip-red-500 flex items-center bg-transparent text-sm text-red-500"
|
||||
data-tooltip="Application exited with an error!"
|
||||
sveltekit:prefetch
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentcolor"
|
||||
@@ -179,20 +188,43 @@
|
||||
</svg>
|
||||
</a>
|
||||
{/if}
|
||||
{#if isRunning}
|
||||
{#if $status.application.initialLoading}
|
||||
<button
|
||||
class="icons tooltip-bottom flex animate-spin items-center space-x-2 bg-transparent text-sm duration-500 ease-in-out"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M9 4.55a8 8 0 0 1 6 14.9m0 -4.45v5h5" />
|
||||
<line x1="5.63" y1="7.16" x2="5.63" y2="7.17" />
|
||||
<line x1="4.06" y1="11" x2="4.06" y2="11.01" />
|
||||
<line x1="4.63" y1="15.1" x2="4.63" y2="15.11" />
|
||||
<line x1="7.16" y1="18.37" x2="7.16" y2="18.38" />
|
||||
<line x1="11" y1="19.94" x2="11" y2="19.95" />
|
||||
</svg>
|
||||
</button>
|
||||
{:else if $status.application.isRunning}
|
||||
<button
|
||||
on:click={stopApplication}
|
||||
title="Stop application"
|
||||
type="submit"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-red-500"
|
||||
class="icons tooltip-bottom flex items-center space-x-2 bg-transparent text-sm text-red-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? $t('application.stop_application')
|
||||
: $t('application.permission_denied_stop_application')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
@@ -210,14 +242,14 @@
|
||||
title="Rebuild application"
|
||||
type="submit"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 hover:text-green-500"
|
||||
class="icons tooltip-bottom flex items-center space-x-2 bg-transparent text-sm hover:text-green-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Rebuild application'
|
||||
: 'You do not have permission to rebuild application.'}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
@@ -239,14 +271,14 @@
|
||||
title="Build and start application"
|
||||
type="submit"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm flex items-center space-x-2 text-green-500"
|
||||
class="icons tooltip-bottom flex items-center space-x-2 bg-transparent text-sm text-green-500"
|
||||
data-tooltip={$session.isAdmin
|
||||
? 'Build and start application'
|
||||
: 'You do not have permission to Build and start application.'}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
@@ -261,18 +293,18 @@
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
<div class="border border-coolgray-500 h-8" />
|
||||
<div class="h-8 border border-coolgray-500" />
|
||||
<a
|
||||
href={!$disabledButton ? `/applications/${id}` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-yellow-500 rounded"
|
||||
class="rounded hover:text-yellow-500"
|
||||
class:text-yellow-500={$page.url.pathname === `/applications/${id}`}
|
||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}`}
|
||||
>
|
||||
<button
|
||||
title="Configurations"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
class="icons tooltip-bottom bg-transparent text-sm"
|
||||
data-tooltip="Configurations"
|
||||
>
|
||||
<svg
|
||||
@@ -301,19 +333,19 @@
|
||||
<a
|
||||
href={!$disabledButton ? `/applications/${id}/secrets` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-pink-500 rounded"
|
||||
class="rounded hover:text-pink-500"
|
||||
class:text-pink-500={$page.url.pathname === `/applications/${id}/secrets`}
|
||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/secrets`}
|
||||
>
|
||||
<button
|
||||
title="Secret"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
class="icons tooltip-bottom bg-transparent text-sm"
|
||||
data-tooltip="Secret"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
@@ -333,19 +365,19 @@
|
||||
<a
|
||||
href={!$disabledButton ? `/applications/${id}/storage` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-pink-500 rounded"
|
||||
class="rounded hover:text-pink-500"
|
||||
class:text-pink-500={$page.url.pathname === `/applications/${id}/storage`}
|
||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/storage`}
|
||||
>
|
||||
<button
|
||||
title="Persistent Storage"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
class="icons tooltip-bottom bg-transparent text-sm"
|
||||
data-tooltip="Persistent Storage"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
@@ -363,19 +395,19 @@
|
||||
<a
|
||||
href={!$disabledButton ? `/applications/${id}/previews` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-orange-500 rounded"
|
||||
class="rounded hover:text-orange-500"
|
||||
class:text-orange-500={$page.url.pathname === `/applications/${id}/previews`}
|
||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/previews`}
|
||||
>
|
||||
<button
|
||||
title="Previews"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
class="icons tooltip-bottom bg-transparent text-sm"
|
||||
data-tooltip="Previews"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
@@ -392,18 +424,18 @@
|
||||
</svg></button
|
||||
></a
|
||||
>
|
||||
<div class="border border-coolgray-500 h-8" />
|
||||
<div class="h-8 border border-coolgray-500" />
|
||||
<a
|
||||
href={!$disabledButton && isRunning ? `/applications/${id}/logs` : null}
|
||||
href={!$disabledButton && $status.application.isRunning ? `/applications/${id}/logs` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-sky-500 rounded"
|
||||
class="rounded hover:text-sky-500"
|
||||
class:text-sky-500={$page.url.pathname === `/applications/${id}/logs`}
|
||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs`}
|
||||
>
|
||||
<button
|
||||
title={$t('application.logs')}
|
||||
disabled={$disabledButton || !isRunning}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
disabled={$disabledButton || !$status.application.isRunning}
|
||||
class="icons tooltip-bottom bg-transparent text-sm"
|
||||
data-tooltip={$t('application.logs')}
|
||||
>
|
||||
<svg
|
||||
@@ -428,14 +460,14 @@
|
||||
<a
|
||||
href={!$disabledButton ? `/applications/${id}/logs/build` : null}
|
||||
sveltekit:prefetch
|
||||
class="hover:text-red-500 rounded"
|
||||
class="rounded hover:text-red-500"
|
||||
class:text-red-500={$page.url.pathname === `/applications/${id}/logs/build`}
|
||||
class:bg-coolgray-500={$page.url.pathname === `/applications/${id}/logs/build`}
|
||||
>
|
||||
<button
|
||||
title="Build Logs"
|
||||
disabled={$disabledButton}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
class="icons tooltip-bottom bg-transparent text-sm"
|
||||
data-tooltip="Build Logs"
|
||||
>
|
||||
<svg
|
||||
@@ -460,7 +492,7 @@
|
||||
</svg>
|
||||
</button></a
|
||||
>
|
||||
<div class="border border-coolgray-500 h-8" />
|
||||
<div class="h-8 border border-coolgray-500" />
|
||||
|
||||
<button
|
||||
on:click={() => deleteApplication(application.name)}
|
||||
@@ -468,7 +500,7 @@
|
||||
type="submit"
|
||||
disabled={!$session.isAdmin}
|
||||
class:hover:text-red-500={$session.isAdmin}
|
||||
class="icons bg-transparent tooltip-bottom text-sm"
|
||||
class="icons tooltip-bottom bg-transparent text-sm"
|
||||
data-tooltip={$session.isAdmin
|
||||
? $t('application.delete_application')
|
||||
: $t('application.permission_denied_delete_application')}
|
||||
|
||||
@@ -52,7 +52,7 @@ export const post: RequestHandler = async (event) => {
|
||||
exposePort = Number(exposePort);
|
||||
|
||||
if (exposePort < 1024 || exposePort > 65535) {
|
||||
throw { message: `Expose Port needs to be between 1024 and 65535.` };
|
||||
throw { message: `Exposed Port needs to be between 1024 and 65535.` };
|
||||
}
|
||||
|
||||
const publicPort = await getPort({ port: exposePort });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
export let application;
|
||||
export let appId;
|
||||
import Select from 'svelte-select';
|
||||
import { page, session } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { errorNotification } from '$lib/form';
|
||||
@@ -33,6 +34,10 @@
|
||||
let showSave = false;
|
||||
let autodeploy = application.settings.autodeploy || true;
|
||||
|
||||
let search = {
|
||||
project: '',
|
||||
branch: ''
|
||||
};
|
||||
let selected = {
|
||||
group: undefined,
|
||||
project: undefined,
|
||||
@@ -84,16 +89,49 @@
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function selectGroup(event) {
|
||||
selected.group = event.detail;
|
||||
selected.project = null;
|
||||
selected.branch = null;
|
||||
showSave = false;
|
||||
loadProjects();
|
||||
}
|
||||
|
||||
async function searchProjects(searchText) {
|
||||
if (!selected.group) {
|
||||
return;
|
||||
}
|
||||
|
||||
search.project = searchText;
|
||||
await loadProjects();
|
||||
return projects;
|
||||
}
|
||||
|
||||
function selectProject(event) {
|
||||
selected.project = event.detail;
|
||||
selected.branch = null;
|
||||
showSave = false;
|
||||
loadBranches();
|
||||
}
|
||||
|
||||
async function loadProjects() {
|
||||
const params = new URLSearchParams({
|
||||
page: 1,
|
||||
per_page: 25,
|
||||
archived: false
|
||||
});
|
||||
|
||||
if (search.project) {
|
||||
params.append('search', search.project);
|
||||
}
|
||||
|
||||
loading.projects = true;
|
||||
if (username === selected.group.name) {
|
||||
try {
|
||||
projects = await get(
|
||||
`${apiUrl}/v4/users/${selected.group.name}/projects?min_access_level=40&page=1&per_page=25&archived=false`,
|
||||
{
|
||||
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||
}
|
||||
);
|
||||
params.append('min_access_level', 40);
|
||||
projects = await get(`${apiUrl}/v4/users/${selected.group.name}/projects?${params}`, {
|
||||
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||
});
|
||||
} catch (error) {
|
||||
errorNotification(error);
|
||||
throw new Error(error);
|
||||
@@ -102,12 +140,9 @@
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
projects = await get(
|
||||
`${apiUrl}/v4/groups/${selected.group.id}/projects?page=1&per_page=25&archived=false`,
|
||||
{
|
||||
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||
}
|
||||
);
|
||||
projects = await get(`${apiUrl}/v4/groups/${selected.group.id}/projects?${params}`, {
|
||||
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||
});
|
||||
} catch (error) {
|
||||
errorNotification(error);
|
||||
throw new Error(error);
|
||||
@@ -117,11 +152,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function searchBranches(searchText) {
|
||||
if (!selected.project) {
|
||||
return;
|
||||
}
|
||||
|
||||
search.branch = searchText;
|
||||
await loadBranches();
|
||||
return branches;
|
||||
}
|
||||
|
||||
function selectBranch(event) {
|
||||
selected.branch = event.detail;
|
||||
isBranchAlreadyUsed();
|
||||
}
|
||||
|
||||
async function loadBranches() {
|
||||
const params = new URLSearchParams({
|
||||
page: 1,
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
if (search.branch) {
|
||||
params.append('search', search.branch);
|
||||
}
|
||||
|
||||
loading.branches = true;
|
||||
try {
|
||||
branches = await get(
|
||||
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?per_page=100&page=1`,
|
||||
`${apiUrl}/v4/projects/${selected.project.id}/repository/branches?${params}`,
|
||||
{
|
||||
Authorization: `Bearer ${$gitTokens.gitlabToken}`
|
||||
}
|
||||
@@ -267,70 +326,79 @@
|
||||
|
||||
<form on:submit={handleSubmit}>
|
||||
<div class="flex flex-col space-y-2 px-4 xl:flex-row xl:space-y-0 xl:space-x-2 ">
|
||||
{#if loading.base}
|
||||
<select name="group" disabled class="w-96">
|
||||
<option selected value="">{$t('application.configuration.loading_groups')}</option>
|
||||
</select>
|
||||
{:else}
|
||||
<select name="group" class="w-96" bind:value={selected.group} on:change={loadProjects}>
|
||||
<option value="" disabled selected>{$t('application.configuration.select_a_group')}</option>
|
||||
{#each groups as group}
|
||||
<option value={group}>{group.full_name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
{#if loading.projects}
|
||||
<select name="project" disabled class="w-96">
|
||||
<option selected value="">{$t('application.configuration.loading_projects')}</option>
|
||||
</select>
|
||||
{:else if !loading.projects && projects.length > 0}
|
||||
<select
|
||||
name="project"
|
||||
class="w-96"
|
||||
bind:value={selected.project}
|
||||
on:change={loadBranches}
|
||||
disabled={!selected.group}
|
||||
>
|
||||
<option value="" disabled selected
|
||||
>{$t('application.configuration.select_a_project')}</option
|
||||
>
|
||||
{#each projects as project}
|
||||
<option value={project}>{project.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<select name="project" disabled class="w-96">
|
||||
<option disabled selected value=""
|
||||
>{$t('application.configuration.no_projects_found')}</option
|
||||
>
|
||||
</select>
|
||||
{/if}
|
||||
|
||||
{#if loading.branches}
|
||||
<select name="branch" disabled class="w-96">
|
||||
<option selected value="">{$t('application.configuration.loading_branches')}</option>
|
||||
</select>
|
||||
{:else if !loading.branches && branches.length > 0}
|
||||
<select
|
||||
name="branch"
|
||||
class="w-96"
|
||||
bind:value={selected.branch}
|
||||
on:change={isBranchAlreadyUsed}
|
||||
disabled={!selected.project}
|
||||
>
|
||||
<option value="" disabled selected>{$t('application.configuration.select_a_branch')}</option
|
||||
>
|
||||
{#each branches as branch}
|
||||
<option value={branch}>{branch.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<select name="project" disabled class="w-96">
|
||||
<option disabled selected value=""
|
||||
>{$t('application.configuration.no_branches_found')}</option
|
||||
>
|
||||
</select>
|
||||
{/if}
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
placeholder={loading.base
|
||||
? $t('application.configuration.loading_groups')
|
||||
: $t('application.configuration.select_a_group')}
|
||||
id="group"
|
||||
showIndicator={!loading.base}
|
||||
isWaiting={loading.base}
|
||||
on:select={selectGroup}
|
||||
on:clear={() => {
|
||||
showSave = false;
|
||||
projects = [];
|
||||
branches = [];
|
||||
selected.group = null;
|
||||
selected.project = null;
|
||||
selected.branch = null;
|
||||
}}
|
||||
value={selected.group}
|
||||
isDisabled={loading.base}
|
||||
isClearable={false}
|
||||
items={groups}
|
||||
labelIdentifier="full_name"
|
||||
optionIdentifier="id"
|
||||
/>
|
||||
</div>
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
placeholder={loading.projects
|
||||
? $t('application.configuration.loading_projects')
|
||||
: $t('application.configuration.select_a_project')}
|
||||
noOptionsMessage={$t('application.configuration.no_projects_found')}
|
||||
id="project"
|
||||
showIndicator={!loading.projects}
|
||||
isWaiting={loading.projects}
|
||||
isDisabled={loading.projects || !selected.group}
|
||||
on:select={selectProject}
|
||||
on:clear={() => {
|
||||
showSave = false;
|
||||
branches = [];
|
||||
selected.project = null;
|
||||
selected.branch = null;
|
||||
}}
|
||||
value={selected.project}
|
||||
isClearable={false}
|
||||
items={projects}
|
||||
loadOptions={searchProjects}
|
||||
labelIdentifier="name"
|
||||
optionIdentifier="id"
|
||||
/>
|
||||
</div>
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
placeholder={loading.branches
|
||||
? $t('application.configuration.loading_branches')
|
||||
: $t('application.configuration.select_a_branch')}
|
||||
noOptionsMessage={$t('application.configuration.no_branches_found')}
|
||||
id="branch"
|
||||
showIndicator={!loading.branches}
|
||||
isWaiting={loading.branches}
|
||||
isDisabled={loading.branches || !selected.project}
|
||||
on:select={selectBranch}
|
||||
on:clear={() => {
|
||||
showSave = false;
|
||||
selected.branch = null;
|
||||
}}
|
||||
value={selected.branch}
|
||||
isClearable={false}
|
||||
items={branches}
|
||||
loadOptions={searchBranches}
|
||||
labelIdentifier="name"
|
||||
optionIdentifier="web_url"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col items-center justify-center space-y-4 pt-5">
|
||||
<button
|
||||
|
||||
@@ -19,10 +19,13 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { t } from '$lib/translations';
|
||||
import { gitTokens } from '$lib/store';
|
||||
|
||||
export let application;
|
||||
export let appId;
|
||||
|
||||
$gitTokens.githubToken = null;
|
||||
|
||||
import GithubRepositories from './_GithubRepositories.svelte';
|
||||
import GitlabRepositories from './_GitlabRepositories.svelte';
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { asyncExecShell, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { checkContainer, isContainerExited } from '$lib/haproxy';
|
||||
import { checkContainer, getContainerUsage, isContainerExited } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { setDefaultConfiguration } from '$lib/buildPacks/common';
|
||||
|
||||
@@ -12,21 +12,14 @@ export const get: RequestHandler = async (event) => {
|
||||
const { id } = event.params;
|
||||
|
||||
const appId = process.env['COOLIFY_APP_ID'];
|
||||
let isRunning = false;
|
||||
let isExited = false;
|
||||
let githubToken = event.locals.cookies?.githubToken || null;
|
||||
let gitlabToken = event.locals.cookies?.gitlabToken || null;
|
||||
try {
|
||||
const application = await db.getApplication({ id, teamId });
|
||||
if (application.destinationDockerId) {
|
||||
isRunning = await checkContainer(application.destinationDocker.engine, id);
|
||||
isExited = await isContainerExited(application.destinationDocker.engine, id);
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
isRunning,
|
||||
isExited,
|
||||
application,
|
||||
appId,
|
||||
githubToken,
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
if (stuff?.application?.id) {
|
||||
return {
|
||||
props: {
|
||||
application: stuff.application,
|
||||
isRunning: stuff.isRunning
|
||||
application: stuff.application
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -34,10 +33,9 @@
|
||||
baseImages: Array<{ value: string; label: string }>;
|
||||
baseBuildImages: Array<{ value: string; label: string }>;
|
||||
};
|
||||
export let isRunning;
|
||||
import { page, session } from '$app/stores';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { onMount } from 'svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import Select from 'svelte-select';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import Setting from '$lib/components/Setting.svelte';
|
||||
@@ -47,11 +45,20 @@
|
||||
import { get, post } from '$lib/api';
|
||||
import cuid from 'cuid';
|
||||
import { browser } from '$app/env';
|
||||
import { disabledButton } from '$lib/store';
|
||||
import { disabledButton, status } from '$lib/store';
|
||||
import { t } from '$lib/translations';
|
||||
const { id } = $page.params;
|
||||
let domainEl: HTMLInputElement;
|
||||
let loading = false;
|
||||
|
||||
let usageLoading = false;
|
||||
let usage = {
|
||||
MemUsage: 0,
|
||||
CPUPerc: 0,
|
||||
NetIO: 0
|
||||
};
|
||||
let usageInterval;
|
||||
|
||||
let forceSave = false;
|
||||
let debug = application.settings.debug;
|
||||
let previews = application.settings.previews;
|
||||
@@ -60,6 +67,9 @@
|
||||
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||
let isNonWWWDomainOK = false;
|
||||
let isWWWDomainOK = false;
|
||||
|
||||
$: isDisabled = !$session.isAdmin || $status.application.isRunning;
|
||||
|
||||
let wsgis = [
|
||||
{
|
||||
value: 'None',
|
||||
@@ -75,15 +85,29 @@
|
||||
}
|
||||
];
|
||||
function containerClass() {
|
||||
if (!$session.isAdmin || isRunning) {
|
||||
return 'text-white border border-dashed border-coolgray-300 bg-transparent font-thin px-0';
|
||||
return 'text-white border border-dashed border-coolgray-300 bg-transparent font-thin px-0';
|
||||
}
|
||||
|
||||
async function getUsage() {
|
||||
if (usageLoading) return;
|
||||
usageLoading = true;
|
||||
const data = await get(`/applications/${id}/usage.json`);
|
||||
usage = data.usage;
|
||||
usageLoading = false;
|
||||
}
|
||||
onDestroy(() => {
|
||||
clearInterval(usageInterval);
|
||||
});
|
||||
onMount(async () => {
|
||||
if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
|
||||
application.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
await handleSubmit();
|
||||
}
|
||||
}
|
||||
if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
|
||||
application.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
}
|
||||
onMount(() => {
|
||||
domainEl.focus();
|
||||
await getUsage();
|
||||
usageInterval = setInterval(async () => {
|
||||
await getUsage();
|
||||
}, 1000);
|
||||
});
|
||||
async function changeSettings(name) {
|
||||
if (name === 'debug') {
|
||||
@@ -125,6 +149,7 @@
|
||||
}
|
||||
}
|
||||
async function handleSubmit() {
|
||||
if (loading) return;
|
||||
loading = true;
|
||||
try {
|
||||
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
|
||||
@@ -254,6 +279,34 @@
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto max-w-4xl px-6 py-4">
|
||||
<div class="text-2xl font-bold">Application Usage</div>
|
||||
<div class="mx-auto">
|
||||
<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
|
||||
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
|
||||
<dt class=" text-sm font-medium text-white">Used Memory / Memory Limit</dt>
|
||||
<dd class="mt-1 text-xl font-semibold text-white">
|
||||
{usage?.MemUsage}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
|
||||
<dt class="truncate text-sm font-medium text-white">Used CPU</dt>
|
||||
<dd class="mt-1 text-xl font-semibold text-white ">
|
||||
{usage?.CPUPerc}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
|
||||
<dt class="truncate text-sm font-medium text-white">Network IO</dt>
|
||||
<dd class="mt-1 text-xl font-semibold text-white ">
|
||||
{usage?.NetIO}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx-auto max-w-4xl px-6">
|
||||
<!-- svelte-ignore missing-declaration -->
|
||||
<form on:submit|preventDefault={handleSubmit} class="py-4">
|
||||
@@ -347,7 +400,7 @@
|
||||
value={application.destinationDocker.name}
|
||||
id="destination"
|
||||
disabled
|
||||
class="bg-transparent "
|
||||
class="bg-transparent"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -358,10 +411,10 @@
|
||||
>
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
isDisabled={!$session.isAdmin || isRunning}
|
||||
containerClasses={containerClass()}
|
||||
{isDisabled}
|
||||
containerClasses={isDisabled && containerClass()}
|
||||
id="baseImages"
|
||||
showIndicator={!isRunning}
|
||||
showIndicator={!$status.application.isRunning}
|
||||
items={application.baseImages}
|
||||
on:select={selectBaseImage}
|
||||
value={application.baseImage}
|
||||
@@ -378,10 +431,10 @@
|
||||
>
|
||||
<div class="custom-select-wrapper">
|
||||
<Select
|
||||
isDisabled={!$session.isAdmin || isRunning}
|
||||
containerClasses={containerClass()}
|
||||
{isDisabled}
|
||||
containerClasses={isDisabled && containerClass()}
|
||||
id="baseBuildImages"
|
||||
showIndicator={!isRunning}
|
||||
showIndicator={!$status.application.isRunning}
|
||||
items={application.baseBuildImages}
|
||||
on:select={selectBaseBuildImage}
|
||||
value={application.baseBuildImage}
|
||||
@@ -414,8 +467,8 @@
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
readonly={!$session.isAdmin || isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
readonly={isDisabled}
|
||||
disabled={isDisabled}
|
||||
bind:this={domainEl}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
@@ -462,12 +515,12 @@
|
||||
<div class="grid grid-cols-2 items-center pb-8">
|
||||
<Setting
|
||||
dataTooltip={$t('forms.must_be_stopped_to_modify')}
|
||||
disabled={isRunning}
|
||||
disabled={$status.application.isRunning}
|
||||
isCenter={false}
|
||||
bind:setting={dualCerts}
|
||||
title={$t('application.ssl_www_and_non_www')}
|
||||
description={$t('application.ssl_explainer')}
|
||||
on:click={() => !isRunning && changeSettings('dualCerts')}
|
||||
on:click={() => !$status.application.isRunning && changeSettings('dualCerts')}
|
||||
/>
|
||||
</div>
|
||||
{#if application.buildPack === 'python'}
|
||||
@@ -531,8 +584,8 @@
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
|
||||
<input
|
||||
readonly={!$session.isAdmin && !isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
readonly={!$session.isAdmin && !$status.application.isRunning}
|
||||
disabled={isDisabled}
|
||||
name="exposePort"
|
||||
id="exposePort"
|
||||
bind:value={application.exposePort}
|
||||
|
||||
@@ -17,7 +17,7 @@ export const get: RequestHandler = async (event) => {
|
||||
const destinationDocker = await db.getDestinationByApplicationId({ id, teamId });
|
||||
const docker = dockerInstance({ destinationDocker });
|
||||
const listContainers = await docker.engine.listContainers({
|
||||
filters: { network: [destinationDocker.network] }
|
||||
filters: { network: [destinationDocker.network], name: [id] }
|
||||
});
|
||||
const containers = listContainers.filter((container) => {
|
||||
return (
|
||||
@@ -30,11 +30,7 @@ export const get: RequestHandler = async (event) => {
|
||||
JSON.parse(Buffer.from(container.Labels['coolify.configuration'], 'base64').toString())
|
||||
)
|
||||
.filter((container) => {
|
||||
return (
|
||||
container.type !== 'manual' &&
|
||||
container.type !== 'webhook_commit' &&
|
||||
container.applicationId === id
|
||||
);
|
||||
return container.pullmergeRequestId && container.applicationId === id;
|
||||
});
|
||||
return {
|
||||
body: {
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { t } from '$lib/translations';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
const { id } = $page.params;
|
||||
async function refreshSecrets() {
|
||||
@@ -39,11 +40,18 @@
|
||||
}
|
||||
async function redeploy(container) {
|
||||
try {
|
||||
await post(`/applications/${id}/deploy.json`, {
|
||||
const { buildId } = await post(`/applications/${id}/deploy.json`, {
|
||||
pullmergeRequestId: container.pullmergeRequestId,
|
||||
branch: container.branch
|
||||
});
|
||||
toast.push('Application redeployed queued.');
|
||||
if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
|
||||
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
|
||||
} else {
|
||||
return await goto(`/applications/${id}/logs/build?buildId=${buildId}`, {
|
||||
replaceState: true
|
||||
});
|
||||
}
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
|
||||
36
src/routes/applications/[id]/status.json.ts
Normal file
36
src/routes/applications/[id]/status.json.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { asyncExecShell, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { checkContainer, getContainerUsage, isContainerExited } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { setDefaultConfiguration } from '$lib/buildPacks/common';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let isRunning = false;
|
||||
let isExited = false;
|
||||
try {
|
||||
const application = await db.getApplication({ id, teamId });
|
||||
if (application.destinationDockerId) {
|
||||
[isRunning, isExited] = await Promise.all([
|
||||
checkContainer(application.destinationDocker.engine, id),
|
||||
isContainerExited(application.destinationDocker.engine, id)
|
||||
]);
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
isRunning,
|
||||
isExited
|
||||
},
|
||||
headers: {}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
30
src/routes/applications/[id]/usage.json.ts
Normal file
30
src/routes/applications/[id]/usage.json.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { getContainerUsage } from '$lib/haproxy';
|
||||
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 };
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let usage = {};
|
||||
try {
|
||||
const application = await db.getApplication({ id, teamId });
|
||||
if (application.destinationDockerId) {
|
||||
[usage] = await Promise.all([getContainerUsage(application.destinationDocker.engine, id)]);
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
usage
|
||||
},
|
||||
headers: {}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
@@ -49,6 +49,7 @@ export const get: RequestHandler = async (event) => {
|
||||
where: { userId },
|
||||
include: { team: { include: { _count: { select: { users: true } } } } }
|
||||
});
|
||||
const settings = await db.prisma.setting.findFirst();
|
||||
return {
|
||||
body: {
|
||||
teams,
|
||||
@@ -57,7 +58,8 @@ export const get: RequestHandler = async (event) => {
|
||||
destinationsCount,
|
||||
teamsCount,
|
||||
databasesCount,
|
||||
servicesCount
|
||||
servicesCount,
|
||||
settings
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
@@ -12,7 +12,7 @@ export const del: RequestHandler = async (event) => {
|
||||
const database = await db.getDatabase({ id, teamId });
|
||||
if (database.destinationDockerId) {
|
||||
const everStarted = await stopDatabase(database);
|
||||
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort);
|
||||
if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
|
||||
}
|
||||
await db.removeDatabase({ id });
|
||||
return { status: 200 };
|
||||
|
||||
@@ -33,10 +33,40 @@
|
||||
|
||||
<script lang="ts">
|
||||
import DatabaseLinks from '$lib/components/DatabaseLinks.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { get } from '$lib/api';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
export let database;
|
||||
export let settings;
|
||||
export let privatePort;
|
||||
export let isRunning;
|
||||
|
||||
const { id } = $page.params;
|
||||
let usageLoading = false;
|
||||
let usage = {
|
||||
MemUsage: 0,
|
||||
CPUPerc: 0,
|
||||
NetIO: 0
|
||||
};
|
||||
let usageInterval;
|
||||
|
||||
async function getUsage() {
|
||||
if (usageLoading) return;
|
||||
usageLoading = true;
|
||||
const data = await get(`/databases/${id}/usage.json`);
|
||||
usage = data.usage;
|
||||
usageLoading = false;
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
clearInterval(usageInterval);
|
||||
});
|
||||
onMount(async () => {
|
||||
await getUsage();
|
||||
usageInterval = setInterval(async () => {
|
||||
await getUsage();
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex items-center space-x-2 p-6 text-2xl font-bold">
|
||||
@@ -49,4 +79,31 @@
|
||||
<DatabaseLinks {database} />
|
||||
</div>
|
||||
|
||||
<div class="mx-auto max-w-4xl px-6 py-4">
|
||||
<div class="text-2xl font-bold">Database Usage</div>
|
||||
<div class="mx-auto">
|
||||
<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
|
||||
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
|
||||
<dt class=" text-sm font-medium text-white">Used Memory / Memory Limit</dt>
|
||||
<dd class="mt-1 text-xl font-semibold text-white">
|
||||
{usage?.MemUsage}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
|
||||
<dt class="truncate text-sm font-medium text-white">Used CPU</dt>
|
||||
<dd class="mt-1 text-xl font-semibold text-white ">
|
||||
{usage?.CPUPerc}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
|
||||
<dt class="truncate text-sm font-medium text-white">Network IO</dt>
|
||||
<dd class="mt-1 text-xl font-semibold text-white ">
|
||||
{usage?.NetIO}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<Databases bind:database {privatePort} {settings} {isRunning} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { generateDatabaseConfiguration, ErrorHandler, getFreePort } from '$lib/database';
|
||||
import { startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy';
|
||||
import { startTcpProxy, startTraefikTCPProxy, stopTcpHttpProxy } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -13,6 +13,7 @@ export const post: RequestHandler = async (event) => {
|
||||
const publicPort = await getFreePort();
|
||||
|
||||
try {
|
||||
const settings = await db.listSettings();
|
||||
await db.setDatabase({ id, isPublic, appendOnly });
|
||||
const database = await db.getDatabase({ id, teamId });
|
||||
const { destinationDockerId, destinationDocker, publicPort: oldPublicPort } = database;
|
||||
@@ -21,10 +22,14 @@ export const post: RequestHandler = async (event) => {
|
||||
if (destinationDockerId) {
|
||||
if (isPublic) {
|
||||
await db.prisma.database.update({ where: { id }, data: { publicPort } });
|
||||
await startTcpProxy(destinationDocker, id, publicPort, privatePort);
|
||||
if (settings.isTraefikUsed) {
|
||||
await startTraefikTCPProxy(destinationDocker, id, publicPort, privatePort);
|
||||
} else {
|
||||
await startTcpProxy(destinationDocker, id, publicPort, privatePort);
|
||||
}
|
||||
} else {
|
||||
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
||||
await stopTcpHttpProxy(destinationDocker, oldPublicPort);
|
||||
await stopTcpHttpProxy(id, destinationDocker, oldPublicPort);
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -13,7 +13,7 @@ export const post: RequestHandler = async (event) => {
|
||||
try {
|
||||
const database = await db.getDatabase({ id, teamId });
|
||||
const everStarted = await stopDatabase(database);
|
||||
if (everStarted) await stopTcpHttpProxy(database.destinationDocker, database.publicPort);
|
||||
if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
|
||||
await db.setDatabase({ id, isPublic: false });
|
||||
await db.prisma.database.update({ where: { id }, data: { publicPort: null } });
|
||||
|
||||
@@ -21,6 +21,7 @@ export const post: RequestHandler = async (event) => {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
30
src/routes/databases/[id]/usage.json.ts
Normal file
30
src/routes/databases/[id]/usage.json.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { getContainerUsage } from '$lib/haproxy';
|
||||
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 };
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let usage = {};
|
||||
try {
|
||||
const database = await db.getDatabase({ id, teamId });
|
||||
if (database.destinationDockerId) {
|
||||
[usage] = await Promise.all([getContainerUsage(database.destinationDocker.engine, id)]);
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
usage
|
||||
},
|
||||
headers: {}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
@@ -26,8 +26,12 @@ export const get: RequestHandler = async (event) => {
|
||||
// // await saveSshKey(destination);
|
||||
// payload.state = await checkContainer(engine, 'coolify-haproxy');
|
||||
} else {
|
||||
let containerName = 'coolify-proxy';
|
||||
if (!settings.isTraefikUsed) {
|
||||
containerName = 'coolify-haproxy';
|
||||
}
|
||||
payload.state =
|
||||
destination?.engine && (await checkContainer(destination.engine, 'coolify-haproxy'));
|
||||
destination?.engine && (await checkContainer(destination.engine, containerName));
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import * as db from '$lib/database';
|
||||
import { startCoolifyProxy, stopCoolifyProxy } from '$lib/haproxy';
|
||||
import {
|
||||
startCoolifyProxy,
|
||||
startTraefikProxy,
|
||||
stopCoolifyProxy,
|
||||
stopTraefikProxy
|
||||
} from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -11,9 +16,16 @@ export const post: RequestHandler = async (event) => {
|
||||
const { engine } = await event.request.json();
|
||||
|
||||
try {
|
||||
await stopCoolifyProxy(engine);
|
||||
await startCoolifyProxy(engine);
|
||||
const settings = await db.prisma.setting.findFirst({});
|
||||
if (settings?.isTraefikUsed) {
|
||||
await stopTraefikProxy(engine);
|
||||
await startTraefikProxy(engine);
|
||||
} else {
|
||||
await stopCoolifyProxy(engine);
|
||||
await startCoolifyProxy(engine);
|
||||
}
|
||||
await db.setDestinationSettings({ engine, isCoolifyProxyUsed: true });
|
||||
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { startCoolifyProxy, stopCoolifyProxy } from '$lib/haproxy';
|
||||
import * as db from '$lib/database';
|
||||
import {
|
||||
startCoolifyProxy,
|
||||
startTraefikProxy,
|
||||
stopCoolifyProxy,
|
||||
stopTraefikProxy
|
||||
} from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -8,14 +14,24 @@ export const post: RequestHandler = async (event) => {
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { engine } = await event.request.json();
|
||||
const settings = await db.prisma.setting.findFirst({});
|
||||
|
||||
try {
|
||||
await startCoolifyProxy(engine);
|
||||
if (settings?.isTraefikUsed) {
|
||||
await startTraefikProxy(engine);
|
||||
} else {
|
||||
await startCoolifyProxy(engine);
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
await stopCoolifyProxy(engine);
|
||||
if (settings?.isTraefikUsed) {
|
||||
await stopTraefikProxy(engine);
|
||||
} else {
|
||||
await stopCoolifyProxy(engine);
|
||||
}
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { stopCoolifyProxy } from '$lib/haproxy';
|
||||
import * as db from '$lib/database';
|
||||
import { stopCoolifyProxy, stopTraefikProxy } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
@@ -9,7 +10,13 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
const { engine } = await event.request.json();
|
||||
try {
|
||||
await stopCoolifyProxy(engine);
|
||||
const settings = await db.prisma.setting.findFirst({});
|
||||
if (settings?.isTraefikUsed) {
|
||||
await stopTraefikProxy(engine);
|
||||
} else {
|
||||
await stopCoolifyProxy(engine);
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
import { t } from '$lib/translations';
|
||||
import { get } from '$lib/api';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import Loading from './applications/[id]/logs/_Loading.svelte';
|
||||
import Trend from './_Trend.svelte';
|
||||
import { session } from '$app/stores';
|
||||
|
||||
|
||||
@@ -30,14 +30,16 @@
|
||||
value={service.minio.rootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="publicPort">{$t('forms.api_port')}</label>
|
||||
<input
|
||||
name="publicPort"
|
||||
id="publicPort"
|
||||
value={service.minio.publicPort}
|
||||
disabled
|
||||
readonly
|
||||
placeholder={$t('forms.generated_automatically_after_start')}
|
||||
/>
|
||||
</div>
|
||||
{#if !service.minio.apiFqdn}
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="publicPort">{$t('forms.api_port')}</label>
|
||||
<input
|
||||
name="publicPort"
|
||||
id="publicPort"
|
||||
value={service.minio.publicPort}
|
||||
disabled
|
||||
readonly
|
||||
placeholder={$t('forms.generated_automatically_after_start')}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { browser } from '$app/env';
|
||||
|
||||
export let service;
|
||||
export let isRunning;
|
||||
export let readOnly;
|
||||
@@ -12,6 +14,8 @@
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { t } from '$lib/translations';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import cuid from 'cuid';
|
||||
import { onMount } from 'svelte';
|
||||
import Fider from './_Fider.svelte';
|
||||
import Ghost from './_Ghost.svelte';
|
||||
import Hasura from './_Hasura.svelte';
|
||||
@@ -29,9 +33,14 @@
|
||||
let dualCerts = service.dualCerts;
|
||||
|
||||
async function handleSubmit() {
|
||||
if (loading) return;
|
||||
loading = true;
|
||||
try {
|
||||
await post(`/services/${id}/check.json`, { fqdn: service.fqdn });
|
||||
await post(`/services/${id}/check.json`, {
|
||||
fqdn: service.fqdn,
|
||||
otherFqdns: service.minio?.apiFqdn ? [service.minio?.apiFqdn] : [],
|
||||
exposePort: service.exposePort
|
||||
});
|
||||
await post(`/services/${id}/${service.type}.json`, { ...service });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
@@ -62,6 +71,28 @@
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
onMount(async () => {
|
||||
if (browser && window.location.hostname === 'demo.coolify.io' && !service.fqdn) {
|
||||
service.fqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
if (service.type === 'wordpress') {
|
||||
service.wordpress.mysqlDatabase = 'db';
|
||||
}
|
||||
if (service.type === 'plausibleanalytics') {
|
||||
service.plausibleAnalytics.email = 'noreply@demo.com';
|
||||
service.plausibleAnalytics.username = 'admin';
|
||||
}
|
||||
if (service.type === 'minio') {
|
||||
service.minio.apiFqdn = `http://${cuid()}.demo.coolify.io`;
|
||||
}
|
||||
if (service.type === 'ghost') {
|
||||
service.ghost.mariadbDatabase = 'db';
|
||||
}
|
||||
if (service.type === 'fider') {
|
||||
service.fider.emailNoreply = 'noreply@demo.com';
|
||||
}
|
||||
await handleSubmit();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-4xl px-6 pb-12">
|
||||
@@ -86,6 +117,14 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-flow-row gap-2">
|
||||
{#if service.type === 'minio' && !service.minio.apiFqdn && isRunning}
|
||||
<div class="text-center">
|
||||
<span class="font-bold text-red-500">IMPORTANT!</span> There was a small modification with
|
||||
Minio in the latest version of Coolify. Now you can separate the Console URL from the API URL,
|
||||
so you could use both through SSL. But this proccess cannot be done automatically, so you have
|
||||
to stop your Minio instance, configure the new domain and start it back. Sorry for any inconvenience.
|
||||
</div>
|
||||
{/if}
|
||||
<div class="mt-2 grid grid-cols-2 items-center px-10">
|
||||
<label for="name" class="text-base font-bold text-stone-100">{$t('forms.name')}</label>
|
||||
<div>
|
||||
@@ -131,25 +170,62 @@
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 px-10">
|
||||
<div class="flex-col ">
|
||||
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
|
||||
>{$t('application.url_fqdn')}</label
|
||||
>
|
||||
<Explainer text={$t('application.https_explainer')} />
|
||||
</div>
|
||||
{#if service.type === 'minio'}
|
||||
<div class="grid grid-cols-2 px-10">
|
||||
<div class="flex-col ">
|
||||
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100">Console URL</label>
|
||||
</div>
|
||||
|
||||
<CopyPasswordField
|
||||
placeholder="eg: https://console.min.io"
|
||||
readonly={!$session.isAdmin && !isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
bind:value={service.fqdn}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 px-10">
|
||||
<div class="flex-col ">
|
||||
<label for="apiFqdn" class="pt-2 text-base font-bold text-stone-100">API URL</label>
|
||||
<Explainer text={$t('application.https_explainer')} />
|
||||
</div>
|
||||
|
||||
<CopyPasswordField
|
||||
placeholder="eg: https://min.io"
|
||||
readonly={!$session.isAdmin && !isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
name="apiFqdn"
|
||||
id="apiFqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
bind:value={service.minio.apiFqdn}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="grid grid-cols-2 px-10">
|
||||
<div class="flex-col ">
|
||||
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
|
||||
>{$t('application.url_fqdn')}</label
|
||||
>
|
||||
<Explainer text={$t('application.https_explainer')} />
|
||||
</div>
|
||||
|
||||
<CopyPasswordField
|
||||
placeholder="eg: https://analytics.coollabs.io"
|
||||
readonly={!$session.isAdmin && !isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
bind:value={service.fqdn}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<CopyPasswordField
|
||||
placeholder="eg: https://analytics.coollabs.io"
|
||||
readonly={!$session.isAdmin && !isRunning}
|
||||
disabled={!$session.isAdmin || isRunning}
|
||||
name="fqdn"
|
||||
id="fqdn"
|
||||
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
|
||||
bind:value={service.fqdn}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<Setting
|
||||
disabled={isRunning}
|
||||
@@ -160,7 +236,7 @@
|
||||
on:click={() => !isRunning && changeSettings('dualCerts')}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<div class="grid grid-cols-2 items-center px-10">
|
||||
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label>
|
||||
<input
|
||||
readonly={!$session.isAdmin && !isRunning}
|
||||
|
||||
21
src/routes/services/[id]/appwrite-wip/index.json.ts
Normal file
21
src/routes/services/[id]/appwrite-wip/index.json.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
519
src/routes/services/[id]/appwrite-wip/start.json.ts
Normal file
519
src/routes/services/[id]/appwrite-wip/start.json.ts
Normal file
@@ -0,0 +1,519 @@
|
||||
import { asyncExecShell, createDirectories, getEngine, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { promises as fs } from 'fs';
|
||||
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';
|
||||
import { getServiceMainPort } from '$lib/components/common';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { type, version, destinationDockerId, destinationDocker, serviceSecret, exposePort } =
|
||||
service;
|
||||
const network = destinationDockerId && destinationDocker.network;
|
||||
const host = getEngine(destinationDocker.engine);
|
||||
const port = getServiceMainPort('n8n');
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
|
||||
if (serviceSecret.length > 0) {
|
||||
serviceSecret.forEach((secret) => {
|
||||
variables[secret.name] = secret.value;
|
||||
});
|
||||
}
|
||||
|
||||
const variables = {
|
||||
_APP_ENV: 'production',
|
||||
_APP_VERSION: '',
|
||||
_APP_LOCALE: '',
|
||||
_APP_OPTIONS_ABUSE: '',
|
||||
_APP_OPTIONS_FORCE_HTTPS: '',
|
||||
_APP_OPENSSL_KEY_V1: '',
|
||||
_APP_DOMAIN: '',
|
||||
_APP_DOMAIN_TARGET: '',
|
||||
_APP_CONSOLE_WHITELIST_ROOT: '',
|
||||
_APP_CONSOLE_WHITELIST_EMAILS: '',
|
||||
_APP_CONSOLE_WHITELIST_IPS: '',
|
||||
_APP_SYSTEM_EMAIL_NAME: '',
|
||||
_APP_SYSTEM_EMAIL_ADDRESS: '',
|
||||
_APP_SYSTEM_RESPONSE_FORMAT: '',
|
||||
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS: '',
|
||||
_APP_USAGE_STATS: '',
|
||||
_APP_LOGGING_PROVIDER: '',
|
||||
_APP_LOGGING_CONFIG: '',
|
||||
_APP_USAGE_AGGREGATION_INTERVAL: '',
|
||||
_APP_WORKER_PER_CORE: '',
|
||||
_APP_REDIS_HOST: '',
|
||||
_APP_REDIS_PORT: '',
|
||||
_APP_REDIS_USER: '',
|
||||
_APP_REDIS_PASS: '',
|
||||
_APP_DB_HOST: '',
|
||||
_APP_DB_PORT: '',
|
||||
_APP_DB_SCHEMA: '',
|
||||
_APP_DB_USER: '',
|
||||
_APP_DB_PASS: '',
|
||||
_APP_DB_ROOT_PASS: '',
|
||||
_APP_INFLUXDB_HOST: '',
|
||||
_APP_INFLUXDB_PORT: '',
|
||||
_APP_STATSD_HOST: '',
|
||||
_APP_STATSD_PORT: '',
|
||||
_APP_SMTP_HOST: '',
|
||||
_APP_SMTP_PORT: '',
|
||||
_APP_SMTP_SECURE: '',
|
||||
_APP_SMTP_USERNAME: '',
|
||||
_APP_SMTP_PASSWORD: '',
|
||||
_APP_STORAGE_LIMIT: '',
|
||||
_APP_STORAGE_ANTIVIRUS: '',
|
||||
_APP_STORAGE_ANTIVIRUS_HOST: '',
|
||||
_APP_STORAGE_ANTIVIRUS_PORT: '',
|
||||
_APP_STORAGE_DEVICE: '',
|
||||
_APP_STORAGE_S3_ACCESS_KEY: '',
|
||||
_APP_STORAGE_S3_SECRET: '',
|
||||
_APP_STORAGE_S3_REGION: '',
|
||||
_APP_STORAGE_S3_BUCKET: '',
|
||||
_APP_STORAGE_DO_SPACES_ACCESS_KEY: '',
|
||||
_APP_STORAGE_DO_SPACES_SECRET: '',
|
||||
_APP_STORAGE_DO_SPACES_REGION: '',
|
||||
_APP_STORAGE_DO_SPACES_BUCKET: '',
|
||||
_APP_FUNCTIONS_SIZE_LIMIT: '',
|
||||
_APP_FUNCTIONS_TIMEOUT: '',
|
||||
_APP_FUNCTIONS_BUILD_TIMEOUT: '',
|
||||
_APP_FUNCTIONS_CONTAINERS: '',
|
||||
_APP_FUNCTIONS_CPUS: '',
|
||||
_APP_FUNCTIONS_MEMORY: '',
|
||||
_APP_FUNCTIONS_MEMORY_SWAP: '',
|
||||
_APP_FUNCTIONS_RUNTIMES: '',
|
||||
_APP_EXECUTOR_SECRET: '',
|
||||
_APP_EXECUTOR_RUNTIME_NETWORK: '',
|
||||
_APP_FUNCTIONS_ENVS: '',
|
||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD: '',
|
||||
DOCKERHUB_PULL_USERNAME: '',
|
||||
DOCKERHUB_PULL_PASSWORD: '',
|
||||
DOCKERHUB_PULL_EMAIL: '',
|
||||
_APP_MAINTENANCE_INTERVAL: '',
|
||||
_APP_MAINTENANCE_RETENTION_EXECUTION: '',
|
||||
_APP_MAINTENANCE_RETENTION_ABUSE: '',
|
||||
_APP_MAINTENANCE_RETENTION_AUDIT: ''
|
||||
};
|
||||
const config = {
|
||||
appwrite: {
|
||||
image: `${image}:${version}`,
|
||||
volumes: [
|
||||
`${id}-appwrite-uploads:/storage/uploads`,
|
||||
`${id}-appwrite-cache:/storage/cache`,
|
||||
`${id}-appwrite-config:/storage/config`,
|
||||
`${id}-appwrite-certificates:/storage/certificates`,
|
||||
`${id}-appwrite-functions:/storage/functions`
|
||||
],
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_WORKER_PER_CORE: variables._APP_WORKER_PER_CORE,
|
||||
_APP_LOCALE: variables._APP_LOCALE,
|
||||
_APP_CONSOLE_WHITELIST_ROOT: variables._APP_CONSOLE_WHITELIST_ROOT,
|
||||
_APP_CONSOLE_WHITELIST_EMAILS: variables._APP_CONSOLE_WHITELIST_EMAILS,
|
||||
_APP_CONSOLE_WHITELIST_IPS: variables._APP_CONSOLE_WHITELIST_IPS,
|
||||
_APP_SYSTEM_EMAIL_NAME: variables._APP_SYSTEM_EMAIL_NAME,
|
||||
_APP_SYSTEM_EMAIL_ADDRESS: variables._APP_SYSTEM_EMAIL_ADDRESS,
|
||||
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS: variables._APP_SYSTEM_SECURITY_EMAIL_ADDRESS,
|
||||
_APP_SYSTEM_RESPONSE_FORMAT: variables._APP_SYSTEM_RESPONSE_FORMAT,
|
||||
_APP_OPTIONS_ABUSE: variables._APP_OPTIONS_ABUSE,
|
||||
_APP_OPTIONS_FORCE_HTTPS: variables._APP_OPTIONS_FORCE_HTTPS,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_DOMAIN: variables._APP_DOMAIN,
|
||||
_APP_DOMAIN_TARGET: variables._APP_DOMAIN_TARGET,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_DB_HOST: variables._APP_DB_HOST,
|
||||
_APP_DB_PORT: variables._APP_DB_PORT,
|
||||
_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
|
||||
_APP_DB_USER: variables._APP_DB_USER,
|
||||
_APP_DB_PASS: variables._APP_DB_PASS,
|
||||
_APP_SMTP_HOST: variables._APP_SMTP_HOST,
|
||||
_APP_SMTP_PORT: variables._APP_SMTP_PORT,
|
||||
_APP_SMTP_SECURE: variables._APP_SMTP_SECURE,
|
||||
_APP_SMTP_USERNAME: variables._APP_SMTP_USERNAME,
|
||||
_APP_SMTP_PASSWORD: variables._APP_SMTP_PASSWORD,
|
||||
_APP_USAGE_STATS: variables._APP_USAGE_STATS,
|
||||
_APP_INFLUXDB_HOST: variables._APP_INFLUXDB_HOST,
|
||||
_APP_INFLUXDB_PORT: variables._APP_INFLUXDB_PORT,
|
||||
_APP_STORAGE_LIMIT: variables._APP_STORAGE_LIMIT,
|
||||
_APP_STORAGE_ANTIVIRUS: variables._APP_STORAGE_ANTIVIRUS,
|
||||
_APP_STORAGE_ANTIVIRUS_HOST: variables._APP_STORAGE_ANTIVIRUS_HOST,
|
||||
_APP_STORAGE_ANTIVIRUS_PORT: variables._APP_STORAGE_ANTIVIRUS_PORT,
|
||||
_APP_STORAGE_DEVICE: variables._APP_STORAGE_DEVICE,
|
||||
_APP_STORAGE_S3_ACCESS_KEY: variables._APP_STORAGE_S3_ACCESS_KEY,
|
||||
_APP_STORAGE_S3_SECRET: variables._APP_STORAGE_S3_SECRET,
|
||||
_APP_STORAGE_S3_REGION: variables._APP_STORAGE_S3_REGION,
|
||||
_APP_STORAGE_S3_BUCKET: variables._APP_STORAGE_S3_BUCKET,
|
||||
_APP_STORAGE_DO_SPACES_ACCESS_KEY: variables._APP_STORAGE_DO_SPACES_ACCESS_KEY,
|
||||
_APP_STORAGE_DO_SPACES_SECRET: variables._APP_STORAGE_DO_SPACES_SECRET,
|
||||
_APP_STORAGE_DO_SPACES_REGION: variables._APP_STORAGE_DO_SPACES_REGION,
|
||||
_APP_STORAGE_DO_SPACES_BUCKET: variables._APP_STORAGE_DO_SPACES_BUCKET,
|
||||
_APP_FUNCTIONS_SIZE_LIMIT: variables._APP_FUNCTIONS_SIZE_LIMIT,
|
||||
_APP_FUNCTIONS_TIMEOUT: variables._APP_FUNCTIONS_TIMEOUT,
|
||||
_APP_FUNCTIONS_BUILD_TIMEOUT: variables._APP_FUNCTIONS_BUILD_TIMEOUT,
|
||||
_APP_FUNCTIONS_CONTAINERS: variables._APP_FUNCTIONS_CONTAINERS,
|
||||
_APP_FUNCTIONS_CPUS: variables._APP_FUNCTIONS_CPUS,
|
||||
_APP_FUNCTIONS_MEMORY: variables._APP_FUNCTIONS_MEMORY,
|
||||
_APP_FUNCTIONS_MEMORY_SWAP: variables._APP_FUNCTIONS_MEMORY_SWAP,
|
||||
_APP_EXECUTOR_SECRET: variables._APP_EXECUTOR_SECRET,
|
||||
_APP_FUNCTIONS_RUNTIMES: variables._APP_FUNCTIONS_RUNTIMES,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG,
|
||||
_APP_STATSD_HOST: variables._APP_STATSD_HOST,
|
||||
_APP_STATSD_PORT: variables._APP_STATSD_PORT,
|
||||
_APP_MAINTENANCE_INTERVAL: variables._APP_MAINTENANCE_INTERVAL,
|
||||
_APP_MAINTENANCE_RETENTION_EXECUTION: variables._APP_MAINTENANCE_RETENTION_EXECUTION,
|
||||
_APP_MAINTENANCE_RETENTION_ABUSE: variables._APP_MAINTENANCE_RETENTION_ABUSE,
|
||||
_APP_MAINTENANCE_RETENTION_AUDIT: variables._APP_MAINTENANCE_RETENTION_AUDIT
|
||||
}
|
||||
},
|
||||
appwriteRealtime: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_WORKER_PER_CORE: variables._APP_WORKER_PER_CORE,
|
||||
_APP_OPTIONS_ABUSE: variables._APP_OPTIONS_ABUSE,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_DB_HOST: variables._APP_DB_HOST,
|
||||
_APP_DB_PORT: variables._APP_DB_PORT,
|
||||
_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
|
||||
_APP_DB_USER: variables._APP_DB_USER,
|
||||
_APP_DB_PASS: variables._APP_DB_PASS,
|
||||
_APP_USAGE_STATS: variables._APP_USAGE_STATS,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
|
||||
}
|
||||
},
|
||||
appwriteExecutor: {
|
||||
image: `${image}:${version}`,
|
||||
volumes: [
|
||||
`${id}-appwrite-functions:/storage/functions`,
|
||||
`/tmp:/tmp`,
|
||||
'/var/run/docker.sock:/var/run/docker.sock'
|
||||
],
|
||||
environmentVariables: {
|
||||
DOCKERHUB_PULL_USERNAME: variables.DOCKERHUB_PULL_USERNAME,
|
||||
DOCKERHUB_PULL_PASSWORD: variables.DOCKERHUB_PULL_PASSWORD,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG,
|
||||
_APP_VERSION: variables._APP_VERSION,
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_STORAGE_DEVICE: variables._APP_STORAGE_DEVICE,
|
||||
_APP_STORAGE_S3_ACCESS_KEY: variables._APP_STORAGE_S3_ACCESS_KEY,
|
||||
_APP_STORAGE_S3_SECRET: variables._APP_STORAGE_S3_SECRET,
|
||||
_APP_STORAGE_S3_REGION: variables._APP_STORAGE_S3_REGION,
|
||||
_APP_STORAGE_S3_BUCKET: variables._APP_STORAGE_S3_BUCKET,
|
||||
_APP_STORAGE_DO_SPACES_ACCESS_KEY: variables._APP_STORAGE_DO_SPACES_ACCESS_KEY,
|
||||
_APP_STORAGE_DO_SPACES_SECRET: variables._APP_STORAGE_DO_SPACES_SECRET,
|
||||
_APP_STORAGE_DO_SPACES_REGION: variables._APP_STORAGE_DO_SPACES_REGION,
|
||||
_APP_STORAGE_DO_SPACES_BUCKET: variables._APP_STORAGE_DO_SPACES_BUCKET,
|
||||
_APP_FUNCTIONS_CPUS: variables._APP_FUNCTIONS_CPUS,
|
||||
_APP_FUNCTIONS_MEMORY: variables._APP_FUNCTIONS_MEMORY,
|
||||
_APP_FUNCTIONS_MEMORY_SWAP: variables._APP_FUNCTIONS_MEMORY_SWAP,
|
||||
_APP_FUNCTIONS_TIMEOUT: variables._APP_FUNCTIONS_TIMEOUT,
|
||||
_APP_EXECUTOR_SECRET: variables._APP_EXECUTOR_SECRET,
|
||||
_APP_FUNCTIONS_RUNTIMES: variables._APP_FUNCTIONS_RUNTIMES,
|
||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD: variables._APP_FUNCTIONS_INACTIVE_THRESHOLD,
|
||||
_APP_EXECUTOR_RUNTIME_NETWORK: variables._APP_EXECUTOR_RUNTIME_NETWORK
|
||||
}
|
||||
},
|
||||
appwriteWorkerDatabase: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_DB_HOST: variables._APP_DB_HOST,
|
||||
_APP_DB_PORT: variables._APP_DB_PORT,
|
||||
_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
|
||||
_APP_DB_USER: variables._APP_DB_USER,
|
||||
_APP_DB_PASS: variables._APP_DB_PASS,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
|
||||
}
|
||||
},
|
||||
appwriteWorkerBuilds: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_EXECUTOR_SECRET: variables._APP_EXECUTOR_SECRET,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_DB_HOST: variables._APP_DB_HOST,
|
||||
_APP_DB_PORT: variables._APP_DB_PORT,
|
||||
_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
|
||||
_APP_DB_USER: variables._APP_DB_USER,
|
||||
_APP_DB_PASS: variables._APP_DB_PASS,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
|
||||
}
|
||||
},
|
||||
appwriteWorkerAudits: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_DB_HOST: variables._APP_DB_HOST,
|
||||
_APP_DB_PORT: variables._APP_DB_PORT,
|
||||
_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
|
||||
_APP_DB_USER: variables._APP_DB_USER,
|
||||
_APP_DB_PASS: variables._APP_DB_PASS,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
|
||||
}
|
||||
},
|
||||
appwriteWorkerWebhooks: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS: variables._APP_SYSTEM_SECURITY_EMAIL_ADDRESS,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
|
||||
}
|
||||
},
|
||||
appwriteWorkerDeletes: {
|
||||
image: `${image}:${version}`,
|
||||
volumes: [
|
||||
`${id}-appwrite-uploads:/storage/uploads`,
|
||||
`${id}-appwrite-cache:/storage/cache`,
|
||||
`${id}-appwrite-certificates:/storage/certificates`
|
||||
],
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_DB_HOST: variables._APP_DB_HOST,
|
||||
_APP_DB_PORT: variables._APP_DB_PORT,
|
||||
_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
|
||||
_APP_DB_USER: variables._APP_DB_USER,
|
||||
_APP_DB_PASS: variables._APP_DB_PASS,
|
||||
_APP_STORAGE_DEVICE: variables._APP_STORAGE_DEVICE,
|
||||
_APP_STORAGE_S3_ACCESS_KEY: variables._APP_STORAGE_S3_ACCESS_KEY,
|
||||
_APP_STORAGE_S3_SECRET: variables._APP_STORAGE_S3_SECRET,
|
||||
_APP_STORAGE_S3_REGION: variables._APP_STORAGE_S3_REGION,
|
||||
_APP_STORAGE_S3_BUCKET: variables._APP_STORAGE_S3_BUCKET,
|
||||
_APP_STORAGE_DO_SPACES_ACCESS_KEY: variables._APP_STORAGE_DO_SPACES_ACCESS_KEY,
|
||||
_APP_STORAGE_DO_SPACES_SECRET: variables._APP_STORAGE_DO_SPACES_SECRET,
|
||||
_APP_STORAGE_DO_SPACES_REGION: variables._APP_STORAGE_DO_SPACES_REGION,
|
||||
_APP_STORAGE_DO_SPACES_BUCKET: variables._APP_STORAGE_DO_SPACES_BUCKET,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
|
||||
}
|
||||
},
|
||||
appwriteWorkerCertificates: {
|
||||
image: `${image}:${version}`,
|
||||
volumes: [
|
||||
`${id}-appwrite-config:/storage/config`,
|
||||
`${id}-appwrite-certificates:/storage/certificates`
|
||||
],
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_SYSTEM_SECURITY_EMAIL_ADDRESS: variables._APP_SYSTEM_SECURITY_EMAIL_ADDRESS,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_DOMAIN_TARGET: variables._APP_DOMAIN_TARGET,
|
||||
_APP_DB_HOST: variables._APP_DB_HOST,
|
||||
_APP_DB_PORT: variables._APP_DB_PORT,
|
||||
_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
|
||||
_APP_DB_USER: variables._APP_DB_USER,
|
||||
_APP_DB_PASS: variables._APP_DB_PASS,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
|
||||
}
|
||||
},
|
||||
appwriteWorkerFunctions: {
|
||||
image: `${image}:${version}`,
|
||||
envvironmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_DB_HOST: variables._APP_DB_HOST,
|
||||
_APP_DB_PORT: variables._APP_DB_PORT,
|
||||
_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
|
||||
_APP_DB_USER: variables._APP_DB_USER,
|
||||
_APP_DB_PASS: variables._APP_DB_PASS,
|
||||
_APP_FUNCTIONS_TIMEOUT: variables._APP_FUNCTIONS_TIMEOUT,
|
||||
_APP_EXECUTOR_SECRET: variables._APP_EXECUTOR_SECRET,
|
||||
_APP_USAGE_STATS: variables._APP_USAGE_STATS,
|
||||
DOCKERHUB_PULL_USERNAME: variables.DOCKERHUB_PULL_USERNAME,
|
||||
DOCKERHUB_PULL_PASSWORD: variables.DOCKERHUB_PULL_PASSWORD
|
||||
}
|
||||
},
|
||||
appwriteWorkerMails: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_SYSTEM_EMAIL_NAME: variables._APP_SYSTEM_EMAIL_NAME,
|
||||
_APP_SYSTEM_EMAIL_ADDRESS: variables._APP_SYSTEM_EMAIL_ADDRESS,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_SMTP_HOST: variables._APP_SMTP_HOST,
|
||||
_APP_SMTP_PORT: variables._APP_SMTP_PORT,
|
||||
_APP_SMTP_SECURE: variables._APP_SMTP_SECURE,
|
||||
_APP_SMTP_USERNAME: variables._APP_SMTP_USERNAME,
|
||||
_APP_SMTP_PASSWORD: variables._APP_SMTP_PASSWORD,
|
||||
_APP_LOGGING_PROVIDER: variables._APP_LOGGING_PROVIDER,
|
||||
_APP_LOGGING_CONFIG: variables._APP_LOGGING_CONFIG
|
||||
}
|
||||
},
|
||||
appwriteMaintenance: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS,
|
||||
_APP_MAINTENANCE_INTERVAL: variables._APP_MAINTENANCE_INTERVAL,
|
||||
_APP_MAINTENANCE_RETENTION_EXECUTION: variables._APP_MAINTENANCE_RETENTION_EXECUTION,
|
||||
_APP_MAINTENANCE_RETENTION_ABUSE: variables._APP_MAINTENANCE_RETENTION_ABUSE,
|
||||
_APP_MAINTENANCE_RETENTION_AUDIT: variables._APP_MAINTENANCE_RETENTION_AUDIT
|
||||
}
|
||||
},
|
||||
appwriteUsage: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_OPENSSL_KEY_V1: variables._APP_OPENSSL_KEY_V1,
|
||||
_APP_DB_HOST: variables._APP_DB_HOST,
|
||||
_APP_DB_PORT: variables._APP_DB_PORT,
|
||||
_APP_DB_SCHEMA: variables._APP_DB_SCHEMA,
|
||||
_APP_DB_USER: variables._APP_DB_USER,
|
||||
_APP_DB_PASS: variables._APP_DB_PASS,
|
||||
_APP_INFLUXDB_HOST: variables._APP_INFLUXDB_HOST,
|
||||
_APP_INFLUXDB_PORT: variables._APP_INFLUXDB_PORT,
|
||||
_APP_USAGE_AGGREGATION_INTERVAL: variables._APP_USAGE_AGGREGATION_INTERVAL,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS
|
||||
}
|
||||
},
|
||||
appwriteSchedule: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
_APP_ENV: variables._APP_ENV,
|
||||
_APP_REDIS_HOST: variables._APP_REDIS_HOST,
|
||||
_APP_REDIS_PORT: variables._APP_REDIS_PORT,
|
||||
_APP_REDIS_USER: variables._APP_REDIS_USER,
|
||||
_APP_REDIS_PASS: variables._APP_REDIS_PASS
|
||||
}
|
||||
},
|
||||
mariadb: {
|
||||
image: 'mariadb:10.7',
|
||||
volumes: [`${id}-appwrite-mariadb:/var/lib/mysql`],
|
||||
environmentVariables: {
|
||||
MYSQL_ROOT_PASSWORD: variables._APP_DB_ROOT_PASS,
|
||||
MYSQL_DATABASE: variables._APP_DB_SCHEMA,
|
||||
MYSQL_USER: variables._APP_DB_USER,
|
||||
MYSQL_PASSWORD: variables._APP_DB_PASS
|
||||
}
|
||||
},
|
||||
redis: {
|
||||
image: 'redis:6.0-alpine3.12',
|
||||
volumes: [`${id}-appwrite-redis:/data`]
|
||||
},
|
||||
influxdb: {
|
||||
image: 'appwrite/influxdb:1.0.0',
|
||||
volumes: [`${id}-appwrite-influxdb:/var/lib/influxdb`]
|
||||
},
|
||||
telegraf: {
|
||||
image: 'appwrite/telegraf:1.0.0',
|
||||
environmentVariables: {
|
||||
_APP_INFLUXDB_HOST: variables._APP_INFLUXDB_HOST,
|
||||
_APP_INFLUXDB_PORT: variables._APP_INFLUXDB_PORT
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const composeFile: ComposeFile = {
|
||||
version: '3.8',
|
||||
services: {
|
||||
[id]: {
|
||||
container_name: id,
|
||||
image: config.image,
|
||||
networks: [network],
|
||||
volumes: [...config.appwrite.volumes],
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
labels: makeLabelForServices('appwrite'),
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
deploy: {
|
||||
restart_policy: {
|
||||
condition: 'on-failure',
|
||||
delay: '5s',
|
||||
max_attempts: 3,
|
||||
window: '120s'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networks: {
|
||||
[network]: {
|
||||
external: true
|
||||
}
|
||||
},
|
||||
volumes: {
|
||||
[config.volume.split(':')[0]]: {
|
||||
name: config.volume.split(':')[0]
|
||||
}
|
||||
}
|
||||
};
|
||||
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
35
src/routes/services/[id]/appwrite-wip/stop.json.ts
Normal file
35
src/routes/services/[id]/appwrite-wip/stop.json.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { getUserDetails, removeDestinationDocker } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const { destinationDockerId, destinationDocker, fqdn } = service;
|
||||
if (destinationDockerId) {
|
||||
const engine = destinationDocker.engine;
|
||||
|
||||
try {
|
||||
const found = await checkContainer(engine, id);
|
||||
if (found) {
|
||||
await removeDestinationDocker({ id, engine });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
@@ -1,19 +1,56 @@
|
||||
import { asyncExecShell, getDomain, getEngine, getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { t } from '$lib/translations';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import getPort from 'get-port';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = event.params;
|
||||
let { fqdn } = await event.request.json();
|
||||
let { fqdn, exposePort, otherFqdns } = await event.request.json();
|
||||
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (otherFqdns && otherFqdns.length > 0) otherFqdns = otherFqdns.map((f) => f.toLowerCase());
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
try {
|
||||
const found = await db.isDomainConfigured({ id, fqdn });
|
||||
let found = await db.isDomainConfigured({ id, fqdn });
|
||||
if (found) {
|
||||
throw {
|
||||
message: t.get('application.domain_already_in_use', {
|
||||
domain: getDomain(fqdn).replace('www.', '')
|
||||
})
|
||||
};
|
||||
}
|
||||
if (otherFqdns && otherFqdns.length > 0) {
|
||||
for (const ofqdn of otherFqdns) {
|
||||
const domain = getDomain(ofqdn);
|
||||
const nakedDomain = domain.replace('www.', '');
|
||||
found = await db.isDomainConfigured({ id, fqdn: ofqdn, checkOwn: true });
|
||||
if (found) {
|
||||
throw {
|
||||
message: t.get('application.domain_already_in_use', {
|
||||
domain: nakedDomain
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exposePort) {
|
||||
exposePort = Number(exposePort);
|
||||
|
||||
if (exposePort < 1024 || exposePort > 65535) {
|
||||
throw { message: `Exposed Port needs to be between 1024 and 65535.` };
|
||||
}
|
||||
|
||||
const publicPort = await getPort({ port: exposePort });
|
||||
if (publicPort !== exposePort) {
|
||||
throw { message: `Port ${exposePort} is already in use.` };
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: found ? 500 : 200,
|
||||
body: {
|
||||
|
||||
@@ -59,7 +59,7 @@ export const post: RequestHandler = async (event) => {
|
||||
fider: {
|
||||
image: `${image}:${version}`,
|
||||
environmentVariables: {
|
||||
HOST_DOMAIN: domain,
|
||||
BASE_URL: domain,
|
||||
DATABASE_URL: `postgresql://${postgresqlUser}:${postgresqlPassword}@${id}-postgresql:5432/${postgresqlDatabase}?sslmode=disable`,
|
||||
JWT_SECRET: `${jwtSecret.replace(/\$/g, '$$$')}`,
|
||||
EMAIL_NOREPLY: emailNoreply,
|
||||
|
||||
@@ -30,19 +30,43 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import cuid from 'cuid';
|
||||
import { browser } from '$app/env';
|
||||
import ServiceLinks from '$lib/components/ServiceLinks.svelte';
|
||||
import Services from './_Services/_Services.svelte';
|
||||
import { get } from '$lib/api';
|
||||
import { page } from '$app/stores';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
|
||||
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`;
|
||||
const { id } = $page.params;
|
||||
let usageLoading = false;
|
||||
let usage = {
|
||||
MemUsage: 0,
|
||||
CPUPerc: 0,
|
||||
NetIO: 0
|
||||
};
|
||||
let usageInterval;
|
||||
|
||||
async function getUsage() {
|
||||
if (usageLoading) return;
|
||||
usageLoading = true;
|
||||
const data = await get(`/services/${id}/usage.json`);
|
||||
usage = data.usage;
|
||||
usageLoading = false;
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
clearInterval(usageInterval);
|
||||
});
|
||||
onMount(async () => {
|
||||
await getUsage();
|
||||
usageInterval = setInterval(async () => {
|
||||
await getUsage();
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex h-20 items-center space-x-2 p-5 px-6 font-bold">
|
||||
@@ -52,6 +76,7 @@
|
||||
</div>
|
||||
<span class="text-xs">{service.name}</span>
|
||||
</div>
|
||||
|
||||
{#if service.fqdn}
|
||||
<a
|
||||
href={service.fqdn}
|
||||
@@ -77,5 +102,31 @@
|
||||
|
||||
<ServiceLinks {service} />
|
||||
</div>
|
||||
<div class="mx-auto max-w-4xl px-6 py-4">
|
||||
<div class="text-2xl font-bold">Service Usage</div>
|
||||
<div class="mx-auto">
|
||||
<dl class="relative mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
|
||||
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
|
||||
<dt class=" text-sm font-medium text-white">Used Memory / Memory Limit</dt>
|
||||
<dd class="mt-1 text-xl font-semibold text-white">
|
||||
{usage?.MemUsage}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
|
||||
<dt class="truncate text-sm font-medium text-white">Used CPU</dt>
|
||||
<dd class="mt-1 text-xl font-semibold text-white ">
|
||||
{usage?.CPUPerc}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden rounded px-4 py-5 text-center sm:p-6 sm:text-left">
|
||||
<dt class="truncate text-sm font-medium text-white">Network IO</dt>
|
||||
<dd class="mt-1 text-xl font-semibold text-white ">
|
||||
{usage?.NetIO}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<Services bind:service {isRunning} {readOnly} {settings} />
|
||||
|
||||
@@ -9,12 +9,17 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let { name, fqdn, exposePort } = await event.request.json();
|
||||
let {
|
||||
name,
|
||||
fqdn,
|
||||
exposePort,
|
||||
minio: { apiFqdn }
|
||||
} = await event.request.json();
|
||||
if (fqdn) fqdn = fqdn.toLowerCase();
|
||||
if (exposePort) exposePort = Number(exposePort);
|
||||
|
||||
if (apiFqdn) apiFqdn = apiFqdn.toLowerCase();
|
||||
try {
|
||||
await db.updateService({ id, fqdn, name, exposePort });
|
||||
await db.updateMinioService({ id, fqdn, apiFqdn, name, exposePort });
|
||||
return { status: 201 };
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
|
||||
@@ -3,7 +3,6 @@ import * as db from '$lib/database';
|
||||
import { promises as fs } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { startHttpProxy } from '$lib/haproxy';
|
||||
import { ErrorHandler, getFreePort, getServiceImage } from '$lib/database';
|
||||
import { makeLabelForServices } from '$lib/buildPacks/common';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
@@ -35,7 +34,6 @@ export const post: RequestHandler = async (event) => {
|
||||
const publicPort = await getFreePort();
|
||||
|
||||
const consolePort = 9001;
|
||||
const apiPort = 9000;
|
||||
|
||||
const { workdir } = await createDirectories({ repository: type, buildId: id });
|
||||
const image = getServiceImage(type);
|
||||
@@ -94,8 +92,7 @@ export const post: RequestHandler = async (event) => {
|
||||
try {
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} up -d`);
|
||||
await db.updateMinioService({ id, publicPort });
|
||||
await startHttpProxy(destinationDocker, id, publicPort, apiPort);
|
||||
await db.updateMinioServicePort({ id, publicPort });
|
||||
return {
|
||||
status: 200
|
||||
};
|
||||
|
||||
@@ -12,12 +12,7 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
const {
|
||||
destinationDockerId,
|
||||
destinationDocker,
|
||||
fqdn,
|
||||
minio: { publicPort }
|
||||
} = service;
|
||||
const { destinationDockerId, destinationDocker } = service;
|
||||
await db.updateMinioService({ id, publicPort: null });
|
||||
if (destinationDockerId) {
|
||||
const engine = destinationDocker.engine;
|
||||
@@ -30,11 +25,6 @@ export const post: RequestHandler = async (event) => {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
try {
|
||||
await stopTcpHttpProxy(destinationDocker, publicPort);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -27,6 +27,7 @@ export const post: RequestHandler = async (event) => {
|
||||
|
||||
const config = {
|
||||
image: `${image}:${version}`,
|
||||
volume: `${id}-nc:/usr/app/data`,
|
||||
environmentVariables: {}
|
||||
};
|
||||
if (serviceSecret.length > 0) {
|
||||
@@ -41,6 +42,7 @@ export const post: RequestHandler = async (event) => {
|
||||
container_name: id,
|
||||
image: config.image,
|
||||
networks: [network],
|
||||
volumes: [config.volume],
|
||||
environment: config.environmentVariables,
|
||||
restart: 'always',
|
||||
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
|
||||
@@ -59,6 +61,11 @@ export const post: RequestHandler = async (event) => {
|
||||
[network]: {
|
||||
external: true
|
||||
}
|
||||
},
|
||||
volumes: {
|
||||
[config.volume.split(':')[0]]: {
|
||||
name: config.volume.split(':')[0]
|
||||
}
|
||||
}
|
||||
};
|
||||
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||
|
||||
@@ -195,7 +195,6 @@ COPY ./init-db.sh /docker-entrypoint-initdb.d/init-db.sh`;
|
||||
}
|
||||
};
|
||||
const composeFileDestination = `${workdir}/docker-compose.yaml`;
|
||||
console.log(JSON.stringify(composeFile, null, 2));
|
||||
await fs.writeFile(composeFileDestination, yaml.dump(composeFile));
|
||||
await asyncExecShell(`DOCKER_HOST=${host} docker compose -f ${composeFileDestination} pull`);
|
||||
await asyncExecShell(
|
||||
|
||||
30
src/routes/services/[id]/usage.json.ts
Normal file
30
src/routes/services/[id]/usage.json.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import { getContainerUsage } from '$lib/haproxy';
|
||||
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 };
|
||||
|
||||
const { id } = event.params;
|
||||
|
||||
let usage = {};
|
||||
try {
|
||||
const service = await db.getService({ id, teamId });
|
||||
if (service.destinationDockerId) {
|
||||
[usage] = await Promise.all([getContainerUsage(service.destinationDocker.engine, id)]);
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
usage
|
||||
},
|
||||
headers: {}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
@@ -3,7 +3,12 @@ import { asyncExecShell, getEngine, getUserDetails } from '$lib/common';
|
||||
import { decrypt, encrypt } from '$lib/crypto';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler, generatePassword, getFreePort } from '$lib/database';
|
||||
import { checkContainer, startTcpProxy, stopTcpHttpProxy } from '$lib/haproxy';
|
||||
import {
|
||||
checkContainer,
|
||||
startTcpProxy,
|
||||
startTraefikTCPProxy,
|
||||
stopTcpHttpProxy
|
||||
} from '$lib/haproxy';
|
||||
import type { ComposeFile } from '$lib/types/composeFile';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import cuid from 'cuid';
|
||||
@@ -31,49 +36,51 @@ export const post: RequestHandler = async (event) => {
|
||||
});
|
||||
const {
|
||||
service: { destinationDockerId, destinationDocker },
|
||||
ftpPublicPort: oldPublicPort,
|
||||
ftpPublicPort,
|
||||
ftpUser: user,
|
||||
ftpPassword: savedPassword,
|
||||
ftpHostKey,
|
||||
ftpHostKeyPrivate
|
||||
} = data;
|
||||
if (user) ftpUser = user;
|
||||
if (savedPassword) ftpPassword = decrypt(savedPassword);
|
||||
const { network, engine } = destinationDocker;
|
||||
const settings = await db.prisma.setting.findFirst();
|
||||
const host = getEngine(engine);
|
||||
if (ftpEnabled) {
|
||||
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 { 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: {
|
||||
@@ -142,24 +149,7 @@ export const post: RequestHandler = async (event) => {
|
||||
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: {
|
||||
@@ -169,6 +159,18 @@ export const post: RequestHandler = async (event) => {
|
||||
}
|
||||
};
|
||||
} 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(id, destinationDocker, ftpPublicPort);
|
||||
return {
|
||||
status: 200,
|
||||
body: {}
|
||||
|
||||
@@ -72,8 +72,7 @@ export const post: RequestHandler = async (event) => {
|
||||
minPort,
|
||||
maxPort,
|
||||
isAutoUpdateEnabled,
|
||||
isDNSCheckEnabled,
|
||||
forceSave
|
||||
isDNSCheckEnabled
|
||||
} = await event.request.json();
|
||||
try {
|
||||
const { id } = await db.listSettings();
|
||||
|
||||
@@ -37,12 +37,13 @@
|
||||
import { getDomain } from '$lib/components/common';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
import { t } from '$lib/translations';
|
||||
import { features } from '$lib/store';
|
||||
import { features, isTraefikUsed } from '$lib/store';
|
||||
|
||||
let isRegistrationEnabled = settings.isRegistrationEnabled;
|
||||
let dualCerts = settings.dualCerts;
|
||||
let isAutoUpdateEnabled = settings.isAutoUpdateEnabled;
|
||||
let isDNSCheckEnabled = settings.isDNSCheckEnabled;
|
||||
$isTraefikUsed = settings.isTraefikUsed;
|
||||
|
||||
let minPort = settings.minPort;
|
||||
let maxPort = settings.maxPort;
|
||||
@@ -55,7 +56,8 @@
|
||||
let isFqdnSet = !!settings.fqdn;
|
||||
let loading = {
|
||||
save: false,
|
||||
remove: false
|
||||
remove: false,
|
||||
proxyMigration: false
|
||||
};
|
||||
|
||||
async function removeFqdn() {
|
||||
@@ -86,6 +88,7 @@
|
||||
if (name === 'isDNSCheckEnabled') {
|
||||
isDNSCheckEnabled = !isDNSCheckEnabled;
|
||||
}
|
||||
|
||||
await post(`/settings.json`, {
|
||||
isRegistrationEnabled,
|
||||
dualCerts,
|
||||
@@ -156,6 +159,20 @@
|
||||
function resetView() {
|
||||
forceSave = false;
|
||||
}
|
||||
async function migrateProxy(to) {
|
||||
if (loading.proxyMigration) return;
|
||||
try {
|
||||
loading.proxyMigration = true;
|
||||
await post(`/update.json`, { type: to });
|
||||
const data = await get(`/settings.json`);
|
||||
$isTraefikUsed = data.settings.isTraefikUsed;
|
||||
return toast.push('Proxy migration started, it takes a few seconds.');
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
loading.proxyMigration = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
@@ -192,6 +209,26 @@
|
||||
</div>
|
||||
<div class="grid grid-flow-row gap-2 px-10">
|
||||
<!-- <Language /> -->
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<div class="flex items-center py-2 pr-8">
|
||||
<div class="flex w-96 flex-col">
|
||||
<div class="text-xs font-bold text-stone-100 md:text-base">New Proxy Available!</div>
|
||||
<Explainer
|
||||
text="We are changing to <span class='text-sky-500 font-bold'>Traefik</span> as <span class='text-red-500 font-bold'>HAProxy</span> had several problems and uses a LOT of unnecessary memory (<span class='text-sky-500 font-bold'>~20MB</span> vs <span class='text-red-500 font-bold'>~200MB</span>).<br><br>You can switch back to HAProxy if something is not working and <span class='text-yellow-500 font-bold'>please let us know</span>!"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
disabled={loading.proxyMigration}
|
||||
class="bg-green-600 text-white hover:bg-green-500"
|
||||
on:click={() => migrateProxy($isTraefikUsed ? 'haproxy' : 'traefik')}
|
||||
>{loading.proxyMigration
|
||||
? 'Migrating...'
|
||||
: $isTraefikUsed
|
||||
? 'Switch back to HAProxy'
|
||||
: 'Migrate to Traefik'}</button
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-start">
|
||||
<div class="flex-col">
|
||||
<div class="pt-2 text-base font-bold text-stone-100">
|
||||
|
||||
@@ -84,6 +84,9 @@
|
||||
<form on:submit|preventDefault={newGithubApp} class="py-4">
|
||||
<div class="flex space-x-1 pb-5 font-bold">
|
||||
<div class="title">General</div>
|
||||
{#if source.apiUrl && source.htmlUrl && source.name}
|
||||
<button class=" bg-orange-600" type="submit">Create new GitHub App</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="grid grid-flow-row gap-2 px-10">
|
||||
<div class="grid grid-flow-row gap-2">
|
||||
@@ -117,11 +120,6 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if source.apiUrl && source.htmlUrl && source.name}
|
||||
<div class="text-center">
|
||||
<button class=" mt-8 bg-orange-600" type="submit">Create new GitHub App</button>
|
||||
</div>
|
||||
{/if}
|
||||
</form>
|
||||
{:else if source.githubApp?.installationId}
|
||||
<form on:submit|preventDefault={handleSubmit} class="py-4">
|
||||
@@ -173,7 +171,7 @@
|
||||
</form>
|
||||
{:else}
|
||||
<div class="text-center">
|
||||
<button class=" bg-orange-600 mt-8" on:click={() => installRepositories(source)}
|
||||
<button class=" mt-8 bg-orange-600" on:click={() => installRepositories(source)}
|
||||
>Install Repositories</button
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -34,14 +34,14 @@ export const get: RequestHandler = async (request) => {
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { type, latestVersion } = await event.request.json();
|
||||
const settings = await db.prisma.setting.findFirst();
|
||||
if (type === 'update') {
|
||||
try {
|
||||
if (!dev) {
|
||||
const { isAutoUpdateEnabled } = await db.prisma.setting.findFirst();
|
||||
await asyncExecShell(`docker pull coollabsio/coolify:${latestVersion}`);
|
||||
await asyncExecShell(`env | grep COOLIFY > .env`);
|
||||
await asyncExecShell(
|
||||
`sed -i '/COOLIFY_AUTO_UPDATE=/c\COOLIFY_AUTO_UPDATE=${isAutoUpdateEnabled}' .env`
|
||||
`sed -i '/COOLIFY_AUTO_UPDATE=/c\COOLIFY_AUTO_UPDATE=${settings.isAutoUpdateEnabled}' .env`
|
||||
);
|
||||
await asyncExecShell(
|
||||
`docker run --rm -tid --env-file .env -v /var/run/docker.sock:/var/run/docker.sock -v coolify-db coollabsio/coolify:${latestVersion} /bin/sh -c "env | grep COOLIFY > .env && echo 'TAG=${latestVersion}' >> .env && docker stop -t 0 coolify coolify-redis && docker rm coolify coolify-redis && docker compose up -d --force-recreate"`
|
||||
@@ -61,6 +61,44 @@ export const post: RequestHandler = async (event) => {
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
} else if (type === 'traefik') {
|
||||
try {
|
||||
// const found = await checkContainer('/var/run/docker.sock', 'coolify-haproxy');
|
||||
// if (found) {
|
||||
// await asyncExecShell(`docker stop -t 0 coolify-haproxy`);
|
||||
// await asyncExecShell(`docker rm coolify-haproxy`);
|
||||
// }
|
||||
// await startTraefikProxy('/var/run/docker.sock');
|
||||
await db.prisma.setting.update({
|
||||
where: { id: settings.id },
|
||||
data: { isTraefikUsed: true }
|
||||
});
|
||||
return {
|
||||
status: 200,
|
||||
body: {}
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
} else if (type === 'haproxy') {
|
||||
try {
|
||||
// const found = await checkContainer('/var/run/docker.sock', 'coolify-proxy');
|
||||
// if (found) {
|
||||
// await asyncExecShell(`docker stop -t 0 coolify-proxy`);
|
||||
// await asyncExecShell(`docker rm coolify-proxy`);
|
||||
// }
|
||||
// await startCoolifyProxy('/var/run/docker.sock');
|
||||
await db.prisma.setting.update({
|
||||
where: { id: settings.id },
|
||||
data: { isTraefikUsed: false }
|
||||
});
|
||||
return {
|
||||
status: 200,
|
||||
body: {}
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: 500
|
||||
|
||||
364
src/routes/webhooks/traefik/main.json.ts
Normal file
364
src/routes/webhooks/traefik/main.json.ts
Normal file
@@ -0,0 +1,364 @@
|
||||
import { dev } from '$app/env';
|
||||
import { asyncExecShell, getDomain, getEngine } from '$lib/common';
|
||||
import { supportedServiceTypesAndVersions } from '$lib/components/common';
|
||||
import * as db from '$lib/database';
|
||||
import { listServicesWithIncludes } from '$lib/database';
|
||||
import { checkContainer } from '$lib/haproxy';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
function configureMiddleware(
|
||||
{ id, container, port, domain, nakedDomain, isHttps, isWWW, isDualCerts, scriptName, type },
|
||||
traefik
|
||||
) {
|
||||
if (isHttps) {
|
||||
traefik.http.routers[id] = {
|
||||
entrypoints: ['web'],
|
||||
rule: `Host(\`${nakedDomain}\`) || Host(\`www.${nakedDomain}\`)`,
|
||||
service: `${id}`,
|
||||
middlewares: ['redirect-to-https']
|
||||
};
|
||||
|
||||
traefik.http.services[id] = {
|
||||
loadbalancer: {
|
||||
servers: [
|
||||
{
|
||||
url: `http://${container}:${port}`
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if (isDualCerts) {
|
||||
traefik.http.routers[`${id}-secure`] = {
|
||||
entrypoints: ['websecure'],
|
||||
rule: `Host(\`${nakedDomain}\`) || Host(\`www.${nakedDomain}\`)`,
|
||||
service: `${id}`,
|
||||
tls: {
|
||||
certresolver: 'letsencrypt'
|
||||
},
|
||||
middlewares: []
|
||||
};
|
||||
} else {
|
||||
if (isWWW) {
|
||||
traefik.http.routers[`${id}-secure-www`] = {
|
||||
entrypoints: ['websecure'],
|
||||
rule: `Host(\`www.${nakedDomain}\`)`,
|
||||
service: `${id}`,
|
||||
tls: {
|
||||
certresolver: 'letsencrypt'
|
||||
},
|
||||
middlewares: []
|
||||
};
|
||||
traefik.http.routers[`${id}-secure`] = {
|
||||
entrypoints: ['websecure'],
|
||||
rule: `Host(\`${nakedDomain}\`)`,
|
||||
service: `${id}`,
|
||||
tls: {
|
||||
domains: {
|
||||
main: `${domain}`
|
||||
}
|
||||
},
|
||||
middlewares: ['redirect-to-www']
|
||||
};
|
||||
traefik.http.routers[`${id}`].middlewares.push('redirect-to-www');
|
||||
} else {
|
||||
traefik.http.routers[`${id}-secure-www`] = {
|
||||
entrypoints: ['websecure'],
|
||||
rule: `Host(\`www.${nakedDomain}\`)`,
|
||||
service: `${id}`,
|
||||
tls: {
|
||||
domains: {
|
||||
main: `${domain}`
|
||||
}
|
||||
},
|
||||
middlewares: ['redirect-to-non-www']
|
||||
};
|
||||
traefik.http.routers[`${id}-secure`] = {
|
||||
entrypoints: ['websecure'],
|
||||
rule: `Host(\`${domain}\`)`,
|
||||
service: `${id}`,
|
||||
tls: {
|
||||
certresolver: 'letsencrypt'
|
||||
},
|
||||
middlewares: []
|
||||
};
|
||||
traefik.http.routers[`${id}`].middlewares.push('redirect-to-non-www');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
traefik.http.routers[id] = {
|
||||
entrypoints: ['web'],
|
||||
rule: `Host(\`${nakedDomain}\`) || Host(\`www.${nakedDomain}\`)`,
|
||||
service: `${id}`,
|
||||
middlewares: []
|
||||
};
|
||||
|
||||
traefik.http.routers[`${id}-secure`] = {
|
||||
entrypoints: ['websecure'],
|
||||
rule: `Host(\`${nakedDomain}\`) || Host(\`www.${nakedDomain}\`)`,
|
||||
service: `${id}`,
|
||||
tls: {
|
||||
domains: {
|
||||
main: `${nakedDomain}`
|
||||
}
|
||||
},
|
||||
middlewares: ['redirect-to-http']
|
||||
};
|
||||
|
||||
traefik.http.services[id] = {
|
||||
loadbalancer: {
|
||||
servers: [
|
||||
{
|
||||
url: `http://${container}:${port}`
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
if (!isDualCerts) {
|
||||
if (isWWW) {
|
||||
traefik.http.routers[`${id}`].middlewares.push('redirect-to-www');
|
||||
traefik.http.routers[`${id}-secure`].middlewares.push('redirect-to-www');
|
||||
} else {
|
||||
traefik.http.routers[`${id}`].middlewares.push('redirect-to-non-www');
|
||||
traefik.http.routers[`${id}-secure`].middlewares.push('redirect-to-non-www');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'plausibleanalytics' && scriptName && scriptName !== 'plausible.js') {
|
||||
if (!traefik.http.routers[`${id}`].middlewares.includes(`${id}-redir`)) {
|
||||
traefik.http.routers[`${id}`].middlewares.push(`${id}-redir`);
|
||||
}
|
||||
if (!traefik.http.routers[`${id}-secure`].middlewares.includes(`${id}-redir`)) {
|
||||
traefik.http.routers[`${id}-secure`].middlewares.push(`${id}-redir`);
|
||||
}
|
||||
}
|
||||
}
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const traefik = {
|
||||
http: {
|
||||
routers: {},
|
||||
services: {},
|
||||
middlewares: {
|
||||
'redirect-to-https': {
|
||||
redirectscheme: {
|
||||
scheme: 'https'
|
||||
}
|
||||
},
|
||||
'redirect-to-http': {
|
||||
redirectscheme: {
|
||||
scheme: 'http'
|
||||
}
|
||||
},
|
||||
'redirect-to-non-www': {
|
||||
redirectregex: {
|
||||
regex: '^https?://www\\.(.+)',
|
||||
replacement: 'http://${1}'
|
||||
}
|
||||
},
|
||||
'redirect-to-www': {
|
||||
redirectregex: {
|
||||
regex: '^https?://(?:www\\.)?(.+)',
|
||||
replacement: 'http://www.${1}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const applications = await db.prisma.application.findMany({
|
||||
include: { destinationDocker: true, settings: true }
|
||||
});
|
||||
const data = {
|
||||
applications: [],
|
||||
services: [],
|
||||
coolify: []
|
||||
};
|
||||
for (const application of applications) {
|
||||
const {
|
||||
fqdn,
|
||||
id,
|
||||
port,
|
||||
destinationDocker,
|
||||
destinationDockerId,
|
||||
settings: { previews, dualCerts }
|
||||
} = application;
|
||||
if (destinationDockerId) {
|
||||
const { engine, network } = destinationDocker;
|
||||
const isRunning = true;
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const nakedDomain = domain.replace(/^www\./, '');
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const isWWW = fqdn.includes('www.');
|
||||
if (isRunning) {
|
||||
data.applications.push({
|
||||
id,
|
||||
container: id,
|
||||
port: port || 3000,
|
||||
domain,
|
||||
nakedDomain,
|
||||
isRunning,
|
||||
isHttps,
|
||||
isWWW,
|
||||
isDualCerts: dualCerts
|
||||
});
|
||||
}
|
||||
if (previews) {
|
||||
const host = getEngine(engine);
|
||||
const { stdout } = await asyncExecShell(
|
||||
`DOCKER_HOST=${host} docker container ls --filter="status=running" --filter="network=${network}" --filter="name=${id}-" --format="{{json .Names}}"`
|
||||
);
|
||||
const containers = stdout
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter((a) => a)
|
||||
.map((c) => c.replace(/"/g, ''));
|
||||
if (containers.length > 0) {
|
||||
for (const container of containers) {
|
||||
const previewDomain = `${container.split('-')[1]}.${domain}`;
|
||||
const nakedDomain = previewDomain.replace(/^www\./, '');
|
||||
data.applications.push({
|
||||
id: container,
|
||||
container,
|
||||
port: port || 3000,
|
||||
domain: previewDomain,
|
||||
isRunning,
|
||||
nakedDomain,
|
||||
isHttps,
|
||||
isWWW,
|
||||
isDualCerts: dualCerts
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const services = await listServicesWithIncludes();
|
||||
|
||||
for (const service of services) {
|
||||
const {
|
||||
fqdn,
|
||||
id,
|
||||
type,
|
||||
destinationDocker,
|
||||
destinationDockerId,
|
||||
dualCerts,
|
||||
plausibleAnalytics
|
||||
} = service;
|
||||
if (destinationDockerId) {
|
||||
const { engine } = destinationDocker;
|
||||
const found = supportedServiceTypesAndVersions.find((a) => a.name === type);
|
||||
if (found) {
|
||||
const port = found.ports.main;
|
||||
const publicPort = service[type]?.publicPort;
|
||||
const isRunning = true;
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const nakedDomain = domain.replace(/^www\./, '');
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const isWWW = fqdn.includes('www.');
|
||||
if (isRunning) {
|
||||
// Plausible Analytics custom script
|
||||
let scriptName = false;
|
||||
if (type === 'plausibleanalytics' && plausibleAnalytics.scriptName !== 'plausible.js') {
|
||||
scriptName = plausibleAnalytics.scriptName;
|
||||
}
|
||||
|
||||
let container = id;
|
||||
let otherDomain = null;
|
||||
let otherNakedDomain = null;
|
||||
let otherIsHttps = null;
|
||||
let otherIsWWW = null;
|
||||
|
||||
if (type === 'minio' && service.minio.apiFqdn) {
|
||||
otherDomain = getDomain(service.minio.apiFqdn);
|
||||
otherNakedDomain = otherDomain.replace(/^www\./, '');
|
||||
otherIsHttps = service.minio.apiFqdn.startsWith('https://');
|
||||
otherIsWWW = service.minio.apiFqdn.includes('www.');
|
||||
}
|
||||
data.services.push({
|
||||
id,
|
||||
container,
|
||||
type,
|
||||
otherDomain,
|
||||
otherNakedDomain,
|
||||
otherIsHttps,
|
||||
otherIsWWW,
|
||||
port,
|
||||
publicPort,
|
||||
domain,
|
||||
nakedDomain,
|
||||
isRunning,
|
||||
isHttps,
|
||||
isWWW,
|
||||
isDualCerts: dualCerts,
|
||||
scriptName
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { fqdn, dualCerts } = await db.prisma.setting.findFirst();
|
||||
if (fqdn) {
|
||||
const domain = getDomain(fqdn);
|
||||
const nakedDomain = domain.replace(/^www\./, '');
|
||||
const isHttps = fqdn.startsWith('https://');
|
||||
const isWWW = fqdn.includes('www.');
|
||||
data.coolify.push({
|
||||
id: dev ? 'host.docker.internal' : 'coolify',
|
||||
container: dev ? 'host.docker.internal' : 'coolify',
|
||||
port: 3000,
|
||||
domain,
|
||||
nakedDomain,
|
||||
isHttps,
|
||||
isWWW,
|
||||
isDualCerts: dualCerts
|
||||
});
|
||||
}
|
||||
for (const application of data.applications) {
|
||||
configureMiddleware(application, traefik);
|
||||
}
|
||||
for (const service of data.services) {
|
||||
const { id, scriptName } = service;
|
||||
|
||||
configureMiddleware(service, traefik);
|
||||
if (service.type === 'minio') {
|
||||
service.id = id + '-minio';
|
||||
service.container = id;
|
||||
service.domain = service.otherDomain;
|
||||
service.nakedDomain = service.otherNakedDomain;
|
||||
service.isHttps = service.otherIsHttps;
|
||||
service.isWWW = service.otherIsWWW;
|
||||
service.port = 9000;
|
||||
configureMiddleware(service, traefik);
|
||||
}
|
||||
|
||||
if (scriptName) {
|
||||
traefik.http.middlewares[`${id}-redir`] = {
|
||||
replacepathregex: {
|
||||
regex: `/js/${scriptName}`,
|
||||
replacement: '/js/plausible.js'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
for (const coolify of data.coolify) {
|
||||
configureMiddleware(coolify, traefik);
|
||||
}
|
||||
if (Object.keys(traefik.http.routers).length === 0) {
|
||||
traefik.http.routers = null;
|
||||
}
|
||||
if (Object.keys(traefik.http.services).length === 0) {
|
||||
traefik.http.services = null;
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
...traefik
|
||||
}
|
||||
};
|
||||
};
|
||||
137
src/routes/webhooks/traefik/other.json.ts
Normal file
137
src/routes/webhooks/traefik/other.json.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { dev } from '$app/env';
|
||||
import { getDomain } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const id = event.url.searchParams.get('id');
|
||||
if (id) {
|
||||
const privatePort = event.url.searchParams.get('privatePort');
|
||||
const publicPort = event.url.searchParams.get('publicPort');
|
||||
const type = event.url.searchParams.get('type');
|
||||
const address = event.url.searchParams.get('address') || id;
|
||||
let traefik = {};
|
||||
if (publicPort && type && privatePort) {
|
||||
if (type === 'tcp') {
|
||||
traefik = {
|
||||
[type]: {
|
||||
routers: {
|
||||
[id]: {
|
||||
entrypoints: [type],
|
||||
rule: `HostSNI(\`*\`)`,
|
||||
service: id
|
||||
}
|
||||
},
|
||||
services: {
|
||||
[id]: {
|
||||
loadbalancer: {
|
||||
servers: [{ address: `${address}:${privatePort}` }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} else if (type === 'http') {
|
||||
const service = await db.prisma.service.findFirst({
|
||||
where: { id },
|
||||
include: { minio: true }
|
||||
});
|
||||
if (service) {
|
||||
if (service.type === 'minio') {
|
||||
if (service?.minio?.apiFqdn) {
|
||||
const {
|
||||
minio: { apiFqdn }
|
||||
} = service;
|
||||
const domain = getDomain(apiFqdn);
|
||||
const isHttps = apiFqdn.startsWith('https://');
|
||||
traefik = {
|
||||
[type]: {
|
||||
routers: {
|
||||
[id]: {
|
||||
entrypoints: [type],
|
||||
rule: `Host(\`${domain}\`)`,
|
||||
service: id
|
||||
}
|
||||
},
|
||||
services: {
|
||||
[id]: {
|
||||
loadbalancer: {
|
||||
servers: [{ url: `http://${id}:${privatePort}` }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (isHttps) {
|
||||
if (dev) {
|
||||
traefik[type].routers[id].tls = {
|
||||
domains: {
|
||||
main: `${domain}`
|
||||
}
|
||||
};
|
||||
} else {
|
||||
traefik[type].routers[id].tls = {
|
||||
certresolver: 'letsencrypt'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (service?.fqdn) {
|
||||
const domain = getDomain(service.fqdn);
|
||||
const isHttps = service.fqdn.startsWith('https://');
|
||||
traefik = {
|
||||
[type]: {
|
||||
routers: {
|
||||
[id]: {
|
||||
entrypoints: [type],
|
||||
rule: `Host(\`${domain}:${privatePort}\`)`,
|
||||
service: id
|
||||
}
|
||||
},
|
||||
services: {
|
||||
[id]: {
|
||||
loadbalancer: {
|
||||
servers: [{ url: `http://${id}:${privatePort}` }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (isHttps) {
|
||||
if (dev) {
|
||||
traefik[type].routers[id].tls = {
|
||||
domains: {
|
||||
main: `${domain}`
|
||||
}
|
||||
};
|
||||
} else {
|
||||
traefik[type].routers[id].tls = {
|
||||
certresolver: 'letsencrypt'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
...traefik
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 500
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user