Merge branch 'next' into main

This commit is contained in:
Andras Bacsai
2022-11-29 10:50:00 +01:00
committed by GitHub
220 changed files with 11969 additions and 9333 deletions

View File

@@ -6,7 +6,7 @@
<ul class="menu border bg-coolgray-100 border-coolgray-200 rounded p-2 space-y-2 sticky top-4">
<li class="menu-title">
<span>Configuration</span>
<span>General</span>
</li>
{#if application.gitSource?.htmlUrl && application.repository && application.branch}
<li>
@@ -86,7 +86,7 @@
<path
d="M7 10h3v-3l-3.5 -3.5a6 6 0 0 1 8 8l6 6a2 2 0 0 1 -3 3l-6 -6a6 6 0 0 1 -8 -8l3.5 3.5"
/>
</svg>Build & Deploy</a
</svg>Configuration</a
>
</li>
<li

View File

@@ -32,10 +32,10 @@
}
</script>
<div class="w-full font-bold grid grid-cols-1 lg:grid-cols-4 gap-2 pb-2">
<div class="w-full grid grid-cols-1 lg:grid-cols-4 gap-2 pb-2">
<div class="flex flex-col">
{#if index === 0 || length === 0}
<label for="name" class="pb-2 uppercase">name</label>
<label for="name" class="pb-2 uppercase font-bold">name</label>
{/if}
<input
@@ -50,7 +50,7 @@
</div>
<div class="flex flex-col">
{#if index === 0 || length === 0}
<label for="value" class="pb-2 uppercase">value</label>
<label for="value" class="pb-2 uppercase font-bold">value</label>
{/if}
<CopyPasswordField
@@ -63,9 +63,12 @@
</div>
<div class="flex lg:flex-col flex-row justify-start items-center pt-3 lg:pt-0">
{#if index === 0 || length === 0}
<label for="name" class="pb-2 uppercase lg:block hidden">Need during buildtime?</label>
<label for="name" class="pb-2 uppercase lg:block hidden font-bold"
>Need during buildtime?</label
>
{/if}
<label for="name" class="pb-2 uppercase lg:hidden block">Need during buildtime?</label>
<label for="name" class="pb-2 uppercase lg:hidden block font-bold">Need during buildtime?</label
>
<div class="flex justify-center h-full items-center pt-0 lg:pt-0 pl-4 lg:pl-0">
<button
@@ -114,7 +117,7 @@
</div>
<div class="flex flex-row lg:flex-col lg:items-center items-start">
{#if index === 0 || length === 0}
<label for="name" class="pb-2 uppercase lg:block hidden">Actions</label>
<label for="name" class="pb-5 uppercase lg:block hidden font-bold" />
{/if}
<div class="flex justify-center h-full items-center pt-3">

View File

@@ -39,11 +39,11 @@
async function addNewSecret() {
try {
if (!name) return errorNotification({ message: 'Name is required.' });
if (!value) return errorNotification({ message: 'Value is required.' });
if (!name.trim()) return errorNotification({ message: 'Name is required.' });
if (!value.trim()) return errorNotification({ message: 'Value is required.' });
await post(`/applications/${id}/secrets`, {
name,
value,
name: name.trim(),
value: value.trim(),
isBuildSecret
});
cleanupState();
@@ -64,8 +64,8 @@
if (isNewSecret) return;
try {
await put(`/applications/${id}/secrets`, {
name,
value,
name: name.trim(),
value: value.trim(),
isBuildSecret: changeIsBuildSecret ? isBuildSecret : undefined
});
addToast({
@@ -79,10 +79,10 @@
}
</script>
<div class="w-full font-bold grid grid-cols-1 lg:grid-cols-4 gap-2 pb-2">
<div class="w-full grid grid-cols-1 lg:grid-cols-4 gap-2 pb-2">
<div class="flex flex-col">
{#if (index === 0 && !isNewSecret) || length === 0}
<label for="name" class="pb-2 uppercase">name</label>
<label for="name" class="pb-2 uppercase font-bold">name</label>
{/if}
<input
@@ -91,7 +91,7 @@
required
placeholder="EXAMPLE_VARIABLE"
readonly={!isNewSecret}
class=" w-full"
class="w-full"
class:bg-coolblack={!isNewSecret}
class:border={!isNewSecret}
class:border-dashed={!isNewSecret}
@@ -101,7 +101,7 @@
</div>
<div class="flex flex-col">
{#if (index === 0 && !isNewSecret) || length === 0}
<label for="value" class="pb-2 uppercase">value</label>
<label for="value" class="pb-2 uppercase font-bold">value</label>
{/if}
<CopyPasswordField
@@ -114,9 +114,12 @@
</div>
<div class="flex lg:flex-col flex-row justify-start items-center pt-3 lg:pt-0">
{#if (index === 0 && !isNewSecret) || length === 0}
<label for="name" class="pb-2 uppercase lg:block hidden">Need during buildtime?</label>
<label for="name" class="pb-2 uppercase lg:block hidden font-bold"
>Need during buildtime?</label
>
{/if}
<label for="name" class="pb-2 uppercase lg:hidden block">Need during buildtime?</label>
<label for="name" class="pb-2 uppercase lg:hidden block font-bold">Need during buildtime?</label
>
<div class="flex justify-center h-full items-center pt-0 lg:pt-0 pl-4 lg:pl-0">
<button
@@ -166,7 +169,7 @@
</div>
<div class="flex flex-row lg:flex-col lg:items-center items-start">
{#if (index === 0 && !isNewSecret) || length === 0}
<label for="name" class="pb-2 uppercase lg:block hidden">Actions</label>
<label for="name" class="pb-5 uppercase lg:block hidden font-bold" />
{/if}
<div class="flex justify-center h-full items-center pt-3">

View File

@@ -59,36 +59,55 @@
}
</script>
<div class="w-full font-bold grid gap-2">
<div class="flex flex-col pb-2">
<div class="flex flex-col lg:flex-row lg:space-y-0 space-y-2">
<div class="w-full lg:px-0 px-4">
{#if storage.predefined}
<div class="flex flex-col lg:flex-row gap-4 pb-2">
<input disabled readonly class="w-full" value={storage.id} />
<input disabled readonly class="w-full" bind:value={storage.path} />
</div>
{:else}
<div class="flex gap-4 pb-2" class:pt-8={isNew}>
{#if storage.applicationId}
{#if storage.oldPath}
<input
disabled
readonly
class="w-full"
value="{storage.applicationId}{storage.path.replace(/\//gi, '-').replace('-app', '')}"
/>
{:else}
<input
disabled
readonly
class="w-full"
value="{storage.applicationId}{storage.path.replace(/\//gi, '-')}"
/>
{/if}
{/if}
<input
class="w-full lg:w-64"
disabled={!isNew}
readonly={!isNew}
class="w-full"
bind:value={storage.path}
required
placeholder="eg: /sqlite.db"
placeholder="eg: /data"
/>
{#if isNew}
<div class="flex items-center justify-center w-full lg:w-64">
<button class="btn btn-sm btn-primary" on:click={() => saveStorage(true)}
>{$t('forms.add')}</button
>
</div>
{:else}
<div class="flex flex-row items-center justify-center space-x-2 w-full lg:w-64">
<div class="flex items-center justify-center">
<button class="btn btn-sm btn-primary" on:click={() => saveStorage(false)}
>{$t('forms.set')}</button
<div class="flex items-center justify-center">
{#if isNew}
<div class="w-full lg:w-64">
<button class="btn btn-sm btn-primary w-full" on:click={() => saveStorage(true)}
>{$t('forms.add')}</button
>
</div>
{:else}
<div class="flex justify-center">
<button class="btn btn-sm btn-error" on:click={removeStorage}
>{$t('forms.remove')}</button
>
</div>
</div>
{/if}
{/if}
</div>
</div>
</div>
{/if}
</div>

View File

@@ -75,7 +75,7 @@
let statusInterval: any;
let forceDelete = false;
let stopping = false;
const { id } = $page.params;
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
@@ -138,17 +138,17 @@
}
async function stopApplication() {
try {
$status.application.initialLoading = true;
stopping = true;
await post(`/applications/${id}/stop`, {});
} catch (error) {
return errorNotification(error);
} finally {
$status.application.initialLoading = false;
stopping = false;
await getStatus();
}
}
async function getStatus() {
if ($status.application.loading) return;
if ($status.application.loading && stopping) return;
$status.application.loading = true;
const data = await get(`/applications/${id}/status`);
@@ -194,6 +194,8 @@
onDestroy(() => {
$status.application.initialLoading = true;
$status.application.loading = false;
$status.application.statuses = [];
$status.application.overallStatus = 'stopped';
$location = null;
$isDeploymentEnabled = false;
clearInterval(statusInterval);
@@ -233,7 +235,7 @@
class:text-red-500={$status.application.overallStatus === 'stopped'}
>
{$status.application.overallStatus === 'healthy'
? 'Running'
? 'Healthy'
: $status.application.overallStatus === 'degraded'
? 'Degraded'
: 'Stopped'}
@@ -242,14 +244,14 @@
{/if}
</div>
{#if $page.url.pathname.startsWith(`/applications/${id}/configuration/`)}
<div class="px-2">
<div class="px-4">
{#if forceDelete}
<button
on:click={() => deleteApplication(application.name, true)}
disabled={!$appSession.isAdmin}
class:bg-red-600={$appSession.isAdmin}
class:hover:bg-red-500={$appSession.isAdmin}
class="btn btn-sm btn-error text-sm"
class="btn btn-sm btn-error hover:bg-red-700 text-sm w-64"
>
Force Delete Application
</button>
@@ -259,7 +261,7 @@
disabled={!$appSession.isAdmin}
class:bg-red-600={$appSession.isAdmin}
class:hover:bg-red-500={$appSession.isAdmin}
class="btn btn-sm btn-error text-sm"
class="btn btn-sm btn-error hover:bg-red-700 text-sm w-64"
>
Delete Application
</button>
@@ -296,7 +298,29 @@
Application Error
</a>
{/if}
{#if $status.application.initialLoading}
{#if stopping}
<button class="btn btn-ghost btn-sm gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 animate-spin duration-500 ease-in-out"
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>
Stopping...
</button>
{:else if $status.application.initialLoading}
<button class="btn btn-ghost btn-sm gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -319,27 +343,6 @@
Loading...
</button>
{:else if $status.application.overallStatus === 'healthy'}
<button
on:click={stopApplication}
type="submit"
disabled={!$isDeploymentEnabled}
class="btn btn-sm btn-error gap-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-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" />
<rect x="6" y="5" width="4" height="14" rx="1" />
<rect x="14" y="5" width="4" height="14" rx="1" />
</svg> Stop
</button>
{#if application.buildPack !== 'compose'}
<button
on:click={restartApplication}
@@ -387,17 +390,38 @@
Force Redeploy
</button>
<button
on:click={stopApplication}
type="submit"
disabled={!$isDeploymentEnabled}
class="btn btn-sm gap-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6 text-error"
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" />
<rect x="6" y="5" width="4" height="14" rx="1" />
<rect x="14" y="5" width="4" height="14" rx="1" />
</svg> Stop
</button>
{:else if $isDeploymentEnabled && !$page.url.pathname.startsWith(`/applications/${id}/configuration/`)}
{#if $status.application.overallStatus === 'degraded'}
<button
on:click={stopApplication}
type="submit"
disabled={!$isDeploymentEnabled}
class="btn btn-sm btn-error gap-2"
class="btn btn-sm gap-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6 "
class="w-6 h-6 text-error"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
@@ -413,14 +437,13 @@
{/if}
<button
class="btn btn-sm gap-2"
class:btn-primary={$status.application.overallStatus !== 'degraded'}
disabled={!$isDeploymentEnabled}
on:click={() => handleDeploySubmit(false)}
>
{#if $status.application.overallStatus !== 'degraded'}
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6"
class="w-6 h-6 text-pink-500"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
@@ -457,7 +480,7 @@
</button>
{/if}
{#if $location && $status.application.overallStatus === 'healthy'}
<a href={$location} target="_blank" class="btn btn-sm gap-2 text-sm bg-primary"
<a href={$location} target="_blank noreferrer" class="btn btn-sm gap-2 text-sm bg-primary"
><svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
@@ -478,7 +501,7 @@
</div>
</div>
<div
class="mx-auto max-w-screen-2xl px-0 lg:px-2 grid grid-cols-1"
class="mx-auto max-w-screen-2xl px-0 lg:px-10 grid grid-cols-1"
class:lg:grid-cols-4={!$page.url.pathname.startsWith(`/applications/${id}/configuration/`)}
>
{#if !$page.url.pathname.startsWith(`/applications/${id}/configuration/`)}

View File

@@ -92,27 +92,9 @@
label: branch.name
}));
}
async function isBranchAlreadyUsed(event: any) {
async function selectBranch(event: any) {
selected.branch = event.detail.value;
try {
// const data = await get(
// `/applications/${id}/configuration/repository?repository=${selected.repository}&branch=${selected.branch}`
// );
// if (data.used) {
// const sure = confirm($t('application.configuration.branch_already_in_use'));
// if (sure) {
// selected.autodeploy = false;
// showSave = true;
// return true;
// }
// showSave = false;
// return true;
// }
showSave = true;
} catch (error) {
showSave = false;
return errorNotification(error);
}
showSave = true;
}
onMount(async () => {
@@ -178,7 +160,7 @@
isWaiting={loading.branches}
showIndicator={selected.repository && !loading.branches}
id="branches"
on:select={isBranchAlreadyUsed}
on:select={selectBranch}
items={branchSelectOptions}
isDisabled={loading.branches || !selected.repository}
isClearable={false}
@@ -186,10 +168,9 @@
</div></div>
<div class="pt-5 flex-col flex justify-center items-center space-y-4">
<button
class="btn btn-wide"
class="btn btn-wide btn-primary"
type="submit"
disabled={!showSave}
class:bg-applications={showSave}
>{$t('forms.save')}</button
>
</div>

View File

@@ -195,27 +195,11 @@
}
}
async function isBranchAlreadyUsed(event) {
async function selectBranch(event: any) {
selected.branch = event.detail;
try {
// const data = await get(
// `/applications/${id}/configuration/repository?repository=${selected.project.path_with_namespace}&branch=${selected.branch.name}`
// );
// if (data.used) {
// const sure = confirm($t('application.configuration.branch_already_in_use'));
// if (sure) {
// autodeploy = false;
// showSave = true;
// return true;
// }
// showSave = false;
// return true;
// }
showSave = true;
} catch (error) {
return errorNotification(error);
}
showSave = true;
}
async function checkSSHKey(sshkeyUrl: any) {
try {
return await post(sshkeyUrl, {});
@@ -394,7 +378,7 @@
showIndicator={!loading.branches}
isWaiting={loading.branches}
isDisabled={loading.branches || !selected.project}
on:select={isBranchAlreadyUsed}
on:select={selectBranch}
on:clear={() => {
showSave = false;
selected.branch = null;
@@ -414,7 +398,6 @@
class="btn btn-wide"
type="submit"
disabled={!showSave || loading.save}
class:bg-applications={showSave && !loading.save}
>{loading.save ? $t('forms.saving') : $t('forms.save')}</button
>
{#if tryAgain}
@@ -423,7 +406,7 @@
configuration <a href={`/sources/${application.gitSource.id}`}>here.</a>
</div>
<button
class="btn btn-sm w-40 bg-green-600"
class="btn btn-sm w-40 btn-primary"
on:click|stopPropagation|preventDefault={() => window.location.reload()}
>
Try again

View File

@@ -39,6 +39,9 @@
if (branch[0] === 'tree' && branch[1]) {
branchName = branch[1];
}
if (branch.length === 1) {
branchName = branch[0]
}
}
if (host === 'gitlab.com') {
host = 'gitlab.com/api/v4';
@@ -46,6 +49,9 @@
if (branch[1] === 'tree' && branch[2]) {
branchName = branch[2];
}
if (branch.length === 1) {
branchName = branch[0]
}
}
const apiUrl = `${protocol}://${host}`;
if (type === 'github') {
@@ -165,7 +171,7 @@
placeholder="eg: https://github.com/coollabsio/nodejs-example/tree/main"
bind:value={publicRepositoryLink}
/>
<button class="btn bg-orange-600" type="submit">
<button class="btn btn-primary" disabled={loading.branches} type="submit" class:loading={loading.branches}>
Load Repository
</button>
</div>

View File

@@ -70,7 +70,11 @@
{$t('application.configuration.no_configurable_destination')}
</div>
<div class="flex justify-center">
<a href="/destinations/new" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500">
<a
href={`/destinations/new?from=/applications/${id}/configuration/destination`}
sveltekit:prefetch
class="add-icon bg-sky-600 hover:bg-sky-500"
>
<svg
class="w-6"
xmlns="http://www.w3.org/2000/svg"
@@ -88,31 +92,111 @@
</div>
</div>
{:else}
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row mx-auto">
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row mx-auto gap-4">
{#each ownDestinations as destination}
<div class="p-2">
<form on:submit|preventDefault={() => handleSubmit(destination.id)}>
<button type="submit" class="box-selection hover:bg-sky-700 font-bold">
<div class="font-bold text-xl text-center truncate">{destination.name}</div>
<div class="text-center truncate">{destination.network}</div>
</button>
</form>
</div>
<button
on:click={() => handleSubmit(destination.id)}
class="box-selection hover:bg-primary font-bold relative"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="absolute top-0 left-0 -m-4 h-12 w-12 text-sky-500"
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="M22 12.54c-1.804 -.345 -2.701 -1.08 -3.523 -2.94c-.487 .696 -1.102 1.568 -.92 2.4c.028 .238 -.32 1.002 -.557 1h-14c0 5.208 3.164 7 6.196 7c4.124 .022 7.828 -1.376 9.854 -5c1.146 -.101 2.296 -1.505 2.95 -2.46z"
/>
<path d="M5 10h3v3h-3z" />
<path d="M8 10h3v3h-3z" />
<path d="M11 10h3v3h-3z" />
<path d="M8 7h3v3h-3z" />
<path d="M11 7h3v3h-3z" />
<path d="M11 4h3v3h-3z" />
<path d="M4.571 18c1.5 0 2.047 -.074 2.958 -.78" />
<line x1="10" y1="16" x2="10" y2="16.01" />
</svg>
{#if destination.remoteEngine}
<svg
xmlns="http://www.w3.org/2000/svg"
class="absolute top-0 left-9 -m-2 h-6 w-6 text-sky-500 rotate-45"
viewBox="0 0 24 24"
stroke-width="3"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="12" y1="18" x2="12.01" y2="18" />
<path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
<path d="M6.343 12.343a8 8 0 0 1 11.314 0" />
<path d="M3.515 9.515c4.686 -4.687 12.284 -4.687 17 0" />
</svg>
{/if}
<div class="font-bold text-xl text-center truncate">{destination.name}</div>
<div class="text-center truncate">{destination.network}</div>
</button>
{/each}
</div>
{#if otherDestinations.length > 0 && $appSession.teamId === '0'}
<div class="px-6 pb-5 pt-10 title">Other Destinations</div>
{/if}
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row mx-auto">
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row mx-auto gap-4">
{#each otherDestinations as destination}
<div class="p-2">
<form on:submit|preventDefault={() => handleSubmit(destination.id)}>
<button type="submit" class="box-selection hover:bg-sky-700 font-bold">
<div class="font-bold text-xl text-center truncate">{destination.name}</div>
<div class="text-center truncate">{destination.network}</div>
</button>
</form>
</div>
<button
class="box-selection hover:bg-sky-700 font-bold relative"
on:click={() => handleSubmit(destination.id)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="absolute top-0 left-0 -m-4 h-12 w-12 text-sky-500"
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="M22 12.54c-1.804 -.345 -2.701 -1.08 -3.523 -2.94c-.487 .696 -1.102 1.568 -.92 2.4c.028 .238 -.32 1.002 -.557 1h-14c0 5.208 3.164 7 6.196 7c4.124 .022 7.828 -1.376 9.854 -5c1.146 -.101 2.296 -1.505 2.95 -2.46z"
/>
<path d="M5 10h3v3h-3z" />
<path d="M8 10h3v3h-3z" />
<path d="M11 10h3v3h-3z" />
<path d="M8 7h3v3h-3z" />
<path d="M11 7h3v3h-3z" />
<path d="M11 4h3v3h-3z" />
<path d="M4.571 18c1.5 0 2.047 -.074 2.958 -.78" />
<line x1="10" y1="16" x2="10" y2="16.01" />
</svg>
{#if destination.remoteEngine}
<svg
xmlns="http://www.w3.org/2000/svg"
class="absolute top-0 left-9 -m-2 h-6 w-6 text-sky-500 rotate-45"
viewBox="0 0 24 24"
stroke-width="3"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="12" y1="18" x2="12.01" y2="18" />
<path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
<path d="M6.343 12.343a8 8 0 0 1 11.314 0" />
<path d="M3.515 9.515c4.686 -4.687 12.284 -4.687 17 0" />
</svg>
{/if}
<div class="font-bold text-xl text-center truncate">{destination.name}</div>
<div class="text-center truncate">{destination.network}</div>
</button>
{/each}
</div>
{/if}

View File

@@ -0,0 +1,118 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ fetch, params, url, stuff }) => {
try {
const { application } = stuff;
if (application?.destinationDockerId && !url.searchParams.get('from')) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
const response = await get(`/settings`);
return {
props: {
...response
}
};
} catch (error) {
return {
status: 500,
error: new Error(`Could not load ${url}`)
};
}
};
</script>
<script lang="ts">
export let registries: any;
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { get, post } from '$lib/api';
import { errorNotification } from '$lib/common';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
async function handleSubmit(registryId: any) {
try {
await post(`/applications/${id}/configuration/registry`, { registryId });
return await goto(from || `/applications/${id}`);
} catch (error) {
return errorNotification(error);
}
}
</script>
<div class="flex flex-col justify-center w-full">
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row mx-auto gap-4">
{#each registries.public as registry}
<button
on:click={() => handleSubmit(registry.id)}
class="box-selection hover:bg-primary relative"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="absolute top-0 left-0 -m-4 h-12 w-12 text-sky-500"
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="M22 12.54c-1.804 -.345 -2.701 -1.08 -3.523 -2.94c-.487 .696 -1.102 1.568 -.92 2.4c.028 .238 -.32 1.002 -.557 1h-14c0 5.208 3.164 7 6.196 7c4.124 .022 7.828 -1.376 9.854 -5c1.146 -.101 2.296 -1.505 2.95 -2.46z"
/>
<path d="M5 10h3v3h-3z" />
<path d="M8 10h3v3h-3z" />
<path d="M11 10h3v3h-3z" />
<path d="M8 7h3v3h-3z" />
<path d="M11 7h3v3h-3z" />
<path d="M11 4h3v3h-3z" />
<path d="M4.571 18c1.5 0 2.047 -.074 2.958 -.78" />
<line x1="10" y1="16" x2="10" y2="16.01" />
</svg>
<div class="font-bold text-xl text-center truncate">{registry.name}</div>
<div class="text-center truncate">{registry.url}</div>
<div>public</div>
</button>
{/each}
{#each registries.private as registry}
<button
on:click={() => handleSubmit(registry.id)}
class="box-selection hover:bg-primary relative"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="absolute top-0 left-0 -m-4 h-12 w-12 text-sky-500"
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="M22 12.54c-1.804 -.345 -2.701 -1.08 -3.523 -2.94c-.487 .696 -1.102 1.568 -.92 2.4c.028 .238 -.32 1.002 -.557 1h-14c0 5.208 3.164 7 6.196 7c4.124 .022 7.828 -1.376 9.854 -5c1.146 -.101 2.296 -1.505 2.95 -2.46z"
/>
<path d="M5 10h3v3h-3z" />
<path d="M8 10h3v3h-3z" />
<path d="M11 10h3v3h-3z" />
<path d="M8 7h3v3h-3z" />
<path d="M11 7h3v3h-3z" />
<path d="M11 4h3v3h-3z" />
<path d="M4.571 18c1.5 0 2.047 -.074 2.958 -.78" />
<line x1="10" y1="16" x2="10" y2="16.01" />
</svg>
<div class="font-bold text-xl text-center truncate">{registry.name}</div>
<div class="text-center truncate">{registry.url}</div>
<div>private</div>
</button>
{/each}
</div>
</div>

View File

@@ -68,7 +68,9 @@
</script>
<div class="max-w-screen-2xl mx-auto px-9">
<div class="title pb-8">Git App</div>
{#if !filteredSources}
<div class="title pb-8">Git App</div>
{/if}
<div class="flex flex-wrap justify-center">
{#if !filteredSources}
<div class="flex-col">
@@ -76,10 +78,7 @@
{$t('application.configuration.no_configurable_git')}
</div>
<div class="flex justify-center">
<a
href="/sources/new?from={$page.url.pathname}"
class="add-icon bg-orange-600 hover:bg-orange-500"
>
<a href="/sources/new?from={$page.url.pathname}" class="add-icon">
<svg
class="w-6"
xmlns="http://www.w3.org/2000/svg"
@@ -141,7 +140,7 @@
<button
disabled={source.gitlabApp && !source.gitlabAppId}
type="submit"
class="disabled:opacity-95 bg-coolgray-200 disabled:text-white box-selection hover:bg-orange-700 group w-full lg:w-96"
class="disabled:opacity-95 disabled:text-white box-selection hover:btn-primary group w-full lg:w-96"
class:border-red-500={source.gitlabApp && !source.gitlabAppId}
class:border-0={source.gitlabApp && !source.gitlabAppId}
class:border-l-4={source.gitlabApp && !source.gitlabAppId}
@@ -244,7 +243,7 @@
{/if}
</div>
<div class="flex flex-row items-center">
<div class="title py-4">Public Repository</div>
<div class="title py-4 pr-4">Public Repository</div>
<DocLink url="https://docs.coollabs.io/coolify/applications/#public-repository" />
</div>
<PublicRepository />

View File

@@ -61,7 +61,7 @@
disabled={!$appSession.isAdmin}
class:bg-red-600={$appSession.isAdmin}
class:hover:bg-red-500={$appSession.isAdmin}
class="btn btn-sm btn-error text-sm"
class="btn btn-lg btn-error hover:bg-red-700 text-sm w-64"
>
Force Delete Application
</button>
@@ -71,7 +71,7 @@
on:click={() => deleteApplication(application.name, false)}
type="submit"
disabled={!$appSession.isAdmin}
class="btn btn-lg btn-error hover:bg-red-700 text-sm"
class="btn btn-lg btn-error hover:bg-red-700 text-sm w-64"
>
Delete Application
</button>

View File

@@ -44,7 +44,6 @@
const { id } = $page.params;
let debug = application.settings.debug;
let previews = application.settings.previews;
let dualCerts = application.settings.dualCerts;
let autodeploy = application.settings.autodeploy;
@@ -52,9 +51,6 @@
let isDBBranching = application.settings.isDBBranching;
async function changeSettings(name: any) {
if (name === 'debug') {
debug = !debug;
}
if (name === 'previews') {
previews = !previews;
}
@@ -77,7 +73,6 @@
try {
await post(`/applications/${id}/settings`, {
previews,
debug,
dualCerts,
isBot,
autodeploy,
@@ -90,9 +85,6 @@
type: 'success'
});
} catch (error) {
if (name === 'debug') {
debug = !debug;
}
if (name === 'previews') {
previews = !previews;
}
@@ -132,29 +124,21 @@
description={$t('application.enable_auto_deploy_webhooks')}
/>
</div>
{#if !application.settings.isBot}
<div class="grid grid-cols-2 items-center">
<Setting
id="previews"
isCenter={false}
bind:setting={previews}
on:click={() => changeSettings('previews')}
title={$t('application.enable_mr_pr_previews')}
description={$t('application.enable_preview_deploy_mr_pr_requests')}
/>
</div>
{/if}
{:else}
No features available for this application
{/if}
{#if !application.settings.isBot && !application.settings.isPublicRepository}
<div class="grid grid-cols-2 items-center">
<Setting
id="previews"
isCenter={false}
bind:setting={previews}
on:click={() => changeSettings('previews')}
title={$t('application.enable_mr_pr_previews')}
description={$t('application.enable_preview_deploy_mr_pr_requests')}
/>
</div>
{/if}
<div class="grid grid-cols-2 items-center w-full">
<Setting
id="debug"
isCenter={false}
bind:setting={debug}
on:click={() => changeSettings('debug')}
title={$t('application.debug_logs')}
description={$t('application.enable_debug_log_during_build')}
/>
</div>
</div>
</div>
</div>

View File

@@ -61,26 +61,29 @@
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
let statues: any = {};
let loading = false;
let loading = {
save: false,
reloadCompose: false
};
let fqdnEl: any = null;
let forceSave = false;
let isPublicRepository = application.settings.isPublicRepository;
let apiUrl = application.gitSource.apiUrl;
let isPublicRepository = application.settings?.isPublicRepository;
let apiUrl = application.gitSource?.apiUrl;
let branch = application.branch;
let repository = application.repository;
let debug = application.settings.debug;
let previews = application.settings.previews;
let dualCerts = application.settings.dualCerts;
let isCustomSSL = application.settings.isCustomSSL;
let autodeploy = application.settings.autodeploy;
let isBot = application.settings.isBot;
let isDBBranching = application.settings.isDBBranching;
let htmlUrl = application.gitSource.htmlUrl;
let debug = application.settings?.debug;
let previews = application.settings?.previews;
let dualCerts = application.settings?.dualCerts;
let isCustomSSL = application.settings?.isCustomSSL;
let autodeploy = application.settings?.autodeploy;
let isBot = application.settings?.isBot;
let isDBBranching = application.settings?.isDBBranching;
let htmlUrl = application.gitSource?.htmlUrl;
let dockerComposeFile = JSON.parse(application.dockerComposeFile) || null;
let dockerComposeServices: any[] = [];
let dockerComposeFileLocation = application.dockerComposeFileLocation;
let dockerComposeConfiguration = JSON.parse(application.dockerComposeConfiguration) || {};
let originalDockerComposeFileLocation = application.dockerComposeFileLocation;
let baseDatabaseBranch: any = application?.connectedDatabase?.hostedDatabaseDBName || null;
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
@@ -102,7 +105,6 @@
label: 'Uvicorn'
}
];
function normalizeDockerServices(services: any[]) {
const tempdockerComposeServices = [];
for (const [name, data] of Object.entries(services)) {
@@ -237,12 +239,16 @@
}
}
async function handleSubmit(toast: boolean = true) {
if (loading) return;
if (toast) loading = true;
if (loading.save) return;
if (toast) loading.save = true;
try {
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
if (application.deploymentType)
if (application.deploymentType) {
application.deploymentType = application.deploymentType.toLowerCase();
}
if (originalDockerComposeFileLocation !== application.dockerComposeFileLocation) {
await reloadCompose();
}
if (!isBot) {
await post(`/applications/${id}/check`, {
fqdn: application.fqdn,
@@ -299,7 +305,7 @@
}
return errorNotification(error);
} finally {
loading = false;
loading.save = false;
}
}
async function selectWSGI(event: any) {
@@ -361,6 +367,11 @@
});
}
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 headers = isPublicRepository
@@ -369,9 +380,10 @@
Authorization: `token ${$appSession.tokens.github}`
};
const data = await get(
`${apiUrl}/repos/${repository}/contents/${dockerComposeFileLocation}?ref=${branch}`,
`${apiUrl}/repos/${repository}/contents/${composeLocation}?ref=${branch}`,
{
...headers,
'If-None-Match': '',
Accept: 'application/vnd.github.v2.json'
}
);
@@ -401,7 +413,7 @@
});
const dockerComposeFileYml = files.find(
(file: { name: string; type: string }) =>
file.name === dockerComposeFileLocation && file.type === 'blob'
file.name === composeLocation && file.type === 'blob'
);
const id = dockerComposeFileYml.id;
@@ -420,13 +432,20 @@
await handleSubmit(false);
}
}
originalDockerComposeFileLocation = application.dockerComposeFileLocation;
addToast({
message: 'Compose file reloaded.',
type: 'success'
});
} catch (error) {
} catch (error: any) {
if (error.message === 'Not Found') {
error.message = `Can't find ${application.dockerComposeFileLocation} file.`;
errorNotification(error);
throw error;
}
errorNotification(error);
} finally {
loading.reloadCompose = false;
}
}
$: if ($status.application.statuses) {
@@ -459,15 +478,15 @@
<form on:submit|preventDefault={() => handleSubmit()}>
<div class="mx-auto w-full">
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
<div class="title font-bold pb-3 ">General</div>
<div class="title font-bold pb-3">General</div>
{#if $appSession.isAdmin}
<button
class="btn btn-sm btn-primary"
type="submit"
class:loading
class:loading={loading.save}
class:bg-orange-600={forceSave}
class:hover:bg-orange-400={forceSave}
disabled={loading}>{$t('forms.save')}</button
disabled={loading.save}>{$t('forms.save')}</button
>
{/if}
</div>
@@ -482,14 +501,14 @@
<input
disabled={isDisabled || application.settings.isPublicRepository}
class="w-full"
value={application.gitSource.name}
value={application.gitSource?.name}
/>
{:else}
<a
href={`/applications/${id}/configuration/source?from=/applications/${id}`}
class="no-underline"
><input
value={application.gitSource.name}
value={application.gitSource?.name}
id="gitSource"
class="cursor-pointer hover:bg-coolgray-500 w-full"
/></a
@@ -533,6 +552,27 @@
>
{/if}
</div>
<div class="grid grid-cols-2 items-center">
<label for="registry">Docker Registry</label>
{#if isDisabled}
<input
class="capitalize w-full"
disabled={isDisabled}
value={application.dockerRegistry.name}
/>
{:else}
<a
href={`/applications/${id}/configuration/registry?from=/applications/${id}`}
class="no-underline"
>
<input
value={application.dockerRegistry.name}
id="registry"
class="cursor-pointer hover:bg-coolgray-500 capitalize w-full"
/></a
>
{/if}
</div>
<div class="grid grid-cols-2 items-center">
<label for="buildPack">{$t('application.build_pack')} </label>
{#if isDisabled}
@@ -586,13 +626,13 @@
<input
bind:this={fqdnEl}
class="w-full"
required={!application.settings.isBot}
required={!application.settings?.isBot}
readonly={isDisabled}
disabled={isDisabled}
name="fqdn"
id="fqdn"
class:border={!application.settings.isBot && !application.fqdn}
class:border-red-500={!application.settings.isBot && !application.fqdn}
class:border={!application.settings?.isBot && !application.fqdn}
class:border-red-500={!application.settings?.isBot && !application.fqdn}
bind:value={application.fqdn}
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
placeholder="eg: https://coollabs.io"
@@ -661,7 +701,7 @@
</div>
{#if application.buildPack !== 'compose'}
<div class="title font-bold pb-3 pt-10 border-b border-coolgray-500 mb-6">
Build & Deploy
Configuration
</div>
<div class="grid grid-flow-row gap-2 px-4 pr-5">
{#if application.buildCommand || application.buildPack === 'rust' || application.buildPack === 'laravel'}
@@ -731,7 +771,7 @@
</div>
{/if}
{#if $features.beta}
{#if !application.settings.isBot && !application.settings.isPublicRepository}
{#if !application.settings?.isBot && !application.settings?.isPublicRepository}
<div class="grid grid-cols-2 items-center">
<Setting
id="isDBBranching"
@@ -855,8 +895,8 @@
>
<input
class="w-full"
readonly={!isDisabled}
disabled={isDisabled}
readonly={!$appSession.isAdmin}
name="exposePort"
id="exposePort"
bind:value={application.exposePort}
@@ -1010,12 +1050,34 @@
<div class="title font-bold pb-3 pt-10 border-b border-coolgray-500 mb-6">
Stack <Beta />
{#if $appSession.isAdmin}
<button class="btn btn-sm btn-primary" on:click|preventDefault={reloadCompose}
>Reload Docker Compose File</button
<button
class="btn btn-sm btn-primary"
class:loading={loading.reloadCompose}
disabled={loading.reloadCompose}
on:click|preventDefault={reloadCompose}>Reload Docker Compose File</button
>
{/if}
</div>
<div class="grid grid-flow-row gap-2">
<div class="grid grid-cols-2 items-center px-8 pb-4">
<label for="dockerComposeFileLocation"
>Docker Compose File Location
<Explainer
explanation="You can specify a custom docker compose file location. <br> Should be absolute path, like <span class='text-settings font-bold'>/data/docker-compose.yml</span> or <span class='text-settings font-bold'>/docker-compose.yml.</span>"
/>
</label>
<div>
<input
class="w-full"
disabled={isDisabled}
readonly={!$appSession.isAdmin}
name="dockerComposeFileLocation"
id="dockerComposeFileLocation"
bind:value={application.dockerComposeFileLocation}
placeholder="eg: /docker-compose.yml"
/>
</div>
</div>
{#each dockerComposeServices as service}
<div
class="grid items-center bg-coolgray-100 rounded border border-coolgray-300 p-2 px-4"

View File

@@ -61,12 +61,27 @@
fromDb = from;
streamInterval = setInterval(async () => {
const nextSequence = logs[logs.length - 1]?.time || 0;
if (status !== 'running' && status !== 'queued') {
loading = false;
try {
const data = await get(
`/applications/${id}/logs/build/${$selectedBuildId}?sequence=${nextSequence}`
);
status = data.status;
currentStatus = status;
fromDb = data.fromDb;
logs = logs.concat(
data.logs.map((log: any) => ({ ...log, line: cleanAnsiCodes(log.line) }))
);
loading = false;
} catch (error) {
return errorNotification(error);
}
clearInterval(streamInterval);
return;
}
const nextSequence = logs[logs.length - 1]?.time || 0;
try {
const data = await get(
`/applications/${id}/logs/build/${$selectedBuildId}?sequence=${nextSequence}`

View File

@@ -32,7 +32,7 @@
import { day } from '$lib/dayjs';
import { onDestroy, onMount } from 'svelte';
const { id } = $page.params;
let debug = application.settings.debug;
let loadBuildLogsInterval: any = null;
let skip = 0;
@@ -104,42 +104,74 @@
return 'text-white';
}
}
async function changeSettings(name: any) {
if (name === 'debug') {
debug = !debug;
}
try {
await post(`/applications/${id}/settings`, {
debug,
branch: application.branch,
projectId: application.projectId
});
return addToast({
message: $t('application.settings_saved'),
type: 'success'
});
} catch (error) {
if (name === 'debug') {
debug = !debug;
}
return errorNotification(error);
}
}
</script>
<div class="mx-auto w-full">
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
<div class="mx-auto w-full lg:px-0 px-1">
<div class="flex lg:flex-row flex-col border-b border-coolgray-500 mb-6 space-x-2">
<div class="flex flex-row">
<div class="title font-bold pb-3 pr-3">Build Logs</div>
<button class="btn btn-sm bg-error" on:click={resetQueue}>Reset Build Queue</button>
</div>
<div class=" flex-1" />
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text text-white pr-4 font-bold">Enable Debug Logs</span>
<input
type="checkbox"
checked={debug}
class="checkbox checkbox-success"
on:click={() => changeSettings('debug')}
/>
</label>
</div>
</div>
</div>
<div class="block flex-col justify-start space-x-5 flex flex-col-reverse lg:flex-row">
<div class="justify-start space-x-5 flex flex-col-reverse lg:flex-row">
<div class="flex-1 md:w-96">
{#if $selectedBuildId}
{#key $selectedBuildId}
<svelte:component this={BuildLog} />
{/key}
{:else if buildCount === 0}
Not build logs found.
{:else}
{#if buildCount === 0}
Not build logs found.
{:else}
Select a build to see the logs.
{/if}
{/if}
</div>
<div class="mb-4 min-w-[16rem] space-y-2 md:mb-0 ">
<div class="top-4 md:sticky">
<div class="flex space-x-2 pb-2">
<button
disabled={noMoreBuilds}
class:btn-primary={!noMoreBuilds}
class=" btn btn-sm w-full"
on:click={loadMoreBuilds}>{$t('application.build.load_more')}</button
>
</div>
<div class="flex space-x-2 pb-2">
<button
disabled={noMoreBuilds}
class:btn-primary={!noMoreBuilds}
class=" btn btn-sm w-full"
on:click={loadMoreBuilds}>{$t('application.build.load_more')}</button
>
</div>
{#each builds as build, index (build.id)}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
id={`building-${build.id}`}
on:click={() => loadBuild(build.id)}
@@ -187,4 +219,4 @@
{/each}
</div>
</div>
</div>
</div>

View File

@@ -1,10 +1,8 @@
<script lang="ts">
import { page } from '$app/stores';
import { get } from '$lib/api';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { onMount, onDestroy } from 'svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
let application: any = {};
let logsLoading = false;
@@ -137,12 +135,7 @@
{:else}
<div class="relative w-full">
<div class="flex justify-start sticky space-x-2 pb-2">
<button
on:click={followBuild}
class="btn btn-sm bg-coollabs"
class:bg-coolgray-300={followingLogs}
class:text-applications={followingLogs}
>
<button on:click={followBuild} class="btn btn-sm " class:bg-coollabs={followingLogs}>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6 mr-2"
@@ -162,8 +155,9 @@
{followingLogs ? 'Following Logs...' : 'Follow Logs'}
</button>
{#if loadLogsInterval}
<button id="streaming" class="btn btn-sm bg-transparent border-none loading" />
<Tooltip triggeredBy="#streaming">Streaming logs</Tooltip>
<button id="streaming" class="btn btn-sm bg-transparent border-none loading"
>Streaming logs</button
>
{/if}
</div>
<div

View File

@@ -216,7 +216,7 @@
<div class="flex justify-end items-end space-x-2 h-10">
{#if preview.customDomain}
<a id="openpreview" href={preview.customDomain} target="_blank" class="icons">
<a id="openpreview" href={preview.customDomain} target="_blank noreferrer" class="icons">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"

View File

@@ -48,10 +48,10 @@
.map((secret) => {
const [name, ...rest] = secret.split('=');
const value = rest.join('=');
const cleanValue = value?.replaceAll('"', '') || '';
const cleanValue = (value?.replaceAll('"', '') || '').trim();
return {
name,
value: cleanValue,
name: name.trim(),
value: cleanValue.trim(),
createSecret: !secrets.find((secret: any) => name === secret.name)
};
});
@@ -60,6 +60,7 @@
batchSecretsPairs.map(({ name, value, createSecret }) =>
limit(async () => {
try {
if (!name || !value) return;
if (createSecret) {
await post(`/applications/${id}/secrets`, {
name,
@@ -87,10 +88,6 @@
);
batchSecrets = '';
await refreshSecrets();
addToast({
message: 'Secrets saved.',
type: 'success'
});
}
</script>

View File

@@ -2,9 +2,11 @@
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ params, stuff, url }) => {
try {
const { application } = stuff;
const response = await get(`/applications/${params.id}/storages`);
return {
props: {
application,
...response
}
};
@@ -19,12 +21,31 @@
<script lang="ts">
export let persistentStorages: any;
export let application: any;
import { page } from '$app/stores';
import Storage from './_Storage.svelte';
import { get } from '$lib/api';
import { t } from '$lib/translations';
import Explainer from '$lib/components/Explainer.svelte';
let composeJson = JSON.parse(application?.dockerComposeFile || '{}');
let predefinedVolumes: any[] = [];
if (composeJson?.services) {
for (const [_, service] of Object.entries(composeJson.services)) {
if (service?.volumes) {
for (const [_, volumeName] of Object.entries(service.volumes)) {
let [volume, target] = volumeName.split(':');
if (!target) {
target = volume;
volume = `${application.id}${volume.replace(/\//gi, '-').replace(/\./gi, '')}`;
} else {
volume = `${application.id}${volume.replace(/\//gi, '-').replace(/\./gi, '')}`;
}
predefinedVolumes.push({ id: volume, path: target, predefined: true });
}
}
}
}
const { id } = $page.params;
async function refreshStorage() {
const data = await get(`/applications/${id}/storages`);
@@ -34,20 +55,40 @@
<div class="w-full">
<div class="mx-auto w-full">
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
<div class="title font-bold pb-3">
Persistent Volumes <Explainer
position="dropdown-bottom"
explanation={$t('application.storage.persistent_storage_explainer')}
/>
</div>
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
<div class="title font-bold pb-3">Persistent Volumes</div>
</div>
<label for="name" class="pb-2 uppercase font-bold">name</label>
{#if predefinedVolumes.length > 0}
<div class="title">Predefined Volumes</div>
<div class="w-full lg:px-0 px-4">
<div class="grid grid-col-1 lg:grid-cols-2 py-2 gap-2">
<div class="font-bold uppercase">Volume Id</div>
<div class="font-bold uppercase">Mount Dir</div>
</div>
</div>
<div class="gap-4">
{#each predefinedVolumes as storage}
{#key storage.id}
<Storage on:refresh={refreshStorage} {storage} />
{/key}
{/each}
</div>
{/if}
{#if persistentStorages.length > 0}
<div class="title" class:pt-10={predefinedVolumes.length > 0}>Custom Volumes</div>
{/if}
{#each persistentStorages as storage}
{#key storage.id}
<Storage on:refresh={refreshStorage} {storage} />
{/key}
{/each}
<div class="title pt-10">
Add New Volume <Explainer
position="dropdown-bottom"
explanation={$t('application.storage.persistent_storage_explainer')}
/>
</div>
<Storage on:refresh={refreshStorage} isNew />
</div>
</div>