fix: UI + refactor

This commit is contained in:
Andras Bacsai 2022-09-01 13:58:44 +02:00
parent 290dbc43cb
commit 44a691ae29
27 changed files with 182 additions and 153 deletions

View File

@ -45,7 +45,7 @@ export function getAPIUrl() {
if (process.env.CODESANDBOX_HOST) { if (process.env.CODESANDBOX_HOST) {
return `https://${process.env.CODESANDBOX_HOST.replace(/\$PORT/, '3001')}` return `https://${process.env.CODESANDBOX_HOST.replace(/\$PORT/, '3001')}`
} }
return isDev ? 'http://host.docker.internal:3001' : 'http://localhost:3000'; return isDev ? 'http://localhost:3001' : 'http://localhost:3000';
} }
export function getUIUrl() { export function getUIUrl() {

View File

@ -452,12 +452,14 @@ export async function stopApplication(request: FastifyRequest<OnlyId>, reply: Fa
export async function deleteApplication(request: FastifyRequest<DeleteApplication>, reply: FastifyReply) { export async function deleteApplication(request: FastifyRequest<DeleteApplication>, reply: FastifyReply) {
try { try {
const { id } = request.params const { id } = request.params
const { force } = request.body
const { teamId } = request.user const { teamId } = request.user
const application = await prisma.application.findUnique({ const application = await prisma.application.findUnique({
where: { id }, where: { id },
include: { destinationDocker: true } include: { destinationDocker: true }
}); });
if (application?.destinationDockerId && application.destinationDocker?.network) { if (!force && application?.destinationDockerId && application.destinationDocker?.network) {
const { stdout: containers } = await executeDockerCmd({ const { stdout: containers } = await executeDockerCmd({
dockerId: application.destinationDocker.id, dockerId: application.destinationDocker.id,
command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${id} --format '{{json .}}'` command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${id} --format '{{json .}}'`

View File

@ -29,6 +29,7 @@ export interface SaveApplicationSettings extends OnlyId {
} }
export interface DeleteApplication extends OnlyId { export interface DeleteApplication extends OnlyId {
Querystring: { domain: string; }; Querystring: { domain: string; };
Body: { force: boolean }
} }
export interface CheckDomain extends OnlyId { export interface CheckDomain extends OnlyId {
Querystring: { domain: string; }; Querystring: { domain: string; };

View File

@ -7,7 +7,7 @@ import { ComposeFile, createDirectories, decrypt, encrypt, errorHandler, execute
import { day } from '../../../../lib/dayjs'; import { day } from '../../../../lib/dayjs';
import { GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSettings, SaveVersion } from '../../../../types'; import { GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSettings, SaveVersion } from '../../../../types';
import { SaveDatabaseType } from './types'; import { DeleteDatabase, SaveDatabaseType } from './types';
export async function listDatabases(request: FastifyRequest) { export async function listDatabases(request: FastifyRequest) {
try { try {
@ -167,6 +167,7 @@ export async function saveDatabaseDestination(request: FastifyRequest<SaveDataba
const { id } = request.params; const { id } = request.params;
const { destinationId } = request.body; const { destinationId } = request.body;
const { arch } = await listSettings();
await prisma.database.update({ await prisma.database.update({
where: { id }, where: { id },
data: { destinationDocker: { connect: { id: destinationId } } } data: { destinationDocker: { connect: { id: destinationId } } }
@ -181,7 +182,7 @@ export async function saveDatabaseDestination(request: FastifyRequest<SaveDataba
if (destinationDockerId) { if (destinationDockerId) {
if (type && version) { if (type && version) {
const baseImage = getDatabaseImage(type); const baseImage = getDatabaseImage(type, arch);
executeDockerCmd({ dockerId, command: `docker pull ${baseImage}:${version}` }) executeDockerCmd({ dockerId, command: `docker pull ${baseImage}:${version}` })
} }
} }
@ -360,20 +361,23 @@ export async function getDatabaseLogs(request: FastifyRequest<GetDatabaseLogs>)
return errorHandler({ status, message }) return errorHandler({ status, message })
} }
} }
export async function deleteDatabase(request: FastifyRequest<OnlyId>) { export async function deleteDatabase(request: FastifyRequest<DeleteDatabase>) {
try { try {
const teamId = request.user.teamId; const teamId = request.user.teamId;
const { id } = request.params; const { id } = request.params;
const { force } = request.body;
const database = await prisma.database.findFirst({ const database = await prisma.database.findFirst({
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } }, where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
include: { destinationDocker: true, settings: true } include: { destinationDocker: true, settings: true }
}); });
if (!force) {
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword); if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword); if (database.rootUserPassword) database.rootUserPassword = decrypt(database.rootUserPassword);
if (database.destinationDockerId) { if (database.destinationDockerId) {
const everStarted = await stopDatabaseContainer(database); const everStarted = await stopDatabaseContainer(database);
if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort); if (everStarted) await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
} }
}
await prisma.databaseSettings.deleteMany({ where: { databaseId: id } }); await prisma.databaseSettings.deleteMany({ where: { databaseId: id } });
await prisma.database.delete({ where: { id } }); await prisma.database.delete({ where: { id } });
return {} return {}

View File

@ -1,7 +1,7 @@
import { FastifyPluginAsync } from 'fastify'; import { FastifyPluginAsync } from 'fastify';
import { deleteDatabase, getDatabase, getDatabaseLogs, getDatabaseStatus, getDatabaseTypes, getDatabaseUsage, getVersions, listDatabases, newDatabase, saveDatabase, saveDatabaseDestination, saveDatabaseSettings, saveDatabaseType, saveVersion, startDatabase, stopDatabase } from './handlers'; import { deleteDatabase, getDatabase, getDatabaseLogs, getDatabaseStatus, getDatabaseTypes, getDatabaseUsage, getVersions, listDatabases, newDatabase, saveDatabase, saveDatabaseDestination, saveDatabaseSettings, saveDatabaseType, saveVersion, startDatabase, stopDatabase } from './handlers';
import type { GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSettings, SaveVersion } from '../../../../types'; import type { DeleteDatabase, GetDatabaseLogs, OnlyId, SaveDatabase, SaveDatabaseDestination, SaveDatabaseSettings, SaveVersion } from '../../../../types';
import type { SaveDatabaseType } from './types'; import type { SaveDatabaseType } from './types';
const root: FastifyPluginAsync = async (fastify): Promise<void> => { const root: FastifyPluginAsync = async (fastify): Promise<void> => {
@ -13,7 +13,7 @@ const root: FastifyPluginAsync = async (fastify): Promise<void> => {
fastify.get<OnlyId>('/:id', async (request) => await getDatabase(request)); fastify.get<OnlyId>('/:id', async (request) => await getDatabase(request));
fastify.post<SaveDatabase>('/:id', async (request, reply) => await saveDatabase(request, reply)); fastify.post<SaveDatabase>('/:id', async (request, reply) => await saveDatabase(request, reply));
fastify.delete<OnlyId>('/:id', async (request) => await deleteDatabase(request)); fastify.delete<DeleteDatabase>('/:id', async (request) => await deleteDatabase(request));
fastify.get<OnlyId>('/:id/status', async (request) => await getDatabaseStatus(request)); fastify.get<OnlyId>('/:id/status', async (request) => await getDatabaseStatus(request));

View File

@ -3,3 +3,6 @@ import type { OnlyId } from "../../../../types";
export interface SaveDatabaseType extends OnlyId { export interface SaveDatabaseType extends OnlyId {
Body: { type: string } Body: { type: string }
} }
export interface DeleteDatabase extends OnlyId {
Body: { force: string }
}

View File

@ -96,5 +96,3 @@ export interface SetGlitchTipSettings extends OnlyId {
emailSmtpUseTls: boolean emailSmtpUseTls: boolean
} }
} }

View File

@ -36,4 +36,3 @@ export interface SaveDatabaseSettings extends OnlyId {
} }

View File

@ -65,6 +65,7 @@
import Tooltip from '$lib/components/Tooltip.svelte'; import Tooltip from '$lib/components/Tooltip.svelte';
let statusInterval: any; let statusInterval: any;
let forceDelete = false;
$disabledButton = $disabledButton =
!$appSession.isAdmin || !$appSession.isAdmin ||
(!application.fqdn && !application.settings.isBot) || (!application.fqdn && !application.settings.isBot) ||
@ -97,14 +98,17 @@
} }
} }
async function deleteApplication(name: string) { async function deleteApplication(name: string, force: boolean) {
const sure = confirm($t('application.confirm_to_delete', { name })); const sure = confirm($t('application.confirm_to_delete', { name }));
if (sure) { if (sure) {
$status.application.initialLoading = true; $status.application.initialLoading = true;
try { try {
await del(`/applications/${id}`, { id }); await del(`/applications/${id}`, { id, force });
return await goto(`/applications`); return await goto(`/applications`);
} catch (error) { } catch (error) {
if (error.message.startsWith(`Command failed: SSH_AUTH_SOCK=/tmp/ssh-agent.pid`)) {
forceDelete = true;
}
return errorNotification(error); return errorNotification(error);
} finally { } finally {
$status.application.initialLoading = false; $status.application.initialLoading = false;
@ -537,9 +541,21 @@
> >
<div class="border border-coolgray-500 h-8" /> <div class="border border-coolgray-500 h-8" />
{#if forceDelete}
<button
on:click={() => deleteApplication(application.name, true)}
type="submit"
disabled={!$appSession.isAdmin}
class:bg-red-600={$appSession.isAdmin}
class:hover:bg-red-500={$appSession.isAdmin}
class="icons bg-transparent text-sm"
>
Force Delete
</button>
{:else}
<button <button
id="delete" id="delete"
on:click={() => deleteApplication(application.name)} on:click={() => deleteApplication(application.name, false)}
type="submit" type="submit"
disabled={!$appSession.isAdmin} disabled={!$appSession.isAdmin}
class:hover:text-red-500={$appSession.isAdmin} class:hover:text-red-500={$appSession.isAdmin}
@ -547,6 +563,7 @@
> >
<DeleteIcon /> <DeleteIcon />
</button> </button>
{/if}
</nav> </nav>
<slot /> <slot />

View File

@ -169,10 +169,6 @@
} }
} }
} }
function selectBranch(event: any) {
selected.branch = event.detail;
isBranchAlreadyUsed();
}
async function loadBranches(page: number = 1) { async function loadBranches(page: number = 1) {
let perPage = 100; let perPage = 100;
//@ts-ignore //@ts-ignore
@ -199,21 +195,22 @@
} }
} }
async function isBranchAlreadyUsed() { async function isBranchAlreadyUsed(event) {
selected.branch = event.detail;
try { try {
const data = await get( // const data = await get(
`/applications/${id}/configuration/repository?repository=${selected.project.path_with_namespace}&branch=${selected.branch.name}` // `/applications/${id}/configuration/repository?repository=${selected.project.path_with_namespace}&branch=${selected.branch.name}`
); // );
if (data.used) { // if (data.used) {
const sure = confirm($t('application.configuration.branch_already_in_use')); // const sure = confirm($t('application.configuration.branch_already_in_use'));
if (sure) { // if (sure) {
autodeploy = false; // autodeploy = false;
showSave = true; // showSave = true;
return true; // return true;
} // }
showSave = false; // showSave = false;
return true; // return true;
} // }
showSave = true; showSave = true;
} catch (error) { } catch (error) {
return errorNotification(error); return errorNotification(error);
@ -227,9 +224,7 @@
} }
} }
async function setWebhook(url: any, webhookToken: any) { async function setWebhook(url: any, webhookToken: any) {
const host = dev const host = dev ? getWebhookUrl('gitlab') : `${window.location.origin}/webhooks/gitlab/events`;
? getWebhookUrl('gitlab')
: `${window.location.origin}/webhooks/gitlab/events`;
try { try {
await post( await post(
url, url,
@ -294,17 +289,15 @@
); );
await post(updateDeployKeyIdUrl, { deployKeyId: id }); await post(updateDeployKeyIdUrl, { deployKeyId: id });
} catch (error) { } catch (error) {
return errorNotification(error);
} finally {
loading.save = false; loading.save = false;
return errorNotification(error);
} }
try { try {
await setWebhook(webhookUrl, webhookToken); await setWebhook(webhookUrl, webhookToken);
} catch (error) { } catch (error) {
return errorNotification(error);
} finally {
loading.save = false; loading.save = false;
return errorNotification(error);
} }
const url = `/applications/${id}/configuration/repository`; const url = `/applications/${id}/configuration/repository`;
@ -317,11 +310,11 @@
autodeploy, autodeploy,
webhookToken webhookToken
}); });
loading.save = false;
return await goto(from || `/applications/${id}/configuration/buildpack`); return await goto(from || `/applications/${id}/configuration/buildpack`);
} catch (error) { } catch (error) {
return errorNotification(error);
} finally {
loading.save = false; loading.save = false;
return errorNotification(error);
} }
} }
async function handleSubmit() { async function handleSubmit() {
@ -396,7 +389,7 @@
showIndicator={!loading.branches} showIndicator={!loading.branches}
isWaiting={loading.branches} isWaiting={loading.branches}
isDisabled={loading.branches || !selected.project} isDisabled={loading.branches || !selected.project}
on:select={selectBranch} on:select={isBranchAlreadyUsed}
on:clear={() => { on:clear={() => {
showSave = false; showSave = false;
selected.branch = null; selected.branch = null;
@ -425,7 +418,7 @@
configuration <a href={`/sources/${application.gitSource.id}`}>here.</a> configuration <a href={`/sources/${application.gitSource.id}`}>here.</a>
</div> </div>
<button <button
class="w-40 bg-green-600" class="btn btn-sm w-40 bg-green-600"
on:click|stopPropagation|preventDefault={() => window.location.reload()} on:click|stopPropagation|preventDefault={() => window.location.reload()}
> >
Try again Try again

View File

@ -4,7 +4,6 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
import Select from 'svelte-select'; import Select from 'svelte-select';
import Explainer from '$lib/components/Explainer.svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';

View File

@ -32,7 +32,6 @@
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store'; import { appSession } from '$lib/store';
import PublicRepository from './_PublicRepository.svelte'; import PublicRepository from './_PublicRepository.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import DocLink from '$lib/components/DocLink.svelte'; import DocLink from '$lib/components/DocLink.svelte';
const { id } = $page.params; const { id } = $page.params;

View File

@ -526,7 +526,7 @@
disabled={$status.application.isRunning} disabled={$status.application.isRunning}
/> />
</div> </div>
<div class="grid grid-cols-2 items-center pb-8"> <div class="grid grid-cols-2 items-center">
<Setting <Setting
id="dualCerts" id="dualCerts"
dataTooltip={$t('forms.must_be_stopped_to_modify')} dataTooltip={$t('forms.must_be_stopped_to_modify')}
@ -539,15 +539,13 @@
/> />
</div> </div>
{#if !isBot} {#if !isBot}
<div class="grid grid-cols-2"> <div class="grid grid-cols-2 items-center">
<div class="flex-col"> <label for="fqdn" class="text-base font-bold text-stone-100"
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
>{$t('application.url_fqdn')} >{$t('application.url_fqdn')}
<DocLink <DocLink
explanation={"If you specify <span class='text-settings font-bold'>https</span>, the application will be accessible only over https.<br>SSL certificate will be generated automatically.<br><br>If you specify <span class='text-settings 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-settings font-bold'>You must set your DNS to point to the server IP in advance.</span>"} explanation={"If you specify <span class='text-settings font-bold'>https</span>, the application will be accessible only over https.<br>SSL certificate will be generated automatically.<br><br>If you specify <span class='text-settings 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-settings font-bold'>You must set your DNS to point to the server IP in advance.</span>"}
/> />
</label> </label>
</div>
<div> <div>
<input <input
readonly={isDisabled} readonly={isDisabled}
@ -648,7 +646,10 @@
{/if} {/if}
{#if !staticDeployments.includes(application.buildPack)} {#if !staticDeployments.includes(application.buildPack)}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="port" class="text-base font-bold text-stone-100">{$t('forms.port')}</label> <label for="port" class="text-base font-bold text-stone-100"
>{$t('forms.port')}
<DocLink explanation={'The port your application listens on.'} /></label
>
<input <input
disabled={isDisabled} disabled={isDisabled}
readonly={!$appSession.isAdmin} readonly={!$appSession.isAdmin}
@ -657,7 +658,6 @@
bind:value={application.port} bind:value={application.port}
placeholder="{$t('forms.default')}: 'python' ? '8000' : '3000'" placeholder="{$t('forms.default')}: 'python' ? '8000' : '3000'"
/> />
<Explainer text={'The port your application listens on.'} />
</div> </div>
{/if} {/if}
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
@ -676,7 +676,7 @@
/> />
</div> </div>
{#if !notNodeDeployments.includes(application.buildPack)} {#if !notNodeDeployments.includes(application.buildPack)}
<div class="grid grid-cols-2 items-center pt-4"> <div class="grid grid-cols-2 items-center">
<label for="installCommand" class="text-base font-bold text-stone-100" <label for="installCommand" class="text-base font-bold text-stone-100"
>{$t('application.install_command')}</label >{$t('application.install_command')}</label
> >

View File

@ -87,7 +87,9 @@
</div> </div>
<div class="mx-auto max-w-6xl rounded-xl px-6 pt-4"> <div class="mx-auto max-w-6xl rounded-xl px-6 pt-4">
<div class="flex justify-center py-4 text-center">
<Explainer customClass="w-full" text={$t('application.storage.persistent_storage_explainer')} />
</div>
<table class="mx-auto border-separate text-left"> <table class="mx-auto border-separate text-left">
<thead> <thead>
<tr class="h-12"> <tr class="h-12">
@ -107,7 +109,4 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div class="flex justify-center py-4 text-center">
<Explainer customClass="w-full" text={$t('application.storage.persistent_storage_explainer')} />
</div>
</div> </div>

View File

@ -16,7 +16,7 @@
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { errorNotification } from '$lib/common'; import { errorNotification } from '$lib/common';
import { addToast, appSession, status } from '$lib/store'; import { addToast, appSession, status } from '$lib/store';
import Explainer from '$lib/components/Explainer.svelte'; import DocLink from '$lib/components/DocLink.svelte';
const { id } = $page.params; const { id } = $page.params;
@ -209,13 +209,13 @@
<div class="grid grid-cols-2 items-center px-10 pb-8"> <div class="grid grid-cols-2 items-center px-10 pb-8">
<div> <div>
<label for="url" class="text-base font-bold text-stone-100" <label for="url" class="text-base font-bold text-stone-100"
>{$t('database.connection_string')}</label >{$t('database.connection_string')}
>
{#if !isPublic && database.destinationDocker.remoteEngine} {#if !isPublic && database.destinationDocker.remoteEngine}
<Explainer <DocLink
text="You can only access the database with this URL if your application is deployed to the same Destination." explanation="You can only access the database with this URL if your application is deployed to the same Destination."
/> />
{/if} {/if}</label
>
</div> </div>
<CopyPasswordField <CopyPasswordField
textarea={true} textarea={true}

View File

@ -2,8 +2,8 @@
export let database: any; export let database: any;
import { status } from '$lib/store'; import { status } from '$lib/store';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import DocLink from '$lib/components/DocLink.svelte';
</script> </script>
<div class="flex space-x-1 py-5 font-bold"> <div class="flex space-x-1 py-5 font-bold">
@ -37,7 +37,8 @@
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="dbUserPassword" class="text-base font-bold text-stone-100" <label for="dbUserPassword" class="text-base font-bold text-stone-100"
>{$t('forms.password')}</label >{$t('forms.password')}
<DocLink explanation="Could be changed while the database is running." /></label
> >
<CopyPasswordField <CopyPasswordField
disabled={!$status.database.isRunning} disabled={!$status.database.isRunning}
@ -48,7 +49,6 @@
name="dbUserPassword" name="dbUserPassword"
bind:value={database.dbUserPassword} bind:value={database.dbUserPassword}
/> />
<Explainer text="Could be changed while the database is running." />
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="rootUser" class="text-base font-bold text-stone-100">{$t('forms.root_user')}</label> <label for="rootUser" class="text-base font-bold text-stone-100">{$t('forms.root_user')}</label>
@ -63,7 +63,7 @@
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="rootUserPassword" class="text-base font-bold text-stone-100" <label for="rootUserPassword" class="text-base font-bold text-stone-100"
>{$t('forms.roots_password')}</label >{$t('forms.roots_password')} <DocLink explanation="Could be changed while the database is running." /></label
> >
<CopyPasswordField <CopyPasswordField
disabled={!$status.database.isRunning} disabled={!$status.database.isRunning}
@ -74,6 +74,5 @@
name="rootUserPassword" name="rootUserPassword"
bind:value={database.rootUserPassword} bind:value={database.rootUserPassword}
/> />
<Explainer text="Could be changed while the database is running." />
</div> </div>
</div> </div>

View File

@ -2,8 +2,8 @@
export let database: any; export let database: any;
import { status } from '$lib/store'; import { status } from '$lib/store';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import DocLink from '$lib/components/DocLink.svelte';
</script> </script>
<div class="flex space-x-1 py-5 font-bold"> <div class="flex space-x-1 py-5 font-bold">
@ -23,7 +23,8 @@
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="rootUserPassword" class="text-base font-bold text-stone-100" <label for="rootUserPassword" class="text-base font-bold text-stone-100"
>{$t('forms.roots_password')}</label >{$t('forms.roots_password')}
<DocLink explanation="Could be changed while the database is running." /></label
> >
<CopyPasswordField <CopyPasswordField
disabled={!$status.database.isRunning} disabled={!$status.database.isRunning}
@ -34,6 +35,5 @@
name="rootUserPassword" name="rootUserPassword"
bind:value={database.rootUserPassword} bind:value={database.rootUserPassword}
/> />
<Explainer text="Could be changed while the database is running." />
</div> </div>
</div> </div>

View File

@ -4,6 +4,7 @@
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte'; import Explainer from '$lib/components/Explainer.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import DocLink from '$lib/components/DocLink.svelte';
</script> </script>
<div class="flex space-x-1 py-5 font-bold"> <div class="flex space-x-1 py-5 font-bold">
@ -37,7 +38,7 @@
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="dbUserPassword" class="text-base font-bold text-stone-100" <label for="dbUserPassword" class="text-base font-bold text-stone-100"
>{$t('forms.password')}</label >{$t('forms.password')} <DocLink explanation="Could be changed while the database is running." /></label
> >
<CopyPasswordField <CopyPasswordField
disabled={!$status.database.isRunning} disabled={!$status.database.isRunning}
@ -48,7 +49,6 @@
name="dbUserPassword" name="dbUserPassword"
bind:value={database.dbUserPassword} bind:value={database.dbUserPassword}
/> />
<Explainer text="Could be changed while the database is running." />
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="rootUser" class="text-base font-bold text-stone-100">{$t('forms.root_user')}</label> <label for="rootUser" class="text-base font-bold text-stone-100">{$t('forms.root_user')}</label>
@ -63,7 +63,7 @@
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="rootUserPassword" class="text-base font-bold text-stone-100" <label for="rootUserPassword" class="text-base font-bold text-stone-100"
>{$t('forms.roots_password')}</label >{$t('forms.roots_password')} <DocLink explanation="Could be changed while the database is running." /></label
> >
<CopyPasswordField <CopyPasswordField
disabled={!$status.database.isRunning} disabled={!$status.database.isRunning}
@ -74,6 +74,5 @@
name="rootUserPassword" name="rootUserPassword"
bind:value={database.rootUserPassword} bind:value={database.rootUserPassword}
/> />
<Explainer text="Could be changed while the database is running." />
</div> </div>
</div> </div>

View File

@ -2,8 +2,8 @@
export let database: any; export let database: any;
import { status } from '$lib/store'; import { status } from '$lib/store';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import DocLink from '$lib/components/DocLink.svelte';
</script> </script>
<div class="flex space-x-1 py-5 font-bold"> <div class="flex space-x-1 py-5 font-bold">
@ -26,7 +26,9 @@
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="rootUser" class="text-base font-bold text-stone-100" <label for="rootUser" class="text-base font-bold text-stone-100"
>Root (postgres) User Password</label >Postgres User Password <DocLink
explanation="Could be changed while the database is running."
/></label
> >
<CopyPasswordField <CopyPasswordField
disabled={!$status.database.isRunning} disabled={!$status.database.isRunning}
@ -37,7 +39,6 @@
name="rootUserPassword" name="rootUserPassword"
bind:value={database.rootUserPassword} bind:value={database.rootUserPassword}
/> />
<Explainer text="Could be changed while the database is running." />
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="dbUser" class="text-base font-bold text-stone-100">{$t('forms.user')}</label> <label for="dbUser" class="text-base font-bold text-stone-100">{$t('forms.user')}</label>
@ -52,7 +53,8 @@
</div> </div>
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="dbUserPassword" class="text-base font-bold text-stone-100" <label for="dbUserPassword" class="text-base font-bold text-stone-100"
>{$t('forms.password')}</label >{$t('forms.password')}
<DocLink explanation="Could be changed while the database is running." /></label
> >
<CopyPasswordField <CopyPasswordField
disabled={!$status.database.isRunning} disabled={!$status.database.isRunning}
@ -63,6 +65,5 @@
name="dbUserPassword" name="dbUserPassword"
bind:value={database.dbUserPassword} bind:value={database.dbUserPassword}
/> />
<Explainer text="Could be changed while the database is running." />
</div> </div>
</div> </div>

View File

@ -2,8 +2,8 @@
export let database: any; export let database: any;
import { status } from '$lib/store'; import { status } from '$lib/store';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import DocLink from '$lib/components/DocLink.svelte';
</script> </script>
<div class="flex space-x-1 py-5 font-bold"> <div class="flex space-x-1 py-5 font-bold">
@ -12,7 +12,8 @@
<div class="space-y-2 px-10"> <div class="space-y-2 px-10">
<div class="grid grid-cols-2 items-center"> <div class="grid grid-cols-2 items-center">
<label for="dbUserPassword" class="text-base font-bold text-stone-100" <label for="dbUserPassword" class="text-base font-bold text-stone-100"
>{$t('forms.password')}</label >{$t('forms.password')}
<DocLink explanation="Could be changed while the database is running." /></label
> >
<CopyPasswordField <CopyPasswordField
disabled={!$status.database.isRunning} disabled={!$status.database.isRunning}
@ -23,6 +24,5 @@
name="dbUserPassword" name="dbUserPassword"
bind:value={database.dbUserPassword} bind:value={database.dbUserPassword}
/> />
<Explainer text="Could be changed while the database is running." />
</div> </div>
</div> </div>

View File

@ -65,15 +65,16 @@
const { id } = $page.params; const { id } = $page.params;
let statusInterval: any = false; let statusInterval: any = false;
let forceDelete = false;
$disabledButton = !$appSession.isAdmin; $disabledButton = !$appSession.isAdmin;
async function deleteDatabase() { async function deleteDatabase(force: boolean) {
const sure = confirm(`Are you sure you would like to delete '${database.name}'?`); const sure = confirm(`Are you sure you would like to delete '${database.name}'?`);
if (sure) { if (sure) {
$status.database.initialLoading = true; $status.database.initialLoading = true;
try { try {
await del(`/databases/${database.id}`, { id: database.id }); await del(`/databases/${database.id}`, { id: database.id, force });
return await goto('/databases'); return await goto('/databases');
} catch (error) { } catch (error) {
return errorNotification(error); return errorNotification(error);
@ -304,14 +305,26 @@
></a ></a
> >
<Tooltip triggeredBy="#databaselogs">{'Logs'}</Tooltip> <Tooltip triggeredBy="#databaselogs">{'Logs'}</Tooltip>
{#if forceDelete}
<button
on:click={() => deleteDatabase(true)}
type="submit"
disabled={!$appSession.isAdmin}
class:hover:text-red-500={$appSession.isAdmin}
class="icons bg-transparent text-sm"
>
Force Delete</button
>{:else}
<button <button
id="delete" id="delete"
on:click={deleteDatabase} on:click={() => deleteDatabase(false)}
type="submit" type="submit"
disabled={!$appSession.isAdmin} disabled={!$appSession.isAdmin}
class:hover:text-red-500={$appSession.isAdmin} class:hover:text-red-500={$appSession.isAdmin}
class="icons bg-transparent text-sm"><DeleteIcon /></button class="icons bg-transparent text-sm"><DeleteIcon /></button
> >
{/if}
<Tooltip triggeredBy="#delete">{'Delete'}</Tooltip> <Tooltip triggeredBy="#delete">{'Delete'}</Tooltip>
</nav> </nav>
{/if} {/if}

View File

@ -1,14 +1,15 @@
<script lang="ts"> <script lang="ts">
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte'; import DocLink from '$lib/components/DocLink.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
export let readOnly: any; export let readOnly: any;
export let service: any; export let service: any;
</script> </script>
<div class="flex space-x-1 py-5"> <div class="flex space-x-1 py-5">
<div class="title">Ghost</div> <div class="title">
<Explainer text={'You can change these values in the Ghost admin panel.'} /> Ghost <DocLink explanation="You can change these values in the Ghost admin panel." />
</div>
</div> </div>
<div class="grid grid-cols-2 items-center px-10"> <div class="grid grid-cols-2 items-center px-10">
<label for="email">{$t('forms.default_email_address')}</label> <label for="email">{$t('forms.default_email_address')}</label>

View File

@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
export let service: any; export let service: any;
</script> </script>

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte'; import DocLink from '$lib/components/DocLink.svelte';
import { appSession, status } from '$lib/store'; import { appSession, status } from '$lib/store';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
export let service: any; export let service: any;
@ -11,7 +11,11 @@
<div class="title">Plausible Analytics</div> <div class="title">Plausible Analytics</div>
</div> </div>
<div class="grid grid-cols-2 items-center px-10"> <div class="grid grid-cols-2 items-center px-10">
<label for="scriptName">Script Name</label> <label for="scriptName"
>Script Name <DocLink
explanation="Useful if you would like to rename the collector script to prevent it blocked by AdBlockers."
/></label
>
<input <input
name="scriptName" name="scriptName"
id="scriptName" id="scriptName"
@ -21,9 +25,6 @@
bind:value={service.plausibleAnalytics.scriptName} bind:value={service.plausibleAnalytics.scriptName}
required required
/> />
<Explainer
text="Useful if you would like to rename the collector script to prevent it blocked by AdBlockers."
/>
</div> </div>
<div class="grid grid-cols-2 items-center px-10"> <div class="grid grid-cols-2 items-center px-10">
<label for="email">{$t('forms.email')}</label> <label for="email">{$t('forms.email')}</label>

View File

@ -14,7 +14,6 @@
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { appSession, disabledButton, status, location, setLocation, addToast } from '$lib/store'; import { appSession, disabledButton, status, location, setLocation, addToast } from '$lib/store';
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte';
import Setting from '$lib/components/Setting.svelte'; import Setting from '$lib/components/Setting.svelte';
import Fider from './_Fider.svelte'; import Fider from './_Fider.svelte';
@ -31,6 +30,7 @@
import Moodle from './_Moodle.svelte'; import Moodle from './_Moodle.svelte';
import Searxng from './_Searxng.svelte'; import Searxng from './_Searxng.svelte';
import Weblate from './_Weblate.svelte'; import Weblate from './_Weblate.svelte';
import DocLink from '$lib/components/DocLink.svelte';
const { id } = $page.params; const { id } = $page.params;
$: isDisabled = $: isDisabled =
@ -283,8 +283,9 @@
</div> </div>
<div class="grid grid-cols-2 px-10"> <div class="grid grid-cols-2 px-10">
<div class="flex-col "> <div class="flex-col ">
<label for="apiFqdn" class="pt-2 text-base font-bold text-stone-100">API URL</label> <label for="apiFqdn" class="pt-2 text-base font-bold text-stone-100"
<Explainer text={$t('application.https_explainer')} /> >API URL <DocLink explanation={$t('application.https_explainer')} /></label
>
</div> </div>
<CopyPasswordField <CopyPasswordField
@ -302,9 +303,9 @@
<div class="grid grid-cols-2 px-10"> <div class="grid grid-cols-2 px-10">
<div class="flex-col "> <div class="flex-col ">
<label for="fqdn" class="pt-2 text-base font-bold text-stone-100" <label for="fqdn" class="pt-2 text-base font-bold text-stone-100"
>{$t('application.url_fqdn')}</label >{$t('application.url_fqdn')}
> <DocLink explanation={$t('application.https_explainer')} />
<Explainer text={$t('application.https_explainer')} /> </label>
</div> </div>
<CopyPasswordField <CopyPasswordField
@ -364,7 +365,11 @@
/> />
</div> </div>
<div class="grid grid-cols-2 items-center px-10"> <div class="grid grid-cols-2 items-center px-10">
<label for="exposePort" class="text-base font-bold text-stone-100">Exposed Port</label> <label for="exposePort" class="text-base font-bold text-stone-100"
>Exposed Port <DocLink
explanation={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
/></label
>
<input <input
readonly={!$appSession.isAdmin && !$status.service.isRunning} readonly={!$appSession.isAdmin && !$status.service.isRunning}
disabled={!$appSession.isAdmin || disabled={!$appSession.isAdmin ||
@ -375,9 +380,6 @@
bind:value={service.exposePort} bind:value={service.exposePort}
placeholder="12345" placeholder="12345"
/> />
<Explainer
text={'You can expose your application to a port on the host system.<br><br>Useful if you would like to use your own reverse proxy or tunnel and also in development mode. Otherwise leave empty.'}
/>
</div> </div>
{#if service.type === 'plausibleanalytics'} {#if service.type === 'plausibleanalytics'}

View File

@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import CopyPasswordField from '$lib/components/CopyPasswordField.svelte'; import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
import Explainer from '$lib/components/Explainer.svelte'; import DocLink from '$lib/components/DocLink.svelte';
export let service: any; export let service: any;
</script> </script>
@ -13,7 +12,11 @@
<input name="adminUser" id="adminUser" placeholder="admin" value="admin" disabled readonly /> <input name="adminUser" id="adminUser" placeholder="admin" value="admin" disabled readonly />
</div> </div>
<div class="grid grid-cols-2 items-center px-10"> <div class="grid grid-cols-2 items-center px-10">
<label for="umamiAdminPassword">Initial Admin Password</label> <label for="umamiAdminPassword"
>Initial Admin Password <DocLink
explanation="It could be changed in Umami. <br>This is just the password set initially after the first start."
/></label
>
<CopyPasswordField <CopyPasswordField
isPasswordField isPasswordField
name="umamiAdminPassword" name="umamiAdminPassword"
@ -23,7 +26,4 @@
disabled disabled
readonly readonly
/> />
<Explainer
text="It could be changed in Umami. <br>This is just the password set initially after the first start."
/>
</div> </div>

View File

@ -3,7 +3,6 @@
export let settings: any; export let settings: any;
import { page } from '$app/stores'; import { page } from '$app/stores';
import { getAPIUrl, getWebhookUrl, post } from '$lib/api'; import { getAPIUrl, getWebhookUrl, post } from '$lib/api';
import Explainer from '$lib/components/Explainer.svelte';
import { t } from '$lib/translations'; import { t } from '$lib/translations';
import { dashify, errorNotification, getDomain } from '$lib/common'; import { dashify, errorNotification, getDomain } from '$lib/common';
import { addToast, appSession } from '$lib/store'; import { addToast, appSession } from '$lib/store';
@ -116,9 +115,11 @@ import DocLink from '$lib/components/DocLink.svelte';
<input name="apiUrl" id="apiUrl" required bind:value={source.apiUrl} /> <input name="apiUrl" id="apiUrl" required bind:value={source.apiUrl} />
</div> </div>
<div class="grid lg:grid-cols-2 items-center"> <div class="grid lg:grid-cols-2 items-center">
<label for="customPort" class="text-base font-bold text-stone-100">Custom SSH Port <DocLink <label for="customPort" class="text-base font-bold text-stone-100"
explanation={"If you use a self-hosted version of Git, you can provide custom port for all the Git related actions."} >Custom SSH Port <DocLink
/></label> explanation={'If you use a self-hosted version of Git, you can provide custom port for all the Git related actions.'}
/></label
>
<input <input
name="customPort" name="customPort"
id="customPort" id="customPort"
@ -196,7 +197,9 @@ import DocLink from '$lib/components/DocLink.svelte';
{#if selfHosted} {#if selfHosted}
<div class="grid lg:grid-cols-2 items-center"> <div class="grid lg:grid-cols-2 items-center">
<label for="customPort" class="text-base font-bold text-stone-100" <label for="customPort" class="text-base font-bold text-stone-100"
>Custom SSH Port</label >Custom SSH Port <DocLink
explanation="If you use a self-hosted version of Git, you can provide custom port for all the Git related actions."
/></label
> >
<input <input
name="customPort" name="customPort"
@ -206,9 +209,6 @@ import DocLink from '$lib/components/DocLink.svelte';
required required
value={source.customPort} value={source.customPort}
/> />
<Explainer
text="If you use a self-hosted version of Git, you can provide custom port for all the Git related actions."
/>
</div> </div>
{/if} {/if}
<div class="grid lg:grid-cols-2"> <div class="grid lg:grid-cols-2">