ui: redesign a lot

This commit is contained in:
Andras Bacsai
2022-08-09 15:28:26 +00:00
parent 839e8179fe
commit 4049af6220
46 changed files with 625 additions and 338 deletions

View File

@@ -1,5 +1,5 @@
{
"name": "coolify-api",
"name": "api",
"description": "Coolify's Fastify API",
"license": "AGPL-3.0",
"scripts": {

View File

@@ -1,5 +1,5 @@
{
"name": "coolify-ui",
"name": "ui",
"description": "Coolify's SvelteKit UI",
"license": "AGPL-3.0",
"scripts": {
@@ -41,6 +41,7 @@
"@sveltejs/adapter-static": "1.0.0-next.37",
"@zerodevx/svelte-toast": "0.7.2",
"cuid": "2.1.8",
"daisyui": "2.22.0",
"js-cookie": "3.0.1",
"p-limit": "4.0.0",
"svelte-select": "4.4.7",

View File

@@ -21,4 +21,5 @@ declare namespace App {
}
declare const GITPOD_WORKSPACE_URL: string

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html data-theme="coollabs" lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />

View File

@@ -1,4 +1,4 @@
import { toast } from '@zerodevx/svelte-toast';
import { addToast } from '$lib/store';
export const supportedServiceTypesAndVersions = [
{
@@ -167,12 +167,20 @@ export const asyncSleep = (delay: number) =>
export function errorNotification(error: any): void {
if (error.message) {
if (error.message === 'Cannot read properties of undefined (reading \'postMessage\')') {
toast.push('Currently there is background process in progress. Please try again later.');
return;
return addToast({
message: 'Currently there is background process in progress. Please try again later.',
type: 'error',
});
}
toast.push(error.message);
addToast({
message: error.message,
type: 'error',
});
} else {
toast.push('Ooops, something is not okay, are you okay?');
addToast({
message: 'Ooops, something is not okay, are you okay?',
type: 'error',
});
}
console.error(JSON.stringify(error));
}

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { browser } from '$app/env';
import { addToast } from '$lib/store';
import { toast } from '@zerodevx/svelte-toast';
let showPassword = false;
@@ -20,7 +21,10 @@
function copyToClipboard() {
if (isHttps && navigator.clipboard) {
navigator.clipboard.writeText(value);
toast.push('Copied to clipboard.');
addToast({
message: 'Copied to clipboard.',
type: 'success',
});
}
}
</script>

View File

@@ -0,0 +1,52 @@
<script>
export let type = 'info';
</script>
<div
class="alert shadow-lg text-white rounded"
class:alert-success={type === 'success'}
class:alert-error={type === 'error'}
class:alert-info={type === 'info'}
>
{#if type === 'success'}
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current flex-shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/></svg
>
{:else if type === 'error'}
<svg
xmlns="http://www.w3.org/2000/svg"
class="stroke-current flex-shrink-0 h-6 w-6"
fill="none"
viewBox="0 0 24 24"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
/></svg
>
{:else if type === 'info'}
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="stroke-current flex-shrink-0 w-6 h-6"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/></svg
>
{/if}
<slot />
</div>

View File

@@ -0,0 +1,22 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import Toast from './Toast.svelte';
import { toasts } from '$lib/store';
</script>
{#if $toasts}
<section>
<article class="toast toast-bottom toast-end rounded-none" role="alert" transition:fade>
{#each $toasts as toast (toast.id)}
<Toast type={toast.type}>{toast.message}</Toast>
{/each}
</article>
</section>
{/if}
<style lang="postcss">
section {
@apply fixed top-0 left-0 right-0 w-full flex flex-col mt-4 justify-center z-[1000];
}
</style>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { dev } from '$app/env';
import { get, post } from '$lib/api';
import { appSession, features } from '$lib/store';
import { addToast, appSession, features } from '$lib/store';
import { toast } from '@zerodevx/svelte-toast';
import { asyncSleep, errorNotification } from '$lib/common';
import { onMount } from 'svelte';
@@ -22,7 +22,11 @@
return window.location.reload();
} else {
await post(`/update`, { type: 'update', latestVersion });
toast.push('Update completed.<br><br>Waiting for the new version to start...');
addToast({
message: 'Update completed.<br><br>Waiting for the new version to start...',
type: 'success'
});
let reachable = false;
let tries = 0;
do {
@@ -36,7 +40,10 @@
if (reachable) break;
tries++;
} while (!reachable || tries < 120);
toast.push('New version reachable. Reloading...');
addToast({
message: 'New version reachable. Reloading...',
type: 'success'
});
updateStatus.loading = false;
updateStatus.success = true;
await asyncSleep(3000);

View File

@@ -19,9 +19,10 @@
};
let usageInterval: any;
let loading = {
usage: false
usage: false,
cleanup: false
};
import { appSession } from '$lib/store';
import { addToast, appSession } from '$lib/store';
import { onDestroy, onMount } from 'svelte';
import { get, post } from '$lib/api';
import { errorNotification } from '$lib/common';
@@ -60,7 +61,18 @@
disk: 'stable'
};
async function manuallyCleanupStorage() {
return await post('/internal/cleanup', {});
try {
loading.cleanup = true
await post('/internal/cleanup', {});
return addToast({
message: "Cleanup done.",
type:"success"
})
} catch(error) {
return errorNotification(error);
} finally {
loading.cleanup = false
}
}
</script>
@@ -132,7 +144,7 @@
<dd class="mt-1 text-3xl font-semibold text-white">
{usage?.disk.usedGb}<span class="text-sm">GB</span>
</dd>
<button on:click={manuallyCleanupStorage} class="bg-coollabs hover:bg-coollabs-100"
<button on:click={manuallyCleanupStorage} class:loading={loading.cleanup} class="btn btn-sm"
>Cleanup Storage</button
>
</div>

View File

@@ -88,7 +88,7 @@
"removing": "Removing...",
"remove_domain": "Remove domain",
"public_port_range": "Public Port Range",
"public_port_range_explainer": "Ports used to expose databases/services/internal services.<br> Add them to your firewall (if applicable).<br><br>You can specify a range of ports, eg: <span class='text-yellow-500 font-bold'>9000-9100</span>",
"public_port_range_explainer": "Ports used to expose databases/services/internal services.<br> Add them to your firewall (if applicable).<br><br>You can specify a range of ports, eg: <span class='text-settings font-bold'>9000-9100</span>",
"no_actions_available": "No actions available",
"admin_api_key": "Admin API key"
},
@@ -144,8 +144,8 @@
},
"preview": {
"need_during_buildtime": "Need during buildtime?",
"setup_secret_app_first": "You can add secrets to PR/MR deployments. Please add secrets to the application first. <br>Useful for creating <span class='text-green-500 font-bold'>staging</span> environments.",
"values_overwriting_app_secrets": "These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-green-500 font-bold'>staging</span> environments.",
"setup_secret_app_first": "You can add secrets to PR/MR deployments. Please add secrets to the application first. <br>Useful for creating <span class='text-applications font-bold'>staging</span> environments.",
"values_overwriting_app_secrets": "These values overwrite application secrets in PR/MR deployments. Useful for creating <span class='text-applications font-bold'>staging</span> environments.",
"redeploy": "Redeploy",
"no_previews_available": "No previews available"
},
@@ -194,14 +194,14 @@
"application": "Application",
"url_fqdn": "URL (FQDN)",
"domain_fqdn": "Domain (FQDN)",
"https_explainer": "If you specify <span class='text-green-500 font-bold'>https</span>, the application will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-green-500 font-bold'>www</span>, the application will be redirected (302) from non-www and vice versa.<br><br>To modify the domain, you must first stop the application.<br><br><span class='text-white font-bold'>You must set your DNS to point to the server IP in advance.</span>",
"https_explainer": "If you specify <span class='text-applications font-bold'>https</span>, the application will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-applications font-bold'>www</span>, the application will be redirected (302) from non-www and vice versa.<br><br>To modify the domain, you must first stop the application.<br><br><span class='text-white font-bold'>You must set your DNS to point to the server IP in advance.</span>",
"ssl_www_and_non_www": "Generate SSL for www and non-www?",
"ssl_explainer": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-green-500'>both DNS entries</span> set in advance.<br><br>Useful if you expect to have visitors on both.",
"ssl_explainer": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-applications'>both DNS entries</span> set in advance.<br><br>Useful if you expect to have visitors on both.",
"install_command": "Install Command",
"build_command": "Build Command",
"start_command": "Start Command",
"directory_to_use_explainer": "Directory to use as the base for all commands.<br>Could be useful with <span class='text-green-500 font-bold'>monorepos</span>.",
"publish_directory_explainer": "Directory containing all the assets for deployment. <br> For example: <span class='text-green-500 font-bold'>dist</span>,<span class='text-green-500 font-bold'>_site</span> or <span class='text-green-500 font-bold'>public</span>.",
"directory_to_use_explainer": "Directory to use as the base for all commands.<br>Could be useful with <span class='text-applications font-bold'>monorepos</span>.",
"publish_directory_explainer": "Directory containing all the assets for deployment. <br> For example: <span class='text-applications font-bold'>dist</span>,<span class='text-applications font-bold'>_site</span> or <span class='text-applications font-bold'>public</span>.",
"features": "Features",
"enable_automatic_deployment": "Enable Automatic Deployment",
"enable_auto_deploy_webhooks": "Enable automatic deployment through webhooks.",
@@ -306,7 +306,7 @@
"change_language": "Change Language",
"permission_denied": "You do not have permission to do this. \\nAsk an admin to modify your permissions.",
"domain_removed": "Domain removed",
"ssl_explainer": "If you specify <span class='text-yellow-500 font-bold'>https</span>, Coolify will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-yellow-500 font-bold'>www</span>, Coolify will be redirected (302) from non-www and vice versa.<br><br><span class='text-yellow-500 font-bold'>WARNING:</span> If you change an already set domain, it will brake webhooks and other integrations! You need to manually update them.",
"ssl_explainer": "If you specify <span class='text-settings font-bold'>https</span>, Coolify will be accessible only over https. SSL certificate will be generated for you.<br>If you specify <span class='text-settings font-bold'>www</span>, Coolify will be redirected (302) from non-www and vice versa.<br><br><span class='text-settings font-bold'>WARNING:</span> If you change an already set domain, it will brake webhooks and other integrations! You need to manually update them.",
"must_remove_domain_before_changing": "Must remove the domain before you can change this setting.",
"registration_allowed": "Registration allowed?",
"registration_allowed_explainer": "Allow further registrations to the application. <br>It's turned off after the first registration.",
@@ -314,7 +314,7 @@
"credential_stat_explainer": "Credentials for <a class=\"text-white font-bold\" href=\"{{link}}\" target=\"_blank\">stats</a> page.",
"auto_update_enabled": "Auto update enabled?",
"auto_update_enabled_explainer": "Enable automatic updates for Coolify. It will be done automatically behind the scenes, if there is no build process running.",
"generate_www_non_www_ssl": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-yellow-500'>both DNS entries</span> set in advance.",
"generate_www_non_www_ssl": "It will generate certificates for both www and non-www. <br>You need to have <span class='font-bold text-settings'>both DNS entries</span> set in advance.",
"is_dns_check_enabled": "DNS check enabled?",
"is_dns_check_enabled_explainer": "You can disable DNS check before creating SSL certificates.<br><br>Turning it off is useful when Coolify is behind a reverse proxy or tunnel."
},

View File

@@ -50,7 +50,7 @@
"delete_application": "Supprimer l'application",
"deployment_queued": "Déploiement en file d'attente.",
"destination": "Destination",
"directory_to_use_explainer": "Répertoire à utiliser comme base pour toutes les commandes.<br>Pourrait être utile avec <span class='text-green-500 font-bold'>monorepos</span>.",
"directory_to_use_explainer": "Répertoire à utiliser comme base pour toutes les commandes.<br>Pourrait être utile avec <span class='text-applications font-bold'>monorepos</span>.",
"dns_not_set_error": "DNS non défini ou propagé pour {{domain}}.<br><br>Veuillez vérifier vos paramètres DNS.",
"dns_not_set_partial_error": "DNS non défini",
"domain_already_in_use": "Le domaine {{domain}} est déjà utilisé.",
@@ -65,7 +65,7 @@
"features": "Caractéristiques",
"git_repository": "Dépôt Git",
"git_source": "Source Git",
"https_explainer": "Si vous spécifiez <span class='text-green-500 font-bold'>https</span>, l'application sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-green-500 font-bold'>www</span>, l'application sera redirigée (302) à partir de non-www et vice versa \n.<br><br>Pour modifier le domaine, vous devez d'abord arrêter l'application.<br><br><span class='text-white font-bold'>Vous devez configurer, en avance, votre DNS pour pointer vers l'IP du serveur.</span>",
"https_explainer": "Si vous spécifiez <span class='text-applications font-bold'>https</span>, l'application sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-applications font-bold'>www</span>, l'application sera redirigée (302) à partir de non-www et vice versa \n.<br><br>Pour modifier le domaine, vous devez d'abord arrêter l'application.<br><br><span class='text-white font-bold'>Vous devez configurer, en avance, votre DNS pour pointer vers l'IP du serveur.</span>",
"install_command": "Commande d'installation",
"logs": "Journaux des applications",
"no_applications_found": "Aucune application trouvée",
@@ -78,11 +78,11 @@
"need_during_buildtime": "Besoin pendant la build ?",
"no_previews_available": "Aucun aperçu disponible",
"redeploy": "Redéployer",
"setup_secret_app_first": "Vous pouvez ajouter des secrets aux déploiements PR/MR. \nVeuillez d'abord ajouter des secrets à l'application. \n<br>Utile pour créer des environnements <span class='text-green-500 font-bold'>de mise en scène</span>.",
"values_overwriting_app_secrets": "Ces valeurs remplacent les secrets d'application dans les déploiements PR/MR. \nUtile pour créer des environnements <span class='text-green-500 font-bold'>de mise en scène</span>."
"setup_secret_app_first": "Vous pouvez ajouter des secrets aux déploiements PR/MR. \nVeuillez d'abord ajouter des secrets à l'application. \n<br>Utile pour créer des environnements <span class='text-applications font-bold'>de mise en scène</span>.",
"values_overwriting_app_secrets": "Ces valeurs remplacent les secrets d'application dans les déploiements PR/MR. \nUtile pour créer des environnements <span class='text-applications font-bold'>de mise en scène</span>."
},
"previews": "Aperçus",
"publish_directory_explainer": "Répertoire contenant tous les actifs à déployer. \n<br> Par exemple : <span class='text-green-500 font-bold'>dist</span>,<span class='text-green-500 font-bold'>_site</span> ou <span \nclass='text-green-500 font-bold'>public</span>.",
"publish_directory_explainer": "Répertoire contenant tous les actifs à déployer. \n<br> Par exemple : <span class='text-applications font-bold'>dist</span>,<span class='text-applications font-bold'>_site</span> ou <span \nclass='text-applications font-bold'>public</span>.",
"rebuild_application": "Re-build l'application",
"secret": "secrets",
"secrets": {
@@ -91,7 +91,7 @@
"use_isbuildsecret": "Utiliser isBuildSecret"
},
"settings_saved": "Paramètres sauvegardés.",
"ssl_explainer": "Il générera des certificats pour www et non-www. \n<br>Vous devez avoir <span class='font-bold text-green-500'>les deux entrées DNS</span> définies à l'avance.<br><br>Utile si vous prévoyez d'avoir des visiteurs sur les deux.",
"ssl_explainer": "Il générera des certificats pour www et non-www. \n<br>Vous devez avoir <span class='font-bold text-applications'>les deux entrées DNS</span> définies à l'avance.<br><br>Utile si vous prévoyez d'avoir des visiteurs sur les deux.",
"ssl_www_and_non_www": "Générer SSL pour www et non-www ?",
"start_command": "Démarrer la commande",
"stop_application": "Arrêter l'application",
@@ -181,7 +181,7 @@
"path": "Chemin",
"port": "Port",
"public_port_range": "Gamme de ports publics",
"public_port_range_explainer": "Ports utilisés pour exposer les bases de données/services/services internes.<br> Ajoutez-les à votre pare-feu (le cas échéant).<br><br>Vous pouvez spécifier une plage de ports, par exemple : <span class='text-yellow-500 \nfont-bold'>9000-9100</span>",
"public_port_range_explainer": "Ports utilisés pour exposer les bases de données/services/services internes.<br> Ajoutez-les à votre pare-feu (le cas échéant).<br><br>Vous pouvez spécifier une plage de ports, par exemple : <span class='text-settings \nfont-bold'>9000-9100</span>",
"publish_directory": "Publier le répertoire",
"remove": "Retirer",
"remove_domain": "Supprimer le domaine",
@@ -266,7 +266,7 @@
"permission_denied": "Vous n'avez pas la permission de faire cela. \n\\nDemandez à un administrateur de modifier vos autorisations.",
"registration_allowed": "Inscription autorisée ?",
"registration_allowed_explainer": "Autoriser d'autres inscriptions à l'application. \n<br>Il est désactivé après la première inscription.",
"ssl_explainer": "Si vous spécifiez <span class='text-yellow-500 font-bold'>https</span>, Coolify sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-yellow-500 font-bold'>www</span>, Coolify sera redirigé (302) à partir de non-www et vice versa."
"ssl_explainer": "Si vous spécifiez <span class='text-settings font-bold'>https</span>, Coolify sera accessible uniquement via https. \nUn certificat SSL sera généré pour vous.<br>Si vous spécifiez <span class='text-settings font-bold'>www</span>, Coolify sera redirigé (302) à partir de non-www et vice versa."
},
"source": {
"application_id": "ID d'application",

View File

@@ -17,6 +17,11 @@ interface AppSession {
gitlab: string | null,
}
}
interface AddToast {
type?: "info" | "success" | "error",
message: string,
timeout?: number | undefined
}
export const loginEmail: Writable<string | undefined> = writable()
export const appSession: Writable<AppSession> = writable({
ipv4: null,
@@ -74,4 +79,30 @@ export const setLocation = (resource: any) => {
} else {
location.set(resource.fqdn)
}
}
export const toasts: any = writable([])
export const dismissToast = (id: number) => {
toasts.update((all: any) => all.filter((t: any) => t.id !== id))
}
export const addToast = (toast: AddToast) => {
// Create a unique ID so we can easily find/remove it
// if it is dismissible/has a timeout.
const id = Math.floor(Math.random() * 10000)
// Setup some sensible defaults for a toast.
const defaults = {
id,
type: 'info',
timeout: 2000,
}
// Push the toast to the top of the list of toasts
const t = { ...defaults, ...toast }
toasts.update((all: any) => [t, ...all])
// If toast is dismissible, dismiss it after "timeout" amount of time.
if (t.timeout) setTimeout(() => dismissToast(id), t.timeout)
}

View File

@@ -86,6 +86,7 @@
import PageLoader from '$lib/components/PageLoader.svelte';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
import Toasts from '$lib/components/Toasts.svelte';
if (userId) $appSession.userId = userId;
if (teamId) $appSession.teamId = teamId;
@@ -110,7 +111,8 @@
<link rel="icon" href={$appSession.whiteLabeledDetails.icon} />
{/if}
</svelte:head>
<SvelteToast options={{ intro: { y: -64 }, duration: 3000, pausable: true }} />
<Toasts />
<!-- <SvelteToast options={{ intro: { y: -64 }, duration: 3000, pausable: true }} /> -->
{#if $navigating}
<div out:fade={{ delay: 100 }}>
<PageLoader />

View File

@@ -12,8 +12,8 @@
import { del } from '$lib/api';
import { errorNotification } from '$lib/common';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import { addToast } from '$lib/store';
import { t } from '$lib/translations';
import { toast } from '@zerodevx/svelte-toast';
import { createEventDispatcher } from 'svelte';
import { saveSecret } from './utils';
@@ -28,7 +28,10 @@
value = '';
isBuildSecret = false;
}
toast.push('Secret removed.');
addToast({
message: 'Secret removed.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -36,6 +39,7 @@
async function createSecret(isNew: any) {
try {
if (!name || !value) return
await saveSecret({
isNew,
name,
@@ -51,9 +55,12 @@
isBuildSecret = false;
}
dispatch('refresh');
toast.push('Secret saved.');
addToast({
message: 'Secret removed.',
type: 'success'
});
} catch (error) {
console.log(error)
console.log(error);
return errorNotification(error);
}
}
@@ -71,7 +78,10 @@
isNewSecret,
applicationId: id
});
toast.push('Secret saved.');
addToast({
message: 'Secret removed.',
type: 'success'
});
}
}
}
@@ -100,8 +110,7 @@
/>
</td>
<td class="text-center">
<div
type="button"
<button
on:click={setSecretValue}
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"
@@ -146,23 +155,25 @@
</svg>
</span>
</span>
</div>
</button>
</td>
<td>
{#if isNewSecret}
<div class="flex items-center justify-center">
<button class="bg-green-600 hover:bg-green-500" on:click={() => createSecret(true)}
<button class="btn bg-applications btn-sm" on:click={() => createSecret(true)}
>{$t('forms.add')}</button
>
</div>
{:else}
<div class="flex flex-row justify-center space-x-2">
<div class="flex items-center justify-center">
<button class="" on:click={() => createSecret(false)}>{$t('forms.set')}</button>
<button class="btn bg-application btn-sm" on:click={() => createSecret(false)}
>{$t('forms.set')}</button
>
</div>
{#if !isPRMRSecret}
<div class="flex justify-center items-end">
<button class="bg-red-600 hover:bg-red-500" on:click={removeSecret}
<button class="btn btn-sm bg-red-600 hover:bg-red-500" on:click={removeSecret}
>{$t('forms.remove')}</button
>
</div>

View File

@@ -11,6 +11,7 @@
import { toast } from '@zerodevx/svelte-toast';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { addToast } from '$lib/store';
const { id } = $page.params;
const dispatch = createEventDispatcher();
@@ -30,8 +31,17 @@
storage.path = null;
storage.id = null;
}
if (newStorage) toast.push($t('application.storage.storage_saved'));
else toast.push($t('application.storage.storage_updated'));
if (newStorage) {
addToast({
message: $t('application.storage.storage_saved'),
type: 'success'
});
} else {
addToast({
message: $t('application.storage.storage_updated'),
type: 'success'
});
}
} catch (error) {
return errorNotification(error);
}
@@ -40,7 +50,10 @@
try {
await del(`/applications/${id}/storages`, { path: storage.path });
dispatch('refresh');
toast.push($t('application.storage.storage_deleted'));
addToast({
message: $t('application.storage.storage_deleted'),
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -58,17 +71,19 @@
<td>
{#if isNew}
<div class="flex items-center justify-center">
<button class="bg-green-600 hover:bg-green-500" on:click={() => saveStorage(true)}
<button class="btn btn-sm bg-applications" on:click={() => saveStorage(true)}
>{$t('forms.add')}</button
>
</div>
{:else}
<div class="flex flex-row justify-center space-x-2">
<div class="flex items-center justify-center">
<button class="" on:click={() => saveStorage(false)}>{$t('forms.set')}</button>
<button class="btn btn-sm bg-applications" on:click={() => saveStorage(false)}
>{$t('forms.set')}</button
>
</div>
<div class="flex justify-center items-end">
<button class="bg-red-600 hover:bg-red-500" on:click={removeStorage}
<button class="btn btn-sm bg-red-600 hover:bg-red-500" on:click={removeStorage}
>{$t('forms.remove')}</button
>
</div>

View File

@@ -60,7 +60,7 @@
import { toast } from '@zerodevx/svelte-toast';
import { onDestroy, onMount } from 'svelte';
import { t } from '$lib/translations';
import { appSession, disabledButton, status, location, setLocation } from '$lib/store';
import { appSession, disabledButton, status, location, setLocation, addToast } from '$lib/store';
import { errorNotification, handlerNotFoundLoad } from '$lib/common';
import Loading from '$lib/components/Loading.svelte';
@@ -80,7 +80,10 @@
async function handleDeploySubmit() {
try {
const { buildId } = await post(`/applications/${id}/deploy`, { ...application });
toast.push($t('application.deployment_queued'));
addToast({
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 {

View File

@@ -190,11 +190,11 @@
</div>
<div class="pt-5 flex-col flex justify-center items-center space-y-4">
<button
class="w-40"
class="btn btn-wide"
type="submit"
disabled={!showSave}
class:bg-orange-600={showSave}
class:hover:bg-orange-500={showSave}>{$t('forms.save')}</button
class:bg-applications={showSave}
>{$t('forms.save')}</button
>
</div>
</form>

View File

@@ -413,11 +413,10 @@
<div class="flex flex-col items-center justify-center space-y-4 pt-5">
<button
on:click|preventDefault={save}
class="w-40"
class="btn btn-wide"
type="submit"
disabled={!showSave || loading.save}
class:bg-orange-600={showSave && !loading.save}
class:hover:bg-orange-500={showSave && !loading.save}
class:bg-applications={showSave && !loading.save}
>{loading.save ? $t('forms.saving') : $t('forms.save')}</button
>
{#if tryAgain}

View File

@@ -35,13 +35,14 @@
import { get, post } from '$lib/api';
import cuid from 'cuid';
import { browser } from '$app/env';
import { appSession, disabledButton, setLocation, status } from '$lib/store';
import { addToast, appSession, disabledButton, setLocation, status } from '$lib/store';
import { t } from '$lib/translations';
import { errorNotification, getDomain, notNodeDeployments, staticDeployments } from '$lib/common';
import Setting from './_Setting.svelte';
const { id } = $page.params;
$: isDisabled = !$appSession.isAdmin || $status.application.isRunning || $status.application.initialLoading;
$: isDisabled =
!$appSession.isAdmin || $status.application.isRunning || $status.application.initialLoading;
let domainEl: HTMLInputElement;
@@ -138,7 +139,10 @@
branch: application.branch,
projectId: application.projectId
});
return toast.push($t('application.settings_saved'));
return addToast({
message: $t('application.settings_saved'),
type: 'success'
});
} catch (error) {
if (name === 'debug') {
debug = !debug;
@@ -169,10 +173,13 @@
exposePort: application.exposePort
});
await post(`/applications/${id}`, { ...application });
setLocation(application)
setLocation(application);
$disabledButton = false;
forceSave = false;
return toast.push('Configurations saved.');
addToast({
message: 'Configuration saved.',
type: 'success',
});
} catch (error) {
console.log(error);
//@ts-ignore
@@ -215,7 +222,10 @@
async function isDNSValid(domain: any, isWWW: any) {
try {
await get(`/applications/${id}/check?domain=${domain}`);
toast.push('DNS configuration is valid.');
addToast({
message: 'DNS configuration is valid.',
type: 'success'
});
isWWW ? (isWWWDomainOK = true) : (isNonWWWDomainOK = true);
return true;
} catch (error) {
@@ -312,29 +322,21 @@
<div class="title">{$t('general')}</div>
{#if $appSession.isAdmin}
<button
class="btn btn-sm"
type="submit"
class:bg-green-600={!loading}
class:bg-applications={!loading}
class:loading={loading}
class:bg-orange-600={forceSave}
class:hover:bg-green-500={!loading}
class:hover:bg-orange-400={forceSave}
disabled={loading}
>{loading
? $t('forms.saving')
: forceSave
? $t('forms.confirm_continue')
: $t('forms.save')}</button
>{$t('forms.save')}</button
>
{/if}
</div>
<div class="grid grid-flow-row gap-2 px-10">
<div class="mt-2 grid grid-cols-2 items-center">
<label for="name" class="text-base font-bold text-stone-100">{$t('forms.name')}</label>
<input
name="name"
id="name"
bind:value={application.name}
required
/>
<input name="name" id="name" bind:value={application.name} required />
</div>
<div class="grid grid-cols-2 items-center">
<label for="gitSource" class="text-base font-bold text-stone-100"

View File

@@ -28,6 +28,7 @@
import { errorNotification, getDomain } from '$lib/common';
import { onMount } from 'svelte';
import Loading from '$lib/components/Loading.svelte';
import { addToast } from '$lib/store';
const { id } = $page.params;
@@ -59,7 +60,10 @@
pullmergeRequestId: container.pullmergeRequestId,
branch: container.branch
});
toast.push('Deployment queued');
addToast({
message: 'Deployment queued',
type: 'success'
});
if ($page.url.pathname.startsWith(`/applications/${id}/logs/build`)) {
return window.location.assign(`/applications/${id}/logs/build?buildId=${buildId}`);
} else {

View File

@@ -28,6 +28,7 @@
import { get } from '$lib/api';
import { saveSecret } from './utils';
import { toast } from '@zerodevx/svelte-toast';
import { addToast } from '$lib/store';
const limit = pLimit(1);
const { id } = $page.params;
@@ -59,7 +60,10 @@
);
batchSecrets = '';
await refreshSecrets();
toast.push('Secrets saved.');
addToast({
message: 'Secrets saved.',
type: 'success'
});
}
</script>
@@ -148,7 +152,7 @@
<form on:submit|preventDefault={getValues} class="mb-12 w-full">
<textarea bind:value={batchSecrets} class="mb-2 min-h-[200px] w-full" />
<button
class="bg-green-600 hover:bg-green-500 disabled:text-white disabled:opacity-40"
class="btn btn-sm bg-applications"
type="submit">Batch add secrets</button
>
</form>

View File

@@ -61,14 +61,14 @@
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl ">{$t('index.applications')}</div>
<div class="mr-4 text-2xl">{$t('index.applications')}</div>
{#if $appSession.isAdmin}
<button
on:click={newApplication}
class="add-icon cursor-pointer bg-green-600 hover:bg-green-500"
class="btn btn-square btn-sm bg-applications"
>
<svg
class="w-6"
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"

View File

@@ -16,7 +16,7 @@
import { toast } from '@zerodevx/svelte-toast';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { appSession, status } from '$lib/store';
import { addToast, appSession, status } from '$lib/store';
import Explainer from '$lib/components/Explainer.svelte';
const { id } = $page.params;
@@ -92,7 +92,10 @@
loading = true;
await post(`/databases/${id}`, { ...database, isRunning: $status.database.isRunning });
generateDbDetails();
toast.push('Configuration saved.');
addToast({
message: 'Configuration saved.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
} finally {
@@ -108,9 +111,10 @@
{#if $appSession.isAdmin}
<button
type="submit"
class:bg-purple-600={!loading}
class:hover:bg-purple-500={!loading}
disabled={loading}>{loading ? $t('forms.saving') : $t('forms.save')}</button
class="btn btn-sm"
class:loading={loading}
class:bg-databases={!loading}
disabled={loading}>{$t('forms.save')}</button
>
{/if}
</div>

View File

@@ -50,21 +50,24 @@
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">{$t('index.databases')}</div>
<div on:click={newDatabase} class="add-icon cursor-pointer bg-purple-600 hover:bg-purple-500">
<svg
class="w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/></svg
<button
on:click={newDatabase}
class="btn btn-square btn-sm bg-databases"
>
</div>
<svg
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/></svg
>
</button>
</div>
<div class="flex-col justify-center">

View File

@@ -9,7 +9,7 @@
import { onMount } from 'svelte';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
import { addToast, appSession } from '$lib/store';
import Setting from '$lib/components/Setting.svelte';
const { id } = $page.params;
@@ -24,7 +24,10 @@
loading.save = true;
try {
await post(`/destinations/${id}`, { ...destination });
toast.push('Configuration saved.');
addToast({
message: 'Configuration saved.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
} finally {
@@ -96,7 +99,10 @@
async function stopProxy() {
try {
await post(`/destinations/${id}/stop`, { engine: destination.engine });
return toast.push($t('destination.coolify_proxy_stopped'));
return addToast({
message: $t('destination.coolify_proxy_stopped'),
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -104,7 +110,10 @@
async function startProxy() {
try {
await post(`/destinations/${id}/start`, { engine: destination.engine });
return toast.push($t('destination.coolify_proxy_started'));
return addToast({
message: $t('destination.coolify_proxy_started'),
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -114,7 +123,10 @@
if (sure) {
try {
loading.restart = true;
toast.push($t('destination.coolify_proxy_restarting'));
addToast({
message: $t('destination.coolify_proxy_restarting'),
type: 'success'
});
await post(`/destinations/${id}/restart`, {
engine: destination.engine,
fqdn: settings.fqdn
@@ -136,19 +148,18 @@
{#if $appSession.isAdmin}
<button
type="submit"
class="bg-sky-600 hover:bg-sky-500"
class:bg-sky-600={!loading.save}
class:hover:bg-sky-500={!loading.save}
class="btn btn-sm"
class:bg-destinations={!loading.save}
class:loading={loading.save}
disabled={loading.save}
>{loading.save ? $t('forms.saving') : $t('forms.save')}
>{$t('forms.save')}
</button>
<button
class={loading.restart ? '' : 'bg-red-600 hover:bg-red-500'}
class="btn btn-sm"
class:loading={loading.restart}
class:bg-error={!loading.restart}
disabled={loading.restart}
on:click|preventDefault={forceRestartProxy}
>{loading.restart
? $t('destination.restarting_please_wait')
: $t('destination.force_restart_proxy')}</button
on:click|preventDefault={forceRestartProxy}>{$t('destination.force_restart_proxy')}</button
>
{/if}
</div>

View File

@@ -40,10 +40,10 @@
<div class="flex-col space-y-2 pb-10 text-center">
<div class="text-xl font-bold text-white">{$t('destination.new.predefined_destinations')}</div>
<div class="flex justify-center space-x-2">
<button class="w-32" on:click={() => setPredefined('localDocker')}
<button class="btn btn-sm" on:click={() => setPredefined('localDocker')}
>{$t('sources.local_docker')}</button
>
<button class="w-32" on:click={() => setPredefined('remoteDocker')}>Remote Docker</button>
<button class="btn btn-sm" on:click={() => setPredefined('remoteDocker')}>Remote Docker</button>
<!-- <button class="w-32" on:click={() => setPredefined('kubernetes')}>Kubernetes</button> -->
</div>
</div>

View File

@@ -10,7 +10,7 @@
import { onMount } from 'svelte';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
import { addToast, appSession } from '$lib/store';
const { id } = $page.params;
@@ -28,7 +28,10 @@
loading.save = true;
try {
await post(`/destinations/${id}`, { ...destination });
toast.push('Configuration saved.');
addToast({
message: 'Configuration saved.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
} finally {
@@ -103,7 +106,10 @@
async function stopProxy() {
try {
await post(`/destinations/${id}/stop`, { engine: destination.engine });
return toast.push($t('destination.coolify_proxy_stopped'));
return addToast({
message: $t('destination.coolify_proxy_stopped'),
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -111,7 +117,10 @@
async function startProxy() {
try {
await post(`/destinations/${id}/start`, { engine: destination.engine });
return toast.push($t('destination.coolify_proxy_started'));
return addToast({
message: $t('destination.coolify_proxy_started'),
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -121,7 +130,10 @@
if (sure) {
try {
loading.restart = true;
toast.push($t('destination.coolify_proxy_restarting'));
addToast({
message: $t('destination.coolify_proxy_restarting'),
type: 'success'
});
await post(`/destinations/${id}/restart`, {
engine: destination.engine,
fqdn: settings.fqdn
@@ -140,8 +152,10 @@
loading.verify = true;
await post(`/destinations/${id}/verify`, {});
destination.remoteVerified = true;
toast.push('Remote Docker Engine verified!');
return;
return addToast({
message: 'Remote Docker Engine verified!',
type: 'success'
});
} catch (error) {
return errorNotification(error);
} finally {
@@ -156,26 +170,28 @@
{#if $appSession.isAdmin}
<button
type="submit"
class="bg-sky-600 hover:bg-sky-500"
class:bg-sky-600={!loading.save}
class:hover:bg-sky-500={!loading.save}
class="btn btn-sm"
class:loading={loading.save}
class:bg-destinations={!loading.save}
disabled={loading.save}
>{loading.save ? $t('forms.saving') : $t('forms.save')}
>{$t('forms.save')}
</button>
{#if !destination.remoteVerified}
<button
disabled={loading.verify}
class="btn btn-sm"
class:loading={loading.verify}
on:click|preventDefault|stopPropagation={verifyRemoteDocker}
>{loading.verify ? 'Verifying...' : 'Verify Remote Docker Engine'}</button
>Verify Remote Docker Engine</button
>
{:else}
<button
class={loading.restart ? '' : 'bg-red-600 hover:bg-red-500'}
class="btn btn-sm"
class:loading={loading.restart}
class:bg-error={!loading.restart}
disabled={loading.restart}
on:click|preventDefault={forceRestartProxy}
>{loading.restart
? $t('destination.restarting_please_wait')
: $t('destination.force_restart_proxy')}</button
>{$t('destination.force_restart_proxy')}</button
>
{/if}
{/if}

View File

@@ -39,9 +39,9 @@
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">{$t('index.destinations')}</div>
{#if $appSession.isAdmin}
<a href="/destinations/new" class="add-icon bg-sky-600 hover:bg-sky-500">
<a href="/destinations/new" class="btn btn-square btn-sm bg-destinations">
<svg
class="w-6"
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"

View File

@@ -28,7 +28,7 @@
import { del, get, post } from '$lib/api';
import { toast } from '@zerodevx/svelte-toast';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
import { addToast, appSession } from '$lib/store';
import { goto } from '$app/navigation';
import Cookies from 'js-cookie';
if (accounts.length === 0) {
@@ -42,7 +42,10 @@
}
try {
await post(`/iam/user/password`, { id });
toast.push('Password reset successfully. Please relogin to reset it.');
return addToast({
message: 'Password reset successfully. Please relogin to reset it.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -54,7 +57,10 @@
}
try {
await del(`/iam/user/remove`, { id });
toast.push('Account deleted.');
addToast({
message: 'Account deleted.',
type: 'success'
});
const data = await get('/iam');
accounts = data.accounts;
} catch (error) {
@@ -104,21 +110,24 @@
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">Identity and Access Management</div>
<div on:click={newTeam} class="add-icon cursor-pointer bg-fuchsia-600 hover:bg-fuchsia-500">
<svg
class="w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/></svg
<button
on:click={newTeam}
class="btn btn-square btn-sm bg-iam"
>
</div>
<svg
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/></svg
>
</button>
</div>
{#if invitations.length > 0}
@@ -132,11 +141,11 @@
<span class="font-bold text-rose-600">{invitation.permission}</span> permission.
</div>
<button
class="hover:bg-green-500"
class="btn btn-sm btn-success"
on:click={() => acceptInvitation(invitation.id, invitation.teamId)}>Accept</button
>
<button
class="hover:bg-red-600"
class="btn btn-sm btn-error"
on:click={() => revokeInvitation(invitation.id, invitation.teamId)}>Delete</button
>
</div>
@@ -168,14 +177,14 @@
<td class="flex space-x-2">
<form on:submit|preventDefault={() => resetPassword(account.id)}>
<button
class="mx-auto my-4 w-32 bg-fuchsia-600 hover:bg-fuchsia-500 disabled:bg-coolgray-200"
class="my-4 btn btn-sm bg-iam"
>Reset Password</button
>
</form>
<form on:submit|preventDefault={() => deleteUser(account.id)}>
<button
disabled={account.id === $appSession.userId}
class="mx-auto my-4 w-32 bg-red-600 hover:bg-red-500 disabled:bg-coolgray-200"
class="my-4 btn btn-sm"
type="submit">Delete User</button
>
</form>

View File

@@ -91,7 +91,7 @@
<form on:submit|preventDefault={handleSubmit} class=" py-4">
<div class="flex space-x-1 pb-5">
<div class="title font-bold">{$t('index.settings')}</div>
<button class="bg-fuchsia-600 hover:bg-fuchsia-500" type="submit">{$t('forms.save')}</button>
<button class="btn btn-sm bg-iam" type="submit">{$t('forms.save')}</button>
</div>
<div class="grid grid-flow-row gap-2 px-10">
<div class="mt-2 grid grid-cols-2">
@@ -130,11 +130,11 @@
{#if $appSession.isAdmin && permission.user.id !== $appSession.userId && permission.permission !== 'owner'}
<td class="flex flex-col items-center justify-center space-y-2 py-4 text-center">
<button
class="w-52 bg-red-600 hover:bg-red-500"
class="btn btn-sm btn-error"
on:click={() => removeFromTeam(permission.user.id)}>{$t('forms.remove')}</button
>
<button
class="w-52"
class="btn btn-sm"
on:click={() =>
changePermission(permission.user.id, permission.id, permission.permission)}
>{$t('team.promote_to', {
@@ -157,7 +157,7 @@
{#if isAdmin(team.permissions[0].permission)}
<td class="flex-col space-y-2 py-4 text-center">
<button
class="w-52 bg-red-600 hover:bg-red-500"
class="btn btn-sm btn-error"
on:click={() => revokeInvitation(invitation.id)}
>{$t('team.revoke_invitation')}</button
>
@@ -174,7 +174,7 @@
<div class="flex space-x-1">
<div class="flex space-x-1">
<div class="title font-bold">{$t('team.invite_new_member')}</div>
<button class="bg-fuchsia-600 hover:bg-fuchsia-500" type="submit"
<button class="btn btn-sm bg-iam" type="submit"
>{$t('team.send_invitation')}</button
>
</div>
@@ -191,14 +191,14 @@
<div class="flex-1" />
<button
on:click={() => (invitation.permission = 'read')}
class="rounded-none rounded-l border border-dashed border-transparent"
class="px-2 rounded-none rounded-l border border-dashed border-transparent"
type="button"
class:border-coolgray-300={invitation.permission !== 'read'}
class:bg-fuchsia-500={invitation.permission === 'read'}>{$t('team.read')}</button
>
<button
on:click={() => (invitation.permission = 'admin')}
class="rounded-none rounded-r border border-dashed border-transparent"
class="px-2 rounded-none rounded-r border border-dashed border-transparent"
type="button"
class:border-coolgray-300={invitation.permission !== 'admin'}
class:bg-red-500={invitation.permission === 'admin'}>{$t('team.admin')}</button

View File

@@ -7,6 +7,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 { toast } from '@zerodevx/svelte-toast';
import { createEventDispatcher } from 'svelte';
@@ -31,7 +32,6 @@
await post(`/services/${id}/secrets`, {
name,
value,
isNew
});
dispatch('refresh');
@@ -39,7 +39,10 @@
name = '';
value = '';
}
toast.push('Secret saved.');
addToast({
message: 'Secret saved.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -72,15 +75,15 @@
<td>
{#if isNewSecret}
<div class="flex items-center justify-center">
<button class="bg-green-600 hover:bg-green-500" on:click={() => saveSecret(true)}>Add</button>
<button class="btn btn-sm bg-success" on:click={() => saveSecret(true)}>Add</button>
</div>
{:else}
<div class="flex flex-row justify-center space-x-2">
<div class="flex items-center justify-center">
<button class="" on:click={() => saveSecret(false)}>Set</button>
<button class="btn btn-sm bg-success" on:click={() => saveSecret(false)}>Set</button>
</div>
<div class="flex justify-center items-end">
<button class="bg-red-600 hover:bg-red-500" on:click={removeSecret}>Remove</button>
<button class="btn btn-sm bg-error" on:click={removeSecret}>Remove</button>
</div>
</div>
{/if}

View File

@@ -13,7 +13,7 @@
import { get, post } from '$lib/api';
import { errorNotification, getDomain } from '$lib/common';
import { t } from '$lib/translations';
import { appSession, disabledButton, status, location, setLocation } from '$lib/store';
import { appSession, disabledButton, status, location, setLocation, addToast } from '$lib/store';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import Setting from '$lib/components/Setting.svelte';
@@ -45,7 +45,10 @@
async function isDNSValid(domain: any, isWWW: any) {
try {
await get(`/services/${id}/check?domain=${domain}`);
toast.push('DNS configuration is valid.');
addToast({
message: 'DNS configuration is valid.',
type: 'success'
});
isWWW ? (isWWWDomainOK = true) : (isNonWWWDomainOK = true);
return true;
} catch (error) {
@@ -70,7 +73,10 @@
setLocation(service);
$disabledButton = false;
forceSave = false;
toast.push('Configuration saved.');
return addToast({
message: 'Configuration saved.',
type: 'success'
});
} catch (error) {
//@ts-ignore
if (error?.message.startsWith($t('application.dns_not_set_partial_error'))) {
@@ -96,7 +102,10 @@
loadingVerification = true;
try {
await post(`/services/${id}/${service.type}/activate`, { id: service.id });
toast.push(t.get('services.all_email_verified'));
return addToast({
message: t.get('services.all_email_verified'),
type: 'success'
});
} catch (error) {
return errorNotification(error);
} finally {
@@ -109,7 +118,10 @@
dualCerts = !dualCerts;
}
await post(`/services/${id}/settings`, { dualCerts });
return toast.push(t.get('application.settings_saved'));
return addToast({
message: t.get('application.settings_saved'),
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -145,13 +157,14 @@
{#if $appSession.isAdmin}
<button
type="submit"
class:bg-pink-600={!loading}
class="btn btn-sm"
class:bg-orange-600={forceSave}
class:hover:bg-pink-500={!loading}
class:hover:bg-orange-400={forceSave}
class:loading={loading}
class:bg-services={!loading}
disabled={loading}
>{loading
? $t('forms.saving')
? $t('forms.save')
: forceSave
? $t('forms.confirm_continue')
: $t('forms.save')}</button

View File

@@ -10,6 +10,7 @@
import { toast } from '@zerodevx/svelte-toast';
import { errorNotification } from '$lib/common';
import { addToast } from '$lib/store';
const { id } = $page.params;
const dispatch = createEventDispatcher();
@@ -29,8 +30,10 @@
storage.path = null;
storage.id = null;
}
if (newStorage) toast.push('Storage saved.');
else toast.push('Storage updated.');
addToast({
message: 'Storage saved.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -39,16 +42,19 @@
try {
await del(`/services/${id}/storages`, { path: storage.path });
dispatch('refresh');
toast.push('Storage deleted.');
return addToast({
message: 'Storage deleted.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
}
async function handleSubmit() {
if (isNew) {
await saveStorage(true)
await saveStorage(true);
} else {
await saveStorage(false)
await saveStorage(false);
}
}
</script>
@@ -66,16 +72,16 @@
<td>
{#if isNew}
<div class="flex items-center justify-center">
<button class="bg-green-600 hover:bg-green-500" on:click={() => saveStorage(true)}>Add</button
<button class="btn btn-sm btn-success" on:click={() => saveStorage(true)}>Add</button
>
</div>
{:else}
<div class="flex flex-row justify-center space-x-2">
<div class="flex items-center justify-center">
<button class="" on:click={() => saveStorage(false)}>Set</button>
<button class="btn btn-sm btn-success" on:click={() => saveStorage(false)}>Set</button>
</div>
<div class="flex justify-center items-end">
<button class="bg-red-600 hover:bg-red-500" on:click={removeStorage}>Remove</button>
<button class="btn btn-sm btn-error" on:click={removeStorage}>Remove</button>
</div>
</div>
{/if}

View File

@@ -46,21 +46,24 @@
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">{$t('index.services')}</div>
<div on:click={newService} class="add-icon cursor-pointer bg-pink-600 hover:bg-pink-500">
<svg
class="w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/></svg
<button
on:click={newService}
class="btn btn-square btn-sm bg-services"
>
</div>
<svg
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/></svg
>
</button>
</div>
<div class="flex-col justify-center">

View File

@@ -24,7 +24,7 @@
import { browser } from '$app/env';
import { toast } from '@zerodevx/svelte-toast';
import { t } from '$lib/translations';
import { appSession, features } from '$lib/store';
import { addToast, appSession, features } from '$lib/store';
import { errorNotification, getDomain } from '$lib/common';
import Menu from './_Menu.svelte';
@@ -83,7 +83,10 @@
isAutoUpdateEnabled,
isDNSCheckEnabled
});
return toast.push(t.get('application.settings_saved'));
return addToast({
message: t.get('application.settings_saved'),
type: 'success'
});
} catch (error) {
return errorNotification(error);
}
@@ -104,7 +107,10 @@
settings.maxPort = maxPort;
}
forceSave = false;
toast.push('Configuration saved.');
return addToast({
message: 'Configuration saved.',
type: 'success'
});
} catch (error) {
if (error.message?.startsWith($t('application.dns_not_set_partial_error'))) {
forceSave = true;
@@ -129,7 +135,10 @@
async function isDNSValid(domain: any, isWWW: any) {
try {
await get(`/settings/check?domain=${domain}`);
toast.push('DNS configuration is valid.');
addToast({
message:'DNS configuration is valid.',
type: 'success'
});
isWWW ? (isWWWDomainOK = true) : (isNonWWWDomainOK = true);
return true;
} catch (error) {
@@ -154,10 +163,9 @@
<div class="flex space-x-1 pb-6">
<div class="title font-bold">{$t('index.global_settings')}</div>
<button
class="btn btn-sm bg-settings text-black"
type="submit"
class:bg-yellow-500={!loading.save}
class:bg-orange-600={forceSave}
class:hover:bg-yellow-500={!loading.save}
class:hover:bg-orange-400={forceSave}
disabled={loading.save}
>{loading.save

View File

@@ -69,8 +69,7 @@
<div class="title font-bold">SSH Keys</div>
<button
on:click={() => (isModalActive = true)}
class:bg-yellow-500={!loading.save}
class:hover:bg-yellow-400={!loading.save}
class="btn btn-sm bg-settings text-black"
disabled={loading.save}>New SSH Key</button
>
</div>
@@ -82,7 +81,7 @@
<div class="box-selection group relative">
<div class="text-xl font-bold">{key.name}</div>
<div class="py-3 text-stone-600">Added on {key.createdAt}</div>
<button on:click={() => deleteSSHKey(key.id)} class="bg-red-500">Delete</button>
<button on:click={() => deleteSSHKey(key.id)} class="btn btn-sm bg-error">Delete</button>
</div>
{/each}
{/if}
@@ -91,7 +90,7 @@
</div>
</div>
{#if isModalActive}
<div class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="relative z-10" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-coolgray-500 bg-opacity-75 transition-opacity" />
<div class="fixed z-10 inset-0 overflow-y-auto text-white">
<div class="flex items-end sm:items-center justify-center min-h-full p-4 text-center sm:p-0">

View File

@@ -7,7 +7,7 @@
import { toast } from '@zerodevx/svelte-toast';
import { t } from '$lib/translations';
import { dashify, errorNotification, getDomain } from '$lib/common';
import { appSession } from '$lib/store';
import { addToast, appSession } from '$lib/store';
import { dev } from '$app/env';
const { id } = $page.params;
@@ -24,7 +24,10 @@
htmlUrl: source.htmlUrl.replace(/\/$/, ''),
apiUrl: source.apiUrl.replace(/\/$/, '')
});
toast.push('Configuration saved.');
return addToast({
message:'Configuration saved.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
} finally {
@@ -115,7 +118,7 @@
<div class="flex space-x-1 pb-7">
<div class="title">General</div>
{#if !source.githubAppId}
<button class="bg-orange-600 font-normal" type="submit">Save</button>
<button class="btn btn-sm bg-sources" type="submit">Save & Redirect to GitHub</button>
{/if}
</div>
<div class="grid grid-flow-row gap-2 px-10">
@@ -171,13 +174,12 @@
<div class="title">{$t('general')}</div>
{#if $appSession.isAdmin}
<button
class="btn btn-sm bg-sources"
type="submit"
class:bg-orange-600={!loading}
class:hover:bg-orange-500={!loading}
disabled={loading}>{loading ? 'Saving...' : 'Save'}</button
>
<a
class="no-underline button justify-center flex items-center"
class="btn btn-sm"
href={`${source.htmlUrl}/apps/${source.githubApp.name}/installations/new`}
>{$t('source.change_app_settings', { name: 'GitHub' })}</a
>
@@ -250,7 +252,7 @@
{:else}
<div class="text-center">
<a href={`${source.htmlUrl}/apps/${source.githubApp.name}/installations/new`}>
<button class="box-selection bg-orange-600 hover:bg-orange-500 text-xl"
<button class="box-selection bg-sources text-xl"
>Install Repositories</button
></a
>

View File

@@ -11,7 +11,7 @@
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
import { addToast, appSession } from '$lib/store';
const { id } = $page.params;
let url = settings.fqdn ? settings.fqdn : window.location.origin;
@@ -73,7 +73,10 @@
apiUrl: source.apiUrl.replace(/\/$/, ''),
customPort: source.customPort
});
toast.push('Configuration saved.');
return addToast({
message: 'Configuration saved.',
type: 'success'
});
} catch (error) {
return errorNotification(error);
} finally {
@@ -124,8 +127,10 @@
break;
case 'group':
if (!source.gitlabApp.groupName) {
toast.push('Please enter a group name first.');
return;
return addToast({
message: 'Please enter a group name first.',
type: 'error'
});
}
window.open(
`${source.htmlUrl}/groups/${source.gitlabApp.groupName}/-/settings/applications`
@@ -146,21 +151,32 @@
{#if $appSession.isAdmin}
<button
type="submit"
class:bg-orange-600={!loading}
class:hover:bg-orange-500={!loading}
class="btn btn-sm bg-sources"
disabled={loading}>{loading ? $t('forms.saving') : $t('forms.save')}</button
>
{#if source.gitlabAppId}
<button on:click|preventDefault={changeSettings}
<button class="btn btn-sm" on:click|preventDefault={changeSettings}
>{$t('source.change_app_settings', { name: 'GitLab' })}</button
>
{:else}
<button on:click|preventDefault|stopPropagation={newApp}>Create new GitLab App</button>
<button class="btn btn-sm" on:click|preventDefault|stopPropagation={newApp}
>Create new GitLab App manually</button
>
{/if}
{/if}
</div>
<div class="grid grid-flow-row gap-2 px-10">
{#if !source.gitlabAppId}
<Explainer
customClass="w-full"
text="<span class='font-bold text-base text-white'>Scopes required:</span>
<br>- <span class='text-sources font-bold'>api</span> (Access the authenticated user's API)
<br>- <span class='text-sources font-bold'>read_repository</span> (Allows read-only access to the repository)
<br>- <span class='text-sources font-bold'>email</span> (Allows read-only access to the user's primary email address using OpenID Connect)
<br>
<br>For extra security, you can set <span class='text-sources font-bold'>Expire Access Tokens</span>
<br><br>Webhook URL: <span class='text-sources font-bold'>{url}/webhooks/gitlab</span>"
/>
<div class="grid grid-cols-2 items-center">
<label for="type" class="text-base font-bold text-stone-100">Application Type</label>
<select name="type" id="type" class="w-96" bind:value={applicationType}>
@@ -295,16 +311,4 @@
</div>
</div>
</form>
{#if !source.gitlabAppId}
<Explainer
customClass="w-full"
text="<span class='font-bold text-base text-white'>Scopes required:</span>
<br>- <span class='text-orange-500 font-bold'>api</span> (Access the authenticated user's API)
<br>- <span class='text-orange-500 font-bold'>read_repository</span> (Allows read-only access to the repository)
<br>- <span class='text-orange-500 font-bold'>email</span> (Allows read-only access to the user's primary email address using OpenID Connect)
<br>
<br>For extra security, you can set <span class='text-orange-500 font-bold'>Expire Access Tokens</span>
<br><br>Webhook URL: <span class='text-orange-500 font-bold'>{url}/webhooks/gitlab</span>"
/>
{/if}
</div>

View File

@@ -44,10 +44,10 @@
</div>
<div class="flex flex-col justify-center">
<div class="flex-col space-y-2 pb-10 text-center">
<div class="text-xl font-bold text-white">Select a provider</div>
<div class="text-xl font-bold text-white">Select a git type</div>
<div class="flex justify-center space-x-2">
<button class="w-32" on:click={() => setPredefined('github')}>GitHub.com</button>
<button class="w-32" on:click={() => setPredefined('gitlab')}>GitLab.com</button>
<button class="btn btn-sm" on:click={() => setPredefined('github')}>GitHub</button>
<button class="btn btn-sm" on:click={() => setPredefined('gitlab')}>GitLab</button>
</div>
</div>
{#if source?.type}

View File

@@ -39,9 +39,9 @@
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">{$t('index.git_sources')}</div>
{#if $appSession.isAdmin}
<a href="/sources/new" class="add-icon bg-orange-600 hover:bg-orange-500">
<a href="/sources/new" class="btn btn-square btn-sm bg-sources">
<svg
class="w-6"
class="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"

View File

@@ -90,10 +90,12 @@ select {
label {
@apply inline-block w-64 text-xs tracking-tight md:text-sm;
}
button, .button {
@apply rounded bg-coolgray-200 p-2 px-3 text-sm outline-none transition-all duration-100 hover:bg-coolgray-500 disabled:cursor-not-allowed disabled:bg-coolblack disabled:text-stone-600;
.btn {
@apply text-white tracking-tighter;
}
/*button, .button {
@apply rounded bg-coolgray-200 p-2 px-3 text-sm outline-none transition-all duration-100 hover:bg-coolgray-500 disabled:cursor-not-allowed disabled:bg-coolblack disabled:text-stone-600;
}*/
a {
@apply underline hover:text-white;

View File

@@ -1,8 +1,30 @@
const defaultTheme = require('tailwindcss/defaultTheme');
// const colors = require('tailwindcss/colors');
module.exports = {
content: ['./**/*.html', './src/**/*.{js,jsx,ts,tsx,svelte}'],
important: true,
important: true,
daisyui: {
themes: [
{
coollabs: {
"base-100":"#323232",
"base-200":"#242424",
"base-300":"#181818",
"primary": "#6d28d9",
"primary-content": "#fff",
"secondary": "#343232",
"accent": "#343232",
"neutral": "#272626",
"info": "#0284c7",
"success": "#16A34A",
"warning": "#FFFF00",
"error": "#DC2626",
"--rounded-btn": "0.3rem",
"--btn-text-case": "normal"
},
}
],
},
theme: {
extend: {
keyframes: {
@@ -18,6 +40,13 @@ module.exports = {
sans: ['Poppins', ...defaultTheme.fontFamily.sans]
},
colors: {
"applications":"#16A34A",
"databases":"#9333EA",
"destinations":"#0284C7",
"sources":"#EA580C",
"services":"#DB2777",
"settings":"#FEE440",
"iam":"#C026D3",
coollabs: '#6B16ED',
'coollabs-100': '#7317FF',
coolblack: '#161616',
@@ -33,5 +62,5 @@ module.exports = {
scrollbar: ['dark'],
extend: {}
},
plugins: [require('tailwindcss-scrollbar')]
plugins: [require('tailwindcss-scrollbar'), require('daisyui')]
};