Merge remote-tracking branch 'upstream/next' into feature/glitchtip-service

# Conflicts:
#	README.md
#	apps/api/src/routes/api/v1/services/handlers.ts
#	apps/ui/src/lib/components/svg/services/index.ts
This commit is contained in:
Guillaume Bonnet
2022-08-16 20:21:44 +02:00
40 changed files with 681 additions and 1073 deletions

View File

@@ -16,7 +16,7 @@
export const load: Load = async ({ fetch, url, params }) => {
try {
const response = await get(`/applications/${params.id}`);
let { application, appId, settings, isQueueActive } = response;
let { application, appId, settings } = response;
if (!application || Object.entries(application).length === 0) {
return {
status: 302,
@@ -36,7 +36,8 @@
return {
props: {
application
application,
settings
},
stuff: {
application,
@@ -52,7 +53,7 @@
<script lang="ts">
export let application: any;
export let settings: any;
import { page } from '$app/stores';
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
import { del, get, post } from '$lib/api';
@@ -65,10 +66,10 @@
let loading = false;
let statusInterval: any;
let isQueueActive= false;
let isQueueActive = false;
$disabledButton =
!$appSession.isAdmin ||
!application.fqdn ||
(!application.fqdn && !application.settings.isBot) ||
!application.gitSource ||
!application.repository ||
!application.destinationDocker ||
@@ -80,9 +81,9 @@
try {
const { buildId } = await post(`/applications/${id}/deploy`, { ...application });
addToast({
message: $t('application.deployment_queued'),
type: 'success'
});
message: $t('application.deployment_queued'),
type: 'success'
});
if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
} else {
@@ -114,7 +115,7 @@
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
}
async function getStatus() {
if ($status.application.loading) return;
@@ -126,18 +127,23 @@
$status.application.loading = false;
$status.application.initialLoading = false;
}
onDestroy(() => {
$status.application.initialLoading = true;
$location = null;
clearInterval(statusInterval);
});
onMount(async () => {
setLocation(application);
setLocation(application, settings);
$status.application.isRunning = false;
$status.application.isExited = false;
$status.application.loading = false;
if (application.gitSourceId && application.destinationDockerId && application.fqdn) {
if (
application.gitSourceId &&
application.destinationDockerId &&
application.fqdn &&
!application.settings.isBot
) {
await getStatus();
statusInterval = setInterval(async () => {
await getStatus();

View File

@@ -30,7 +30,6 @@
import { page } from '$app/stores';
import { get } from '$lib/api';
import { appSession } from '$lib/store';
import { browser } from '$app/env';
import { t } from '$lib/translations';
import { buildPacks, findBuildPack, scanningTemplates } from '$lib/templates';
import { errorNotification } from '$lib/common';
@@ -263,11 +262,27 @@
</div>
</div>
{:else}
<div class="max-w-7xl mx-auto flex flex-wrap justify-center">
{#each buildPacks as buildPack}
<div class="max-w-7xl mx-auto ">
<div class="title pb-2">Coolify Buildpacks</div>
<div class="flex flex-wrap justify-center">
{#each buildPacks.filter(bp => bp.isCoolifyBuildPack === true) as buildPack}
<div class="p-2">
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
</div>
{/each}
</div>
</div>
<div class="max-w-7xl mx-auto ">
<div class="title pb-2">Heroku</div>
<div class="flex flex-wrap justify-center">
{#each buildPacks.filter(bp => bp.isHerokuBuildPack === true) as buildPack}
<div class="p-2">
<BuildPack {packageManager} {buildPack} {scanning} bind:foundConfig />
</div>
{/each}
</div>
</div>
{/if}

View File

@@ -31,6 +31,7 @@
import { t } from '$lib/translations';
import { appSession } from '$lib/store';
import { errorNotification } from '$lib/common';
import { onMount } from 'svelte';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
@@ -55,6 +56,11 @@
return errorNotification(error);
}
}
onMount(async () => {
if (destinations.length === 1) {
await handleSubmit(destinations[0].id);
}
});
</script>
<div class="flex space-x-1 p-6 font-bold">
@@ -65,7 +71,9 @@
<div class="flex flex-col justify-center">
{#if !destinations || ownDestinations.length === 0}
<div class="flex-col">
<div class="pb-2 text-center font-bold">{$t('application.configuration.no_configurable_destination')}</div>
<div class="pb-2 text-center font-bold">
{$t('application.configuration.no_configurable_destination')}
</div>
<div class="flex justify-center">
<a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500">
<svg

View File

@@ -5,7 +5,8 @@
if (stuff?.application?.id) {
return {
props: {
application: stuff.application
application: stuff.application,
settings: stuff.settings
}
};
}
@@ -26,6 +27,7 @@
<script lang="ts">
export let application: any;
export let settings: any;
import { page } from '$app/stores';
import { onDestroy, onMount } from 'svelte';
import Select from 'svelte-select';
@@ -60,6 +62,7 @@
let previews = application.settings.previews;
let dualCerts = application.settings.dualCerts;
let autodeploy = application.settings.autodeploy;
let isBot = application.settings.isBot;
let nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
let isNonWWWDomainOK = false;
@@ -99,7 +102,7 @@
application.fqdn = `http://${cuid()}.demo.coolify.io`;
await handleSubmit();
}
domainEl.focus();
// !isBot && domainEl.focus();
await getUsage();
usageInterval = setInterval(async () => {
await getUsage();
@@ -129,11 +132,16 @@
if (name === 'autodeploy') {
autodeploy = !autodeploy;
}
if (name === 'isBot') {
isBot = !isBot;
setLocation(application, settings);
}
try {
await post(`/applications/${id}/settings`, {
previews,
debug,
dualCerts,
isBot,
autodeploy,
branch: application.branch,
projectId: application.projectId
@@ -155,24 +163,28 @@
if (name === 'autodeploy') {
autodeploy = !autodeploy;
}
if (name === 'isBot') {
isBot = !isBot;
}
return errorNotification(error);
}
}
async function handleSubmit() {
if (loading || !application.fqdn) return;
if (loading || (!application.fqdn && !isBot)) return;
loading = true;
try {
nonWWWDomain = application.fqdn && getDomain(application.fqdn).replace(/^www\./, '');
if (application.deploymentType)
application.deploymentType = application.deploymentType.toLowerCase();
await post(`/applications/${id}/check`, {
fqdn: application.fqdn,
forceSave,
dualCerts,
exposePort: application.exposePort
});
!isBot &&
(await post(`/applications/${id}/check`, {
fqdn: application.fqdn,
forceSave,
dualCerts,
exposePort: application.exposePort
}));
await post(`/applications/${id}`, { ...application });
setLocation(application);
setLocation(application, settings);
$disabledButton = false;
forceSave = false;
addToast({
@@ -332,53 +344,56 @@
<label for="gitSource" class="text-base font-bold text-stone-100"
>{$t('application.git_source')}</label
>
<a
href={!isDisabled
? `/applications/${id}/configuration/source?from=/applications/${id}`
: ''}
class="no-underline"
><input
value={application.gitSource.name}
id="gitSource"
disabled
class="cursor-pointer hover:bg-coolgray-500"
/></a
>
{#if isDisabled}
<input disabled={isDisabled} value={application.gitSource.name} />
{:else}
<a
href={`/applications/${id}/configuration/source?from=/applications/${id}`}
class="no-underline"
><input
value={application.gitSource.name}
id="gitSource"
class="cursor-pointer hover:bg-coolgray-500"
/></a
>
{/if}
</div>
<div class="grid grid-cols-2 items-center">
<label for="repository" class="text-base font-bold text-stone-100"
>{$t('application.git_repository')}</label
>
<a
href={!isDisabled
? `/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack`
: ''}
class="no-underline"
><input
value="{application.repository}/{application.branch}"
id="repository"
disabled
class="cursor-pointer hover:bg-coolgray-500"
/></a
>
{#if isDisabled}
<input disabled={isDisabled} value="{application.repository}/{application.branch}" />
{:else}
<a
href={`/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack`}
class="no-underline"
><input
value="{application.repository}/{application.branch}"
id="repository"
class="cursor-pointer hover:bg-coolgray-500"
/></a
>
{/if}
</div>
<div class="grid grid-cols-2 items-center">
<label for="buildPack" class="text-base font-bold text-stone-100"
>{$t('application.build_pack')}</label
>
<a
href={!isDisabled
? `/applications/${id}/configuration/buildpack?from=/applications/${id}`
: ''}
class="no-underline "
>
<input
value={application.buildPack}
id="buildPack"
disabled
class="cursor-pointer hover:bg-coolgray-500"
/></a
>
{#if isDisabled}
<input class="capitalize" disabled={isDisabled} value={application.buildPack} />
{:else}
<a
href={`/applications/${id}/configuration/buildpack?from=/applications/${id}`}
class="no-underline "
>
<input
value={application.buildPack}
id="buildPack"
class="cursor-pointer hover:bg-coolgray-500 capitalize"
/></a
>
{/if}
</div>
<div class="grid grid-cols-2 items-center pb-8">
<label for="destination" class="text-base font-bold text-stone-100"
@@ -465,77 +480,88 @@
<div class="title">{$t('application.application')}</div>
</div>
<div class="grid grid-flow-row gap-2 px-10">
<div class="grid grid-cols-2">
<div class="flex-col">
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
>{$t('application.url_fqdn')}</label
>
{#if browser && window.location.hostname === 'demo.coolify.io'}
<Explainer
text="<span class='text-white font-bold'>You can use the predefined random url name or enter your own domain name.</span>"
<div class="grid grid-cols-2 items-center">
<Setting
isCenter={false}
bind:setting={isBot}
on:click={() => changeSettings('isBot')}
title="Is your application a bot?"
description="You can deploy applications without domains. <br>They will listen on <span class='text-green-500 font-bold'>IP:PORT</span> instead.<br></Setting><br>Useful for <span class='text-green-500 font-bold'>example bots.</span>"
/>
</div>
{#if !isBot}
<div class="grid grid-cols-2">
<div class="flex-col">
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
>{$t('application.url_fqdn')}</label
>
{#if browser && window.location.hostname === 'demo.coolify.io'}
<Explainer
text="<span class='text-white font-bold'>You can use the predefined random url name or enter your own domain name.</span>"
/>
{/if}
<Explainer text={$t('application.https_explainer')} />
</div>
<div>
<input
readonly={isDisabled}
disabled={isDisabled}
bind:this={domainEl}
name="fqdn"
id="fqdn"
required
bind:value={application.fqdn}
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
placeholder="eg: https://coollabs.io"
/>
{/if}
<Explainer text={$t('application.https_explainer')} />
</div>
<div>
<input
readonly={isDisabled}
disabled={isDisabled}
bind:this={domainEl}
name="fqdn"
id="fqdn"
required
bind:value={application.fqdn}
pattern="^https?://([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{'{'}2,{'}'}$"
placeholder="eg: https://coollabs.io"
/>
{#if forceSave}
<div class="flex-col space-y-2 pt-4 text-center">
{#if isNonWWWDomainOK}
<button
class="bg-green-600 hover:bg-green-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
>
{:else}
<button
class="bg-red-600 hover:bg-red-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
>
{/if}
{#if dualCerts}
{#if isWWWDomainOK}
{#if forceSave}
<div class="flex-col space-y-2 pt-4 text-center">
{#if isNonWWWDomainOK}
<button
class="bg-green-600 hover:bg-green-500"
on:click|preventDefault={() =>
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
class="btn btn-sm bg-green-600 hover:bg-green-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
>
{:else}
<button
class="bg-red-600 hover:bg-red-500"
on:click|preventDefault={() =>
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
class="btn btn-sm bg-red-600 hover:bg-red-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
>
{/if}
{/if}
</div>
{/if}
{#if dualCerts}
{#if isWWWDomainOK}
<button
class="btn btn-sm bg-green-600 hover:bg-green-500"
on:click|preventDefault={() =>
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
>
{:else}
<button
class="btn btn-sm bg-red-600 hover:bg-red-500"
on:click|preventDefault={() =>
isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
>
{/if}
{/if}
</div>
{/if}
</div>
</div>
</div>
<div class="grid grid-cols-2 items-center pb-8">
<Setting
dataTooltip={$t('forms.must_be_stopped_to_modify')}
disabled={$status.application.isRunning}
isCenter={false}
bind:setting={dualCerts}
title={$t('application.ssl_www_and_non_www')}
description={$t('application.ssl_explainer')}
on:click={() => !$status.application.isRunning && changeSettings('dualCerts')}
/>
</div>
<div class="grid grid-cols-2 items-center pb-8">
<Setting
dataTooltip={$t('forms.must_be_stopped_to_modify')}
disabled={$status.application.isRunning}
isCenter={false}
bind:setting={dualCerts}
title={$t('application.ssl_www_and_non_www')}
description={$t('application.ssl_explainer')}
on:click={() => !$status.application.isRunning && changeSettings('dualCerts')}
/>
</div>
{/if}
{#if application.buildPack === 'python'}
<div class="grid grid-cols-2 items-center">
<label for="pythonModule" class="text-base font-bold text-stone-100">WSGI / ASGI</label>
@@ -585,7 +611,7 @@
</div>
{/if}
{/if}
{#if !staticDeployments.includes(application.buildPack)}
{#if !staticDeployments.includes(application.buildPack) && !isBot}
<div class="grid grid-cols-2 items-center">
<label for="port" class="text-base font-bold text-stone-100">{$t('forms.port')}</label>
<input
@@ -606,6 +632,7 @@
name="exposePort"
id="exposePort"
bind:value={application.exposePort}
required={isBot}
placeholder="12345"
/>
<Explainer
@@ -698,7 +725,7 @@
/>
</div>
{/if}
{#if application.buildPack !== 'laravel'}
{#if application.buildPack !== 'laravel' && application.buildPack !== 'heroku'}
<div class="grid grid-cols-2 items-center">
<div class="flex-col">
<label for="baseDirectory" class="pt-2 text-base font-bold text-stone-100"

View File

@@ -30,6 +30,7 @@
import { get, post } from '$lib/api';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { onMount } from 'svelte';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
@@ -45,6 +46,11 @@
return errorNotification(error);
}
}
onMount(async () => {
if (destinations.length === 1) {
await handleSubmit(destinations[0].id);
}
});
</script>
<div class="flex space-x-1 p-6 font-bold">
@@ -55,7 +61,9 @@
<div class="flex justify-center">
{#if !destinations || destinations.length === 0}
<div class="flex-col">
<div class="pb-2 text-center font-bold">{$t('application.configuration.no_configurable_destination')}</div>
<div class="pb-2 text-center font-bold">
{$t('application.configuration.no_configurable_destination')}
</div>
<div class="flex justify-center">
<a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500">
<svg

View File

@@ -39,7 +39,7 @@
export let services: any;
let numberOfGetStatus = 0;
function getRndInteger(min, max) {
function getRndInteger(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1) ) + min;
}

View File

@@ -55,7 +55,7 @@
<a href="https://fider.io" target="_blank">
<Icons.Fider />
</a>
{:else if service.type === 'appwrote'}
{:else if service.type === 'appwrite'}
<a href="https://appwrite.io" target="_blank">
<Icons.Appwrite/>
</a>

View File

@@ -323,13 +323,13 @@
<div class="flex-col space-y-2 pt-4 text-center">
{#if isNonWWWDomainOK}
<button
class="bg-green-600 hover:bg-green-500"
class="btn btn-sm bg-green-600 hover:bg-green-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is OK, click to recheck.</button
>
{:else}
<button
class="bg-red-600 hover:bg-red-500"
class="btn btn-sm bg-red-600 hover:bg-red-500"
on:click|preventDefault={() => isDNSValid(getDomain(nonWWWDomain), false)}
>DNS settings for {nonWWWDomain} is invalid, click to recheck.</button
>
@@ -337,13 +337,13 @@
{#if dualCerts}
{#if isWWWDomainOK}
<button
class="bg-green-600 hover:bg-green-500"
class="btn btn-sm bg-green-600 hover:bg-green-500"
on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is OK, click to recheck.</button
>
{:else}
<button
class="bg-red-600 hover:bg-red-500"
class="btn btn-sm bg-red-600 hover:bg-red-500"
on:click|preventDefault={() => isDNSValid(getDomain(`www.${nonWWWDomain}`), true)}
>DNS settings for www.{nonWWWDomain} is invalid, click to recheck.</button
>

View File

@@ -22,7 +22,7 @@
function generateUrl(publicPort: any) {
return browser
? `sftp://${
settings.fqdn ? getDomain(settings.fqdn) : window.location.hostname
settings?.fqdn ? getDomain(settings.fqdn) : window.location.hostname
}:${publicPort}`
: 'Loading...';
}

View File

@@ -110,7 +110,6 @@
loading = true;
try {
await post(`/services/${service.id}/${service.type}/start`, {});
return window.location.reload();
} catch (error) {
return errorNotification(error);
} finally {

View File

@@ -32,10 +32,16 @@
import { get, post } from '$lib/api';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { onMount } from 'svelte';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
onMount(async () => {
if (destinations.length === 1) {
await handleSubmit(destinations[0].id);
}
});
async function handleSubmit(destinationId: any) {
try {
await post(`/services/${id}/configuration/destination`, { destinationId });
@@ -54,7 +60,9 @@
<div class="flex justify-center">
{#if !destinations || destinations.length === 0}
<div class="flex-col">
<div class="pb-2 text-center font-bold">{$t('application.configuration.no_configurable_destination')}</div>
<div class="pb-2 text-center font-bold">
{$t('application.configuration.no_configurable_destination')}
</div>
<div class="flex justify-center">
<a href="/new/destination" sveltekit:prefetch class="add-icon bg-sky-600 hover:bg-sky-500">
<svg

View File

@@ -32,12 +32,20 @@
import { get, post } from '$lib/api';
import { t } from '$lib/translations';
import { errorNotification, supportedServiceTypesAndVersions } from '$lib/common';
import { onMount } from 'svelte';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
let recommendedVersion = supportedServiceTypesAndVersions.find(
({ name }) => name === type
)?.recommendedVersion;
onMount(async () => {
if (versions.length === 1) {
await handleSubmit(versions[0]);
}
});
async function handleSubmit(version: any) {
try {
await post(`/services/${id}/configuration/version`, { version });

View File

@@ -102,7 +102,7 @@
{#each otherServices as service}
<a href="/services/{service.id}" class="p-2 no-underline">
<div class="box-selection group relative hover:bg-pink-600">
<Services type={service.type} />
<ServiceIcons type={service.type} />
<div class="truncate text-center text-xl font-bold">
{service.name}
</div>