Merge branch 'next' into tool-tip

This commit is contained in:
Andras Bacsai
2023-01-10 11:29:36 +01:00
committed by GitHub
324 changed files with 21840 additions and 2605 deletions

View File

@@ -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 &&

View File

@@ -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 />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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'}

View File

@@ -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')}

View File

@@ -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">

View File

@@ -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>

View File

@@ -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}

View File

@@ -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>

View File

@@ -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">

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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}

View File

@@ -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

View File

@@ -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')}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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}

View File

@@ -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