From da888ba1ba42e979984fa2af159843c1e8b35909 Mon Sep 17 00:00:00 2001 From: Eliott C Date: Thu, 15 Jun 2023 23:29:35 +0200 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20Add=20maintenance=20mode=20(#18?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.html | 4 +- src/error.html | 20 ++ src/hooks.server.ts | 22 +- src/lib/server/runtime-config.ts | 2 + src/lib/types/Picture.ts | 2 + src/routes/+layout.server.ts | 1 + src/routes/+layout.svelte | 311 ++++++++++++------------ src/routes/+server.ts | 8 - src/routes/admin/config/+page.server.ts | 30 ++- src/routes/admin/config/+page.svelte | 19 ++ src/routes/logo/+server.ts | 19 ++ 11 files changed, 259 insertions(+), 179 deletions(-) create mode 100644 src/error.html delete mode 100644 src/routes/+server.ts create mode 100644 src/routes/logo/+server.ts diff --git a/src/app.html b/src/app.html index effe0d0d2..2d06c872b 100644 --- a/src/app.html +++ b/src/app.html @@ -6,7 +6,7 @@ %sveltekit.head% - -
%sveltekit.body%
+ + %sveltekit.body% diff --git a/src/error.html b/src/error.html new file mode 100644 index 000000000..11b419299 --- /dev/null +++ b/src/error.html @@ -0,0 +1,20 @@ + + + + + %sveltekit.error.message% + + + + Message: %sveltekit.error.message% + Logo + + diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 3ebe39140..73258ad7b 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,11 +1,12 @@ import { ZodError } from 'zod'; -import type { HandleServerError, Handle } from '@sveltejs/kit'; +import { type HandleServerError, type Handle, error } from '@sveltejs/kit'; import { collections } from '$lib/server/database'; import { ObjectId } from 'mongodb'; import { addYears } from 'date-fns'; import '$lib/server/locks'; import { ADMIN_LOGIN, ADMIN_PASSWORD } from '$env/static/private'; +import { runtimeConfig } from '$lib/server/runtime-config'; export const handleError = (({ error, event }) => { console.error('handleError', error); @@ -43,11 +44,8 @@ export const handleError = (({ error, event }) => { }) satisfies HandleServerError; export const handle = (async ({ event, resolve }) => { - if ( - (event.url.pathname.startsWith('/admin/') || event.url.pathname === '/admin') && - ADMIN_LOGIN && - ADMIN_PASSWORD - ) { + const isAdminUrl = event.url.pathname.startsWith('/admin/') || event.url.pathname === '/admin'; + if (isAdminUrl && ADMIN_LOGIN && ADMIN_PASSWORD) { const authorization = event.request.headers.get('authorization'); if (!authorization?.startsWith('Basic ')) { @@ -73,6 +71,18 @@ export const handle = (async ({ event, resolve }) => { } } + if ( + runtimeConfig.isMaintenance && + !isAdminUrl && + event.url.pathname !== '/logo' && + !runtimeConfig.maintenanceIps.split(',').includes(event.getClientAddress()) + ) { + if (event.request.method !== 'GET') { + throw error(405, 'Site is in maintenance mode. Please try again later.'); + } + throw error(503, 'Site is in maintenance mode. Please try again later.'); + } + const token = event.cookies.get('bootik-session'); event.locals.sessionId = token || crypto.randomUUID(); diff --git a/src/lib/server/runtime-config.ts b/src/lib/server/runtime-config.ts index 999a0c313..b157dc0c7 100644 --- a/src/lib/server/runtime-config.ts +++ b/src/lib/server/runtime-config.ts @@ -5,6 +5,8 @@ const defaultConfig = { BTC_EUR: 30_000, orderNumber: 0, subscriptionNumber: 0, + isMaintenance: false, + maintenanceIps: '', brandName: 'My Space', subscriptionDuration: 'month' as 'month' | 'day' | 'hour', subscriptionReminderSeconds: 24 * 60 * 60, diff --git a/src/lib/types/Picture.ts b/src/lib/types/Picture.ts index 13ee11c59..c845705fd 100644 --- a/src/lib/types/Picture.ts +++ b/src/lib/types/Picture.ts @@ -17,3 +17,5 @@ export interface Picture extends Timestamps { formats: ImageData[]; }; } + +export const DEFAULT_LOGO = 'https://coyo.dev/icons/logo.png'; diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 7fcc6684e..3b89c2356 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -15,6 +15,7 @@ export async function load({ depends, locals }) { : null; return { + isMaintenance: runtimeConfig.isMaintenance, exchangeRate: runtimeConfig.BTC_EUR, brandName: runtimeConfig.brandName, logoPicture, diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 657718940..f2f7fe6ed 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -24,6 +24,7 @@ import Picture from '$lib/components/Picture.svelte'; import CartQuantity from '$lib/components/CartQuantity.svelte'; import IconTrash from '$lib/components/icons/IconTrash.svelte'; + import { DEFAULT_LOGO } from '$lib/types/Picture'; export let data; @@ -58,173 +59,171 @@ -
-
- - {#if data.logoPicture} - - {:else} - Main logo - {/if} - {data.brandName} - - - - {#if 0} -
- Connect your wallet - {/if} -
-
-
-
- - {#if 0} -
- - - - {/if} -
+
+
+
+
+ {#if 0} - -
- - -
-
+
+ + + {/if} -
- { - if (!data.cart || $page.url.pathname === '/checkout') { - return; - } - cartOpen = !cartOpen; - ev.preventDefault(); - }} - class="flex gap-2 items-center" - > - - {totalItems} - - {#if $productAddedToCart} - - ($productAddedToCart = null)} - product={$productAddedToCart.product} - picture={$productAddedToCart.picture} +
+ {#if 0} + +
+ + - - {:else if cartOpen} - -
- {#each items as item} -
{ - if (action.searchParams.has('/increase')) { - item.quantity++; - } else if (action.searchParams.has('/decrease')) { - item.quantity--; - } else if (action.searchParams.has('/remove')) { - item.quantity = 0; - } - actionCount++; - let currentCount = actionCount; +
+
+ {/if} +
+ { + if (!data.cart || $page.url.pathname === '/checkout') { + return; + } + cartOpen = !cartOpen; + ev.preventDefault(); + }} + class="flex gap-2 items-center" + > + + {totalItems} + + {#if $productAddedToCart} + + ($productAddedToCart = null)} + product={$productAddedToCart.product} + picture={$productAddedToCart.picture} + /> + + {:else if cartOpen} + +
+ {#each items as item} + { + if (action.searchParams.has('/increase')) { + item.quantity++; + } else if (action.searchParams.has('/decrease')) { + item.quantity--; + } else if (action.searchParams.has('/remove')) { + item.quantity = 0; + } + actionCount++; + let currentCount = actionCount; - return async ({ result }) => { - if (actionCount === currentCount) { - if (result.type === 'redirect') { - // Invalidate all to remove 0-quantity items - await goto(result.location, { noScroll: true, invalidateAll: true }); - return; + return async ({ result }) => { + if (actionCount === currentCount) { + if (result.type === 'redirect') { + // Invalidate all to remove 0-quantity items + await goto(result.location, { noScroll: true, invalidateAll: true }); + return; + } + await applyAction(result); } - await applyAction(result); - } - }; - }} - > -
- {#if item.picture} - + {#if item.picture} + + {/if} +
+
+

{item.product.name}

+ {#if item.product.type !== 'subscription'} +
+ Quantity: + +
+ {/if} +
+
+ - {/if} -
-
-

{item.product.name}

- {#if item.product.type !== 'subscription'} -
- Quantity: - -
- {/if} -
-
- - -
- - {/each} -
- Total + +
+ + {/each} +
+ Total +
+ View cart + {#if items.length > 0} Checkout {/if}
- View cart - {#if items.length > 0} Checkout {/if} -
- - {/if} + + {/if} +
+
+
+
- -
- -
- + + diff --git a/src/routes/+server.ts b/src/routes/+server.ts deleted file mode 100644 index 89a9498ca..000000000 --- a/src/routes/+server.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const GET = async () => { - const { runtimeConfig } = await import('$lib/server/runtime-config'); - return { - body: { - x: runtimeConfig.BTC_EUR - } - }; -}; diff --git a/src/routes/admin/config/+page.server.ts b/src/routes/admin/config/+page.server.ts index e93248a06..8fbc4afbf 100644 --- a/src/routes/admin/config/+page.server.ts +++ b/src/routes/admin/config/+page.server.ts @@ -5,6 +5,8 @@ import { z } from 'zod'; export async function load() { return { + isMaintenance: runtimeConfig.isMaintenance, + maintenanceIps: runtimeConfig.maintenanceIps, checkoutButtonOnProductPage: runtimeConfig.checkoutButtonOnProductPage, discovery: runtimeConfig.discovery, subscriptionDuration: runtimeConfig.subscriptionDuration, @@ -20,6 +22,8 @@ export const actions = { const result = z .object({ + isMaintenance: z.boolean({ coerce: true }), + maintenanceIps: z.string(), checkoutButtonOnProductPage: z.boolean({ coerce: true }), discovery: z.boolean({ coerce: true }), subscriptionDuration: z.enum(['month', 'day', 'hour']), @@ -30,13 +34,25 @@ export const actions = { .max(24 * 60 * 60 * 7), confirmationBlocks: z.number({ coerce: true }).int().min(0) }) - .parse({ - checkoutButtonOnProductPage: formData.get('checkoutButtonOnProductPage'), - discovery: formData.get('discovery'), - subscriptionDuration: formData.get('subscriptionDuration'), - subscriptionReminderSeconds: formData.get('subscriptionReminderSeconds'), - confirmationBlocks: formData.get('confirmationBlocks') - }); + .parse(Object.fromEntries(formData)); + + if (runtimeConfig.isMaintenance !== result.isMaintenance) { + runtimeConfig.isMaintenance = result.isMaintenance; + await collections.runtimeConfig.updateOne( + { _id: 'isMaintenance' }, + { $set: { data: result.isMaintenance, updatedAt: new Date() } }, + { upsert: true } + ); + } + + if (runtimeConfig.maintenanceIps !== result.maintenanceIps) { + runtimeConfig.maintenanceIps = result.maintenanceIps; + await collections.runtimeConfig.updateOne( + { _id: 'maintenanceIps' }, + { $set: { data: result.maintenanceIps, updatedAt: new Date() } }, + { upsert: true } + ); + } if (runtimeConfig.checkoutButtonOnProductPage !== result.checkoutButtonOnProductPage) { runtimeConfig.checkoutButtonOnProductPage = result.checkoutButtonOnProductPage; diff --git a/src/routes/admin/config/+page.svelte b/src/routes/admin/config/+page.svelte index d6b92034e..be404334a 100644 --- a/src/routes/admin/config/+page.svelte +++ b/src/routes/admin/config/+page.svelte @@ -29,6 +29,25 @@ /> discovery + +