Merge branch 'next' into tool-tip
This commit is contained in:
@@ -23,7 +23,8 @@ interface AppSession {
|
||||
github: string | null,
|
||||
gitlab: string | null,
|
||||
},
|
||||
pendingInvitations: Array<any>
|
||||
pendingInvitations: Array<any>,
|
||||
isARM: boolean
|
||||
}
|
||||
interface AddToast {
|
||||
type?: "info" | "success" | "error",
|
||||
@@ -52,15 +53,15 @@ export const appSession: Writable<AppSession> = writable({
|
||||
github: null,
|
||||
gitlab: null
|
||||
},
|
||||
pendingInvitations: []
|
||||
pendingInvitations: [],
|
||||
isARM: false
|
||||
});
|
||||
export const disabledButton: Writable<boolean> = writable(false);
|
||||
export const isDeploymentEnabled: Writable<boolean> = writable(false);
|
||||
export function checkIfDeploymentEnabledApplications(isAdmin: boolean, application: any) {
|
||||
export function checkIfDeploymentEnabledApplications(application: any) {
|
||||
return !!(
|
||||
isAdmin &&
|
||||
(application.buildPack === 'compose') ||
|
||||
(application.fqdn || application.settings.isBot) &&
|
||||
(application.fqdn || application.settings?.isBot) &&
|
||||
((application.gitSource &&
|
||||
application.repository &&
|
||||
application.buildPack) || application.simpleDockerfile) &&
|
||||
@@ -68,9 +69,8 @@ export function checkIfDeploymentEnabledApplications(isAdmin: boolean, applicati
|
||||
|
||||
);
|
||||
}
|
||||
export function checkIfDeploymentEnabledServices(isAdmin: boolean, service: any) {
|
||||
export function checkIfDeploymentEnabledServices( service: any) {
|
||||
return (
|
||||
isAdmin &&
|
||||
service.fqdn &&
|
||||
service.destinationDocker &&
|
||||
service.version &&
|
||||
|
@@ -75,6 +75,7 @@
|
||||
$appSession.version = baseSettings.version;
|
||||
$appSession.whiteLabeled = baseSettings.whiteLabeled;
|
||||
$appSession.whiteLabeledDetails.icon = baseSettings.whiteLabeledIcon;
|
||||
$appSession.isARM = baseSettings.isARM;
|
||||
|
||||
$appSession.pendingInvitations = pendingInvitations;
|
||||
|
||||
@@ -135,6 +136,10 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let sidedrawerToggler: HTMLInputElement;
|
||||
|
||||
const closeDrawer = () => (sidedrawerToggler.checked = false);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -154,7 +159,7 @@
|
||||
{/if}
|
||||
|
||||
<div class="drawer">
|
||||
<input id="main-drawer" type="checkbox" class="drawer-toggle" />
|
||||
<input id="main-drawer" type="checkbox" class="drawer-toggle" bind:this={sidedrawerToggler} />
|
||||
<div class="drawer-content">
|
||||
{#if $appSession.userId}
|
||||
<Tooltip triggeredBy="#dashboard" placement="right" color="bg-pink-500">Dashboard</Tooltip>
|
||||
@@ -290,6 +295,29 @@
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
id="documentation"
|
||||
sveltekit:prefetch
|
||||
href="https://docs.coollabs.io/coolify/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="icons hover:text-info"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-9 h-9"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
@@ -368,6 +396,7 @@
|
||||
sveltekit:prefetch
|
||||
href="/"
|
||||
class:bg-pink-500={$page.url.pathname === '/'}
|
||||
on:click={closeDrawer}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -396,6 +425,7 @@
|
||||
sveltekit:prefetch
|
||||
href="/servers"
|
||||
class:bg-sky-500={$page.url.pathname.startsWith('/servers')}
|
||||
on:click={closeDrawer}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -421,6 +451,7 @@
|
||||
class="no-underline icons hover:text-white hover:bg-iam"
|
||||
href="/iam"
|
||||
class:bg-iam={$page.url.pathname.startsWith('/iam')}
|
||||
on:click={closeDrawer}
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -450,6 +481,7 @@
|
||||
href={$appSession.teamId === '0' ? '/settings/coolify' : '/settings/ssh'}
|
||||
class:bg-settings={$page.url.pathname.startsWith('/settings')}
|
||||
class:text-black={$page.url.pathname.startsWith('/settings')}
|
||||
on:click={closeDrawer}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -470,6 +502,30 @@
|
||||
Settings
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="no-underline icons hover:text-white hover:bg-info"
|
||||
href="https://docs.coollabs.io/coolify/"
|
||||
target="_blank"
|
||||
rel="noreferrer external"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-8 h-8"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25"
|
||||
/>
|
||||
</svg>
|
||||
Documentation
|
||||
</a>
|
||||
</li>
|
||||
<li class="flex-1 bg-transparent" />
|
||||
<div class="block lg:hidden">
|
||||
<UpdateAvailable />
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
export let application: any;
|
||||
import { status } from '$lib/store';
|
||||
import { appSession, status } from '$lib/store';
|
||||
import { page } from '$app/stores';
|
||||
</script>
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
<li class="menu-title">
|
||||
<span>Advanced</span>
|
||||
</li>
|
||||
{#if application.gitSourceId}
|
||||
{#if application.gitSourceId && $appSession.isAdmin}
|
||||
<li
|
||||
class="rounded"
|
||||
class:bg-coollabs={$page.url.pathname === `/applications/${$page.params.id}/revert`}
|
||||
@@ -295,6 +295,7 @@
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
{#if $appSession.isAdmin}
|
||||
<li
|
||||
class="rounded"
|
||||
class:bg-coollabs={$page.url.pathname === `/applications/${$page.params.id}/danger`}
|
||||
@@ -318,4 +319,5 @@
|
||||
</svg>Danger Zone</a
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
import { del, post, put } from '$lib/api';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import { addToast } from '$lib/store';
|
||||
import { addToast, appSession } from '$lib/store';
|
||||
import { t } from '$lib/translations';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
<label for="name" class="pb-5 uppercase lg:block hidden font-bold" />
|
||||
{/if}
|
||||
|
||||
{#if $appSession.isAdmin}
|
||||
<div class="flex justify-center h-full items-center pt-3">
|
||||
<div class="flex flex-row justify-center space-x-2">
|
||||
<div class="flex items-center justify-center">
|
||||
@@ -127,5 +128,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -10,7 +10,7 @@
|
||||
import { del, post, put } from '$lib/api';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import { addToast } from '$lib/store';
|
||||
import { addToast, appSession } from '$lib/store';
|
||||
import { t } from '$lib/translations';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
@@ -124,8 +124,9 @@
|
||||
<div class="flex justify-center h-full items-center pt-0 lg:pt-0 pl-4 lg:pl-0">
|
||||
<button
|
||||
on:click={() => updateSecret({ changeIsBuildSecret: true })}
|
||||
disabled={!$appSession.isAdmin}
|
||||
aria-pressed="false"
|
||||
class="relative inline-flex h-6 w-11 flex-shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out "
|
||||
class="relative inline-flex h-6 w-11 flex-shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out"
|
||||
class:bg-green-600={isBuildSecret}
|
||||
class:bg-stone-700={!isBuildSecret}
|
||||
>
|
||||
@@ -177,7 +178,7 @@
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="btn btn-sm btn-primary" on:click={addNewSecret}>Add</button>
|
||||
</div>
|
||||
{:else}
|
||||
{:else if $appSession.isAdmin}
|
||||
<div class="flex flex-row justify-center space-x-2">
|
||||
<div class="flex items-center justify-center">
|
||||
<button class="btn btn-sm btn-primary" on:click={() => updateSecret()}>Set</button>
|
||||
|
@@ -83,7 +83,7 @@
|
||||
let forceDelete = false;
|
||||
let stopping = false;
|
||||
const { id } = $page.params;
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications(application);
|
||||
|
||||
async function deleteApplication(name: string, force: boolean) {
|
||||
const sure = confirm($t('application.confirm_to_delete', { name }));
|
||||
@@ -292,7 +292,6 @@
|
||||
<a
|
||||
href={$isDeploymentEnabled ? `/applications/${id}/logs` : null}
|
||||
class="btn btn-sm text-sm gap-2"
|
||||
sveltekit:prefetch
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -363,7 +362,7 @@
|
||||
<button
|
||||
on:click={restartApplication}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
class="btn btn-sm gap-2"
|
||||
>
|
||||
<svg
|
||||
@@ -383,7 +382,7 @@
|
||||
</button>
|
||||
{/if}
|
||||
<button
|
||||
disabled={!$isDeploymentEnabled}
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
class="btn btn-sm gap-2"
|
||||
on:click={() => handleDeploySubmit(true)}
|
||||
>
|
||||
@@ -409,7 +408,7 @@
|
||||
<button
|
||||
on:click={stopApplication}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
class="btn btn-sm gap-2"
|
||||
>
|
||||
<svg
|
||||
@@ -432,7 +431,7 @@
|
||||
<button
|
||||
on:click={stopApplication}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
class="btn btn-sm gap-2"
|
||||
>
|
||||
<svg
|
||||
@@ -453,7 +452,7 @@
|
||||
{/if}
|
||||
<button
|
||||
class="btn btn-sm gap-2"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
on:click={() => handleDeploySubmit(true)}
|
||||
>
|
||||
{#if $status.application.overallStatus !== 'degraded'}
|
||||
|
@@ -51,6 +51,7 @@
|
||||
let isDBBranching = application.settings.isDBBranching;
|
||||
|
||||
async function changeSettings(name: any) {
|
||||
if (!$appSession.isAdmin) return
|
||||
if (name === 'previews') {
|
||||
previews = !previews;
|
||||
}
|
||||
@@ -102,7 +103,7 @@
|
||||
}
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications(application);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -119,6 +120,7 @@
|
||||
id="autodeploy"
|
||||
isCenter={false}
|
||||
bind:setting={autodeploy}
|
||||
disabled={!$appSession.isAdmin}
|
||||
on:click={() => changeSettings('autodeploy')}
|
||||
title={$t('application.enable_automatic_deployment')}
|
||||
description={$t('application.enable_auto_deploy_webhooks')}
|
||||
@@ -130,6 +132,7 @@
|
||||
id="previews"
|
||||
isCenter={false}
|
||||
bind:setting={previews}
|
||||
disabled={!$appSession.isAdmin}
|
||||
on:click={() => changeSettings('previews')}
|
||||
title={$t('application.enable_mr_pr_previews')}
|
||||
description={$t('application.enable_preview_deploy_mr_pr_requests')}
|
||||
|
@@ -58,7 +58,7 @@
|
||||
$status.application.overallStatus === 'degraded' ||
|
||||
$status.application.overallStatus === 'healthy' ||
|
||||
$status.application.initialLoading;
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications(application);
|
||||
let statues: any = {};
|
||||
let loading = {
|
||||
save: false,
|
||||
@@ -235,7 +235,7 @@
|
||||
}
|
||||
return errorNotification(error);
|
||||
} finally {
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications(application);
|
||||
}
|
||||
}
|
||||
async function handleSubmit(toast: boolean = true) {
|
||||
@@ -269,7 +269,7 @@
|
||||
}
|
||||
await saveForm(id, application, baseDatabaseBranch, dockerComposeConfiguration);
|
||||
setLocation(application, settings);
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledApplications(application);
|
||||
|
||||
forceSave = false;
|
||||
if (toast) {
|
||||
@@ -366,11 +366,12 @@
|
||||
async function reloadCompose() {
|
||||
if (loading.reloadCompose) return;
|
||||
loading.reloadCompose = true;
|
||||
const composeLocation = application.dockerComposeFileLocation.startsWith('/')
|
||||
? application.dockerComposeFileLocation
|
||||
: `/${application.dockerComposeFileLocation}`;
|
||||
try {
|
||||
if (application.gitSource.type === 'github') {
|
||||
const composeLocation = application.dockerComposeFileLocation.startsWith('/')
|
||||
? application.dockerComposeFileLocation
|
||||
: `/${application.dockerComposeFileLocation}`;
|
||||
|
||||
const headers = isPublicRepository
|
||||
? {}
|
||||
: {
|
||||
@@ -397,6 +398,17 @@
|
||||
if (!$appSession.tokens.gitlab) {
|
||||
await getGitlabToken();
|
||||
}
|
||||
|
||||
const composeLocation = application.dockerComposeFileLocation.startsWith('/')
|
||||
? application.dockerComposeFileLocation.substring(1) // Remove the '/' from the start
|
||||
: application.dockerComposeFileLocation;
|
||||
|
||||
// If the file is in a subdirectory, lastIndex will be > 0
|
||||
// Otherwise it will be -1 and path will be an empty string
|
||||
const lastIndex = composeLocation.lastIndexOf('/') + 1
|
||||
const path = composeLocation.substring(0, lastIndex)
|
||||
const fileName = composeLocation.substring(lastIndex)
|
||||
|
||||
const headers = isPublicRepository
|
||||
? {}
|
||||
: {
|
||||
@@ -404,13 +416,13 @@
|
||||
};
|
||||
const url = isPublicRepository
|
||||
? ``
|
||||
: `/v4/projects/${application.projectId}/repository/tree`;
|
||||
: `/v4/projects/${application.projectId}/repository/tree?path=${path}`;
|
||||
const files = await get(`${apiUrl}${url}`, {
|
||||
...headers
|
||||
});
|
||||
const dockerComposeFileYml = files.find(
|
||||
(file: { name: string; type: string }) =>
|
||||
file.name === composeLocation && file.type === 'blob'
|
||||
file.name === fileName && file.type === 'blob'
|
||||
);
|
||||
const id = dockerComposeFileYml.id;
|
||||
|
||||
@@ -490,7 +502,7 @@
|
||||
<div class="grid grid-flow-row gap-2 px-4">
|
||||
<div class="mt-2 grid grid-cols-2 items-center">
|
||||
<label for="name">{$t('forms.name')}</label>
|
||||
<input name="name" id="name" class="w-full" bind:value={application.name} required />
|
||||
<input name="name" id="name" class="w-full" bind:value={application.name} disabled={!$appSession.isAdmin} required />
|
||||
</div>
|
||||
{#if !isSimpleDockerfile}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
|
@@ -23,7 +23,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { asyncSleep, errorNotification, getRndInteger } from '$lib/common';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { addToast } from '$lib/store';
|
||||
import { addToast, appSession } from '$lib/store';
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
||||
|
||||
@@ -264,6 +264,7 @@
|
||||
{:else}
|
||||
<button
|
||||
id="restart"
|
||||
disabled={!$appSession.isAdmin}
|
||||
on:click={() => restartPreview(preview)}
|
||||
type="submit"
|
||||
class="icons bg-transparent text-sm flex items-center space-x-2"
|
||||
@@ -286,7 +287,12 @@
|
||||
{/if}
|
||||
|
||||
<Tooltip triggeredBy="#restart">Restart (useful to change secrets)</Tooltip>
|
||||
<button id="forceredeploypreview" class="icons" on:click={() => redeploy(preview)}>
|
||||
<button
|
||||
id="forceredeploypreview"
|
||||
class="icons"
|
||||
disabled={!$appSession.isAdmin}
|
||||
on:click={() => redeploy(preview)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6"
|
||||
@@ -310,7 +316,7 @@
|
||||
id="deletepreview"
|
||||
class="icons"
|
||||
class:hover:text-error={!loading.removing}
|
||||
disabled={loading.removing}
|
||||
disabled={loading.removing || !$appSession.isAdmin}
|
||||
on:click={() => removeApplication(preview)}
|
||||
><DeleteIcon />
|
||||
</button>
|
||||
|
@@ -25,7 +25,7 @@
|
||||
import pLimit from 'p-limit';
|
||||
import { page } from '$app/stores';
|
||||
import { get, post, put } from '$lib/api';
|
||||
import { addToast } from '$lib/store';
|
||||
import { addToast, appSession } from '$lib/store';
|
||||
import Secret from './_Secret.svelte';
|
||||
import PreviewSecret from './_PreviewSecret.svelte';
|
||||
import { errorNotification } from '$lib/common';
|
||||
@@ -48,10 +48,9 @@
|
||||
.map((secret) => {
|
||||
const [name, ...rest] = secret.split('=');
|
||||
const value = rest.join('=');
|
||||
const cleanValue = (value?.replaceAll('"', '') || '').trim();
|
||||
return {
|
||||
name: name.trim(),
|
||||
value: cleanValue.trim(),
|
||||
value: value.trim(),
|
||||
createSecret: !secrets.find((secret: any) => name === secret.name)
|
||||
};
|
||||
});
|
||||
@@ -107,46 +106,50 @@
|
||||
/>
|
||||
{/key}
|
||||
{/each}
|
||||
<div class="lg:pt-0 pt-10">
|
||||
<Secret on:refresh={refreshSecrets} length={secrets.length} isNewSecret />
|
||||
</div>
|
||||
{#if !application.settings.isBot && !application.simpleDockerfile}
|
||||
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
|
||||
<div class="title font-bold pb-3 pt-8">
|
||||
Preview Secrets <Explainer
|
||||
explanation="These values overwrite application secrets in PR/MR deployments. <br>Useful for creating <span class='text-green-500 font-bold'>staging</span> environments."
|
||||
/>
|
||||
{#if $appSession.isAdmin}
|
||||
<div class="lg:pt-0 pt-10">
|
||||
<Secret on:refresh={refreshSecrets} length={secrets.length} isNewSecret />
|
||||
</div>
|
||||
</div>
|
||||
{#if previewSecrets.length !== 0}
|
||||
{#each previewSecrets as secret, index}
|
||||
{#key index}
|
||||
<PreviewSecret
|
||||
{index}
|
||||
length={secrets.length}
|
||||
name={secret.name}
|
||||
value={secret.value}
|
||||
isBuildSecret={secret.isBuildSecret}
|
||||
on:refresh={refreshSecrets}
|
||||
/>
|
||||
{/key}
|
||||
{/each}
|
||||
{:else}
|
||||
Add secrets first to see Preview Secrets.
|
||||
{/if}
|
||||
{#if !application.settings.isBot && !application.simpleDockerfile}
|
||||
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
|
||||
<div class="title font-bold pb-3 pt-8">
|
||||
Preview Secrets <Explainer
|
||||
explanation="These values overwrite application secrets in PR/MR deployments. <br>Useful for creating <span class='text-green-500 font-bold'>staging</span> environments."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if previewSecrets.length !== 0}
|
||||
{#each previewSecrets as secret, index}
|
||||
{#key index}
|
||||
<PreviewSecret
|
||||
{index}
|
||||
length={secrets.length}
|
||||
name={secret.name}
|
||||
value={secret.value}
|
||||
isBuildSecret={secret.isBuildSecret}
|
||||
on:refresh={refreshSecrets}
|
||||
/>
|
||||
{/key}
|
||||
{/each}
|
||||
{:else}
|
||||
Add secrets first to see Preview Secrets.
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<form on:submit|preventDefault={getValues} class="mb-12 w-full">
|
||||
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2 pt-10">
|
||||
<div class="flex flex-row space-x-2">
|
||||
<div class="title font-bold pb-3 ">Paste <code>.env</code> file</div>
|
||||
<button type="submit" class="btn btn-sm bg-primary">Add Secrets in Batch</button>
|
||||
{#if $appSession.isAdmin}
|
||||
<form on:submit|preventDefault={getValues} class="mb-12 w-full">
|
||||
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2 pt-10">
|
||||
<div class="flex flex-row space-x-2">
|
||||
<div class="title font-bold pb-3 ">Paste <code>.env</code> file</div>
|
||||
<button type="submit" class="btn btn-sm bg-primary">Add Secrets in Batch</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
placeholder={`PORT=1337\nPASSWORD=supersecret`}
|
||||
bind:value={batchSecrets}
|
||||
class="mb-2 min-h-[200px] w-full"
|
||||
/>
|
||||
</form>
|
||||
<textarea
|
||||
placeholder={`PORT=1337\nPASSWORD=supersecret`}
|
||||
bind:value={batchSecrets}
|
||||
class="mb-2 min-h-[200px] w-full"
|
||||
/>
|
||||
</form>
|
||||
{/if}
|
||||
|
@@ -27,6 +27,7 @@
|
||||
import { get } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import { appSession } from '$lib/store';
|
||||
|
||||
let composeJson = JSON.parse(application?.dockerComposeFile || '{}');
|
||||
let predefinedVolumes: any[] = [];
|
||||
@@ -86,12 +87,15 @@
|
||||
<Storage on:refresh={refreshStorage} {storage} />
|
||||
{/key}
|
||||
{/each}
|
||||
<div class="Preview Secrets" class:pt-10={predefinedVolumes.length > 0}>
|
||||
{#if $appSession.isAdmin}
|
||||
<div class:pt-10={predefinedVolumes.length > 0}>
|
||||
Add New Volume <Explainer
|
||||
position="dropdown-bottom"
|
||||
explanation={$t('application.storage.persistent_storage_explainer')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Storage on:refresh={refreshStorage} isNew />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
export let database: any;
|
||||
import { status } from '$lib/store';
|
||||
import { status, appSession } from '$lib/store';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import { t } from '$lib/translations';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
@@ -56,7 +56,7 @@
|
||||
placeholder={$t('forms.generated_automatically_after_start')}
|
||||
id="rootUser"
|
||||
name="rootUser"
|
||||
value={database.rootUser}
|
||||
value={$appSession.isARM ? 'root' : database.rootUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
export let database: any;
|
||||
import { status } from '$lib/store';
|
||||
import { status, appSession } from '$lib/store';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import { t } from '$lib/translations';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
@@ -22,22 +22,24 @@
|
||||
bind:value={database.defaultDatabase}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUser"
|
||||
>Postgres User Password <Explainer
|
||||
explanation="Could be changed while the database is running."
|
||||
/></label
|
||||
>
|
||||
<CopyPasswordField
|
||||
disabled={!$status.database.isRunning}
|
||||
readonly={!$status.database.isRunning}
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="rootUserPassword"
|
||||
name="rootUserPassword"
|
||||
bind:value={database.rootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
{#if !$appSession.isARM}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="rootUser"
|
||||
>Postgres User Password <Explainer
|
||||
explanation="Could be changed while the database is running."
|
||||
/></label
|
||||
>
|
||||
<CopyPasswordField
|
||||
disabled={!$status.database.isRunning}
|
||||
readonly={!$status.database.isRunning}
|
||||
placeholder="Generated automatically after start"
|
||||
isPasswordField
|
||||
id="rootUserPassword"
|
||||
name="rootUserPassword"
|
||||
bind:value={database.rootUserPassword}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="dbUser">{$t('forms.user')}</label>
|
||||
<CopyPasswordField
|
||||
|
@@ -669,7 +669,7 @@
|
||||
<button class="btn btn-sm btn-primary" on:click={refreshStatusApplications}
|
||||
>{noInitialStatus.applications ? 'Load Status' : 'Refresh Status'}</button
|
||||
>
|
||||
{#if foundUnconfiguredApplication}
|
||||
{#if foundUnconfiguredApplication && $appSession.isAdmin}
|
||||
<button
|
||||
class="btn btn-sm"
|
||||
class:loading={loading.applications}
|
||||
@@ -783,11 +783,13 @@
|
||||
</svg>
|
||||
</a>
|
||||
{/if}
|
||||
{#if $appSession.isAdmin}
|
||||
<button
|
||||
class="icons hover:bg-green-500"
|
||||
on:click|stopPropagation|preventDefault={() =>
|
||||
deleteApplication(application.id)}><DeleteIcon /></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -896,11 +898,13 @@
|
||||
</svg>
|
||||
</a>
|
||||
{/if}
|
||||
{#if $appSession.isAdmin}
|
||||
<button
|
||||
class="icons hover:bg-green-500"
|
||||
on:click|stopPropagation|preventDefault={() =>
|
||||
deleteApplication(application.id)}><DeleteIcon /></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -991,11 +995,13 @@
|
||||
</svg>
|
||||
</a>
|
||||
{/if}
|
||||
{#if $appSession.isAdmin}
|
||||
<button
|
||||
class="icons hover:bg-pink-500"
|
||||
on:click|stopPropagation|preventDefault={() => deleteService(service.id)}
|
||||
><DeleteIcon /></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1077,11 +1083,13 @@
|
||||
</svg>
|
||||
</a>
|
||||
{/if}
|
||||
{#if $appSession.isAdmin}
|
||||
<button
|
||||
class="icons hover:bg-pink-500"
|
||||
on:click|stopPropagation|preventDefault={() => deleteService(service.id)}
|
||||
><DeleteIcon /></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1173,11 +1181,13 @@
|
||||
</svg>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $appSession.isAdmin}
|
||||
<button
|
||||
class="icons hover:bg-databases-100"
|
||||
on:click|stopPropagation|preventDefault={() => deleteDatabase(database.id)}
|
||||
><DeleteIcon /></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1259,11 +1269,13 @@
|
||||
</svg>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $appSession.isAdmin}
|
||||
<button
|
||||
class="icons hover:bg-databases"
|
||||
on:click|stopPropagation|preventDefault={() => deleteDatabase(database.id)}
|
||||
><DeleteIcon /></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,6 +2,7 @@
|
||||
export let service: any;
|
||||
export let template: any;
|
||||
import { page } from '$app/stores';
|
||||
import { appSession } from '$lib/store';
|
||||
import ServiceLinks from './_ServiceLinks.svelte';
|
||||
</script>
|
||||
|
||||
@@ -106,6 +107,7 @@
|
||||
</svg>Service</a
|
||||
>
|
||||
</li>
|
||||
{#if $appSession.isAdmin}
|
||||
<li class="menu-title">
|
||||
<span>Advanced</span>
|
||||
</li>
|
||||
@@ -132,4 +134,5 @@
|
||||
</svg>Danger Zone</a
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
import { del, post } from '$lib/api';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
|
||||
import { addToast } from '$lib/store';
|
||||
import { addToast, appSession } from '$lib/store';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
@@ -77,6 +77,7 @@
|
||||
/>
|
||||
</td>
|
||||
|
||||
{#if $appSession.isAdmin}
|
||||
<td>
|
||||
{#if isNewSecret}
|
||||
<div class="flex items-center justify-center">
|
||||
@@ -93,3 +94,4 @@
|
||||
</div>
|
||||
{/if}
|
||||
</td>
|
||||
{/if}
|
||||
|
@@ -76,10 +76,9 @@
|
||||
import { dev } from '$app/env';
|
||||
const { id } = $page.params;
|
||||
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledServices($appSession.isAdmin, service);
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledServices(service);
|
||||
|
||||
let statusInterval: any;
|
||||
|
||||
|
||||
async function deleteService() {
|
||||
const sure = confirm($t('application.confirm_to_delete', { name: service.name }));
|
||||
@@ -291,7 +290,7 @@
|
||||
</button>
|
||||
{:else if $status.service.overallStatus === 'healthy'}
|
||||
<button
|
||||
disabled={!$isDeploymentEnabled}
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
class="btn btn-sm gap-2"
|
||||
on:click={() => restartService()}
|
||||
>
|
||||
@@ -317,7 +316,7 @@
|
||||
<button
|
||||
on:click={() => stopService(false)}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
class="btn btn-sm gap-2"
|
||||
>
|
||||
<svg
|
||||
@@ -338,10 +337,10 @@
|
||||
</button>
|
||||
{:else if $status.service.overallStatus === 'degraded'}
|
||||
<button
|
||||
on:click={stopService}
|
||||
on:click={() => stopService()}
|
||||
type="submit"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
class="btn btn-sm gap-2"
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
class="btn btn-sm gap-2"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -362,7 +361,7 @@
|
||||
{#if $status.service.overallStatus === 'degraded'}
|
||||
<button
|
||||
class="btn btn-sm gap-2"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
on:click={() => restartService()}
|
||||
>
|
||||
<svg
|
||||
@@ -386,7 +385,7 @@
|
||||
{:else if $status.service.overallStatus === 'stopped'}
|
||||
<button
|
||||
class="btn btn-sm gap-2"
|
||||
disabled={!$isDeploymentEnabled}
|
||||
disabled={!$isDeploymentEnabled || !$appSession.isAdmin}
|
||||
on:click={() => startService()}
|
||||
>
|
||||
<svg
|
||||
|
@@ -110,7 +110,7 @@
|
||||
if (formData) service = await saveForm(formData, service);
|
||||
setLocation(service);
|
||||
forceSave = false;
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledServices($appSession.isAdmin, service);
|
||||
$isDeploymentEnabled = checkIfDeploymentEnabledServices(service);
|
||||
return addToast({
|
||||
message: 'Configuration saved.',
|
||||
type: 'success'
|
||||
@@ -165,6 +165,7 @@
|
||||
}
|
||||
}
|
||||
async function changeSettings(name: any) {
|
||||
if (!$appSession.isAdmin) return
|
||||
try {
|
||||
if (name === 'dualCerts') {
|
||||
dualCerts = !dualCerts;
|
||||
@@ -277,7 +278,7 @@
|
||||
<div class="grid grid-flow-row gap-2 px-4">
|
||||
<div class="mt-2 grid grid-cols-2 items-center">
|
||||
<label for="name">{$t('forms.name')}</label>
|
||||
<input name="name" id="name" class="w-full" bind:value={service.name} required />
|
||||
<input name="name" id="name" class="w-full" disabled={!$appSession.isAdmin} bind:value={service.name} required />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="version">Version / Tag</label>
|
||||
@@ -386,7 +387,7 @@
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<Setting
|
||||
id="dualCerts"
|
||||
disabled={$status.service.isRunning}
|
||||
disabled={$status.service.isRunning || !$appSession.isAdmin}
|
||||
dataTooltip={$t('forms.must_be_stopped_to_modify')}
|
||||
bind:setting={dualCerts}
|
||||
title={$t('application.ssl_www_and_non_www')}
|
||||
|
@@ -25,7 +25,7 @@
|
||||
import { get } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
import pLimit from 'p-limit';
|
||||
import { addToast } from '$lib/store';
|
||||
import { addToast, appSession } from '$lib/store';
|
||||
import { saveSecret } from './utils';
|
||||
const limit = pLimit(1);
|
||||
|
||||
@@ -83,7 +83,12 @@
|
||||
{#each secrets as secret}
|
||||
{#key secret.id}
|
||||
<tr>
|
||||
<Secret name={secret.name} value={secret.value} readonly={secret.readOnly} on:refresh={refreshSecrets} />
|
||||
<Secret
|
||||
name={secret.name}
|
||||
value={secret.value}
|
||||
readonly={secret.readOnly}
|
||||
on:refresh={refreshSecrets}
|
||||
/>
|
||||
</tr>
|
||||
{/key}
|
||||
{/each}
|
||||
@@ -93,18 +98,20 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<form on:submit|preventDefault={getValues} class="mb-12 w-full">
|
||||
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2 pt-10">
|
||||
<div class="flex flex-row space-x-2">
|
||||
<div class="title font-bold pb-3 ">Paste <code>.env</code> file</div>
|
||||
<button type="submit" class="btn btn-sm bg-primary">Add Secrets in Batch</button>
|
||||
{#if $appSession.isAdmin}
|
||||
<form on:submit|preventDefault={getValues} class="mb-12 w-full">
|
||||
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2 pt-10">
|
||||
<div class="flex flex-row space-x-2">
|
||||
<div class="title font-bold pb-3 ">Paste <code>.env</code> file</div>
|
||||
<button type="submit" class="btn btn-sm bg-primary">Add Secrets in Batch</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
placeholder={`PORT=1337\nPASSWORD=supersecret`}
|
||||
bind:value={batchSecrets}
|
||||
class="mb-2 min-h-[200px] w-full"
|
||||
/>
|
||||
</form>
|
||||
<textarea
|
||||
placeholder={`PORT=1337\nPASSWORD=supersecret`}
|
||||
bind:value={batchSecrets}
|
||||
class="mb-2 min-h-[200px] w-full"
|
||||
/>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
|
@@ -26,6 +26,7 @@
|
||||
import { get } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import { appSession } from '$lib/store';
|
||||
|
||||
const { id } = $page.params;
|
||||
async function refreshStorage() {
|
||||
@@ -81,10 +82,11 @@
|
||||
{/key}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<div class="title" class:pt-10={persistentStorages.filter((s) => s.predefined).length > 0}>
|
||||
Add New Volume
|
||||
</div>
|
||||
<Storage on:refresh={refreshStorage} isNew {services} />
|
||||
{#if $appSession.isAdmin}
|
||||
<div class="title" class:pt-10={persistentStorages.filter((s) => s.predefined).length > 0}>
|
||||
Add New Volume
|
||||
</div>
|
||||
<Storage on:refresh={refreshStorage} isNew {services} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -190,7 +190,7 @@
|
||||
id="name"
|
||||
required
|
||||
bind:value={source.name}
|
||||
disabled={$appSession.teamId !== '0'}
|
||||
disabled={!$appSession.isAdmin}
|
||||
/>
|
||||
<label for="htmlUrl">HTML URL</label>
|
||||
<input
|
||||
@@ -236,16 +236,18 @@
|
||||
placeholder="eg: coollabsio"
|
||||
bind:value={source.organization}
|
||||
/>
|
||||
<Setting
|
||||
customClass="pt-4"
|
||||
id="autodeploy"
|
||||
isCenter={false}
|
||||
disabled={$appSession.teamId !== '0'}
|
||||
bind:setting={source.isSystemWide}
|
||||
on:click={() => changeSettings('isSystemWide', true)}
|
||||
title="System Wide Git Source"
|
||||
description="System Wide Git Sources are available to all the users in your Coolify instance. <br><br> <span class='font-bold text-warning'>Use with caution, as it can be a security risk.</span>"
|
||||
/>
|
||||
{#if $appSession.isAdmin}
|
||||
<Setting
|
||||
customClass="pt-4"
|
||||
id="autodeploy"
|
||||
isCenter={false}
|
||||
disabled={$appSession.teamId !== '0'}
|
||||
bind:setting={source.isSystemWide}
|
||||
on:click={() => changeSettings('isSystemWide', true)}
|
||||
title="System Wide Git Source"
|
||||
description="System Wide Git Sources are available to all the users in your Coolify instance. <br><br> <span class='font-bold text-warning'>Use with caution, as it can be a security risk.</span>"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
{:else}
|
||||
|
@@ -51,7 +51,8 @@
|
||||
appId: source.gitlabApp.appId,
|
||||
appSecret: source.gitlabApp.appSecret,
|
||||
groupName: source.gitlabApp.groupName,
|
||||
customPort: source.customPort
|
||||
customPort: source.customPort,
|
||||
customUser: source.customUser,
|
||||
});
|
||||
const from = $page.url.searchParams.get('from');
|
||||
if (from) {
|
||||
@@ -70,7 +71,8 @@
|
||||
name: source.name,
|
||||
htmlUrl: source.htmlUrl.replace(/\/$/, ''),
|
||||
apiUrl: source.apiUrl.replace(/\/$/, ''),
|
||||
customPort: source.customPort
|
||||
customPort: source.customPort,
|
||||
customUser: source.customUser
|
||||
});
|
||||
return addToast({
|
||||
message: 'Configuration saved.',
|
||||
@@ -244,6 +246,22 @@
|
||||
/>
|
||||
</div>
|
||||
{#if selfHosted}
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="customPort" class="text-base font-bold text-stone-100"
|
||||
>Custom SSH User <Explainer
|
||||
explanation={'If you use a self-hosted version of Git, you can provide a custom SSH user for all the Git related actions.'}
|
||||
/></label
|
||||
>
|
||||
<input
|
||||
class="w-full"
|
||||
name="customUser"
|
||||
id="customUser"
|
||||
disabled={!selfHosted}
|
||||
readonly={!selfHosted}
|
||||
required
|
||||
bind:value={source.customUser}
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<label for="customPort" class="text-base font-bold text-stone-100"
|
||||
>Custom SSH Port <Explainer
|
||||
|
Reference in New Issue
Block a user