From 6a00a2ed55134e65770f32b1f256eaf06840f17d Mon Sep 17 00:00:00 2001 From: Sean Morley Date: Sat, 7 Dec 2024 16:15:41 -0500 Subject: [PATCH] Refactor Docker Compose configuration and enhance email management in settings --- docker-compose.yml | 8 +- frontend/src/lib/index.server.ts | 24 ----- .../src/routes/_allauth/[...path]/+server.ts | 94 +++++++++++++++++++ frontend/src/routes/api/[...path]/+server.ts | 5 +- frontend/src/routes/calendar/+page.server.ts | 21 ++++- frontend/src/routes/calendar/+page.svelte | 19 +--- frontend/src/routes/settings/+page.server.ts | 15 ++- frontend/src/routes/settings/+page.svelte | 48 +++++++--- 8 files changed, 172 insertions(+), 62 deletions(-) create mode 100644 frontend/src/routes/_allauth/[...path]/+server.ts diff --git a/docker-compose.yml b/docker-compose.yml index 3f84851..d2ee82a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: web: - build: ./frontend/ - #image: ghcr.io/seanmorley15/adventurelog-frontend:latest + #build: ./frontend/ + image: ghcr.io/seanmorley15/adventurelog-frontend:latest container_name: adventurelog-frontend restart: unless-stopped environment: @@ -25,8 +25,8 @@ services: - postgres_data:/var/lib/postgresql/data/ server: - build: ./backend/ - #image: ghcr.io/seanmorley15/adventurelog-backend:latest + #build: ./backend/ + image: ghcr.io/seanmorley15/adventurelog-backend:latest container_name: adventurelog-backend restart: unless-stopped environment: diff --git a/frontend/src/lib/index.server.ts b/frontend/src/lib/index.server.ts index 3ff2206..12f9885 100644 --- a/frontend/src/lib/index.server.ts +++ b/frontend/src/lib/index.server.ts @@ -10,27 +10,3 @@ export const fetchCSRFToken = async () => { return null; } }; - -export const tryRefreshToken = async (refreshToken: string) => { - const csrfToken = await fetchCSRFToken(); - const refreshFetch = await fetch(`${serverEndpoint}/auth/token/refresh/`, { - method: 'POST', - headers: { - 'X-CSRFToken': csrfToken, - 'Content-Type': 'application/json' // Corrected header name - }, - body: JSON.stringify({ refresh: refreshToken }) - }); - - if (refreshFetch.ok) { - const refresh = await refreshFetch.json(); - const token = `auth=${refresh.access}`; - return token; - // event.cookies.set('auth', `auth=${refresh.access}`, { - // httpOnly: true, - // sameSite: 'lax', - // expires: new Date(Date.now() + 60 * 60 * 1000), // 60 minutes - // path: '/' - // }); - } -}; diff --git a/frontend/src/routes/_allauth/[...path]/+server.ts b/frontend/src/routes/_allauth/[...path]/+server.ts new file mode 100644 index 0000000..9261320 --- /dev/null +++ b/frontend/src/routes/_allauth/[...path]/+server.ts @@ -0,0 +1,94 @@ +const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; +const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; +import { fetchCSRFToken } from '$lib/index.server'; +import { json } from '@sveltejs/kit'; + +/** @type {import('./$types').RequestHandler} */ +export async function GET(event) { + const { url, params, request, fetch, cookies } = event; + const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; + return handleRequest(url, params, request, fetch, cookies, searchParam); +} + +/** @type {import('./$types').RequestHandler} */ +export async function POST({ url, params, request, fetch, cookies }) { + const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; + return handleRequest(url, params, request, fetch, cookies, searchParam, true); +} + +export async function PATCH({ url, params, request, fetch, cookies }) { + const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; + return handleRequest(url, params, request, fetch, cookies, searchParam, true); +} + +export async function PUT({ url, params, request, fetch, cookies }) { + const searchParam = url.search ? `${url.search}&format=json` : '?format=json'; + return handleRequest(url, params, request, fetch, cookies, searchParam, true); +} + +export async function DELETE({ url, params, request, fetch, cookies }) { + const searchParam = url.search ? `${url.search}` : ''; + return handleRequest(url, params, request, fetch, cookies, searchParam, false); +} + +async function handleRequest( + url: any, + params: any, + request: any, + fetch: any, + cookies: any, + searchParam: string, + requreTrailingSlash: boolean | undefined = false +) { + const path = params.path; + let targetUrl = `${endpoint}/_allauth/${path}`; + + // Ensure the path ends with a trailing slash + if (requreTrailingSlash && !targetUrl.endsWith('/')) { + targetUrl += '/'; + } + + // Append query parameters to the path correctly + targetUrl += searchParam; // This will add ?format=json or &format=json to the URL + + const headers = new Headers(request.headers); + + const csrfToken = await fetchCSRFToken(); + if (!csrfToken) { + return json({ error: 'CSRF token is missing or invalid' }, { status: 400 }); + } + + try { + const response = await fetch(targetUrl, { + method: request.method, + headers: { + ...Object.fromEntries(headers), + 'X-CSRFToken': csrfToken, + Cookie: `csrftoken=${csrfToken}` + }, + body: + request.method !== 'GET' && request.method !== 'HEAD' ? await request.text() : undefined, + credentials: 'include' // This line ensures cookies are sent with the request + }); + + if (response.status === 204) { + return new Response(null, { + status: 204, + headers: response.headers + }); + } + + const responseData = await response.text(); + // Create a new Headers object without the 'set-cookie' header + const cleanHeaders = new Headers(response.headers); + cleanHeaders.delete('set-cookie'); + + return new Response(responseData, { + status: response.status, + headers: cleanHeaders + }); + } catch (error) { + console.error('Error forwarding request:', error); + return json({ error: 'Internal Server Error' }, { status: 500 }); + } +} diff --git a/frontend/src/routes/api/[...path]/+server.ts b/frontend/src/routes/api/[...path]/+server.ts index ef6bbbf..33c2e2a 100644 --- a/frontend/src/routes/api/[...path]/+server.ts +++ b/frontend/src/routes/api/[...path]/+server.ts @@ -79,10 +79,13 @@ async function handleRequest( } const responseData = await response.text(); + // Create a new Headers object without the 'set-cookie' header + const cleanHeaders = new Headers(response.headers); + cleanHeaders.delete('set-cookie'); return new Response(responseData, { status: response.status, - headers: response.headers + headers: cleanHeaders }); } catch (error) { console.error('Error forwarding request:', error); diff --git a/frontend/src/routes/calendar/+page.server.ts b/frontend/src/routes/calendar/+page.server.ts index a7972e5..0305b93 100644 --- a/frontend/src/routes/calendar/+page.server.ts +++ b/frontend/src/routes/calendar/+page.server.ts @@ -12,9 +12,28 @@ export const load = (async (event) => { }); let adventures = (await visitedFetch.json()) as Adventure[]; + let dates: Array<{ + id: string; + start: string; + end: string; + title: string; + backgroundColor?: string; + }> = []; + adventures.forEach((adventure) => { + adventure.visits.forEach((visit) => { + dates.push({ + id: adventure.id, + start: visit.start_date, + end: visit.end_date || visit.start_date, + title: adventure.name + (adventure.category?.icon ? ' ' + adventure.category.icon : '') + }); + }); + }); + return { props: { - adventures + adventures, + dates } }; }) satisfies PageServerLoad; diff --git a/frontend/src/routes/calendar/+page.svelte b/frontend/src/routes/calendar/+page.svelte index abeca24..80f4327 100644 --- a/frontend/src/routes/calendar/+page.svelte +++ b/frontend/src/routes/calendar/+page.svelte @@ -11,24 +11,7 @@ export let data: PageData; let adventures = data.props.adventures; - - let dates: Array<{ - id: string; - start: string; - end: string; - title: string; - backgroundColor?: string; - }> = []; - adventures.forEach((adventure) => { - adventure.visits.forEach((visit) => { - dates.push({ - id: adventure.id, - start: visit.start_date, - end: visit.end_date, - title: adventure.name + ' ' + adventure.category?.icon - }); - }); - }); + let dates = data.props.dates; let plugins = [TimeGrid, DayGrid]; let options = { diff --git a/frontend/src/routes/settings/+page.server.ts b/frontend/src/routes/settings/+page.server.ts index 04537a4..f3ea702 100644 --- a/frontend/src/routes/settings/+page.server.ts +++ b/frontend/src/routes/settings/+page.server.ts @@ -20,13 +20,24 @@ export const load: PageServerLoad = async (event) => { }); let user = (await res.json()) as User; - if (!res.ok) { + let emailFetch = await fetch(`${endpoint}/_allauth/browser/v1/account/email`, { + headers: { + Cookie: `sessionid=${sessionId}` + } + }); + let emailResponse = (await emailFetch.json()) as { + status: number; + data: { email: string; verified: boolean; primary: boolean }[]; + }; + let emails = emailResponse.data; + if (!res.ok || !emailFetch.ok) { return redirect(302, '/'); } return { props: { - user + user, + emails } }; }; diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index d15d26b..7b73e9b 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -9,8 +9,10 @@ export let data; let user: User; + let emails: typeof data.props.emails; if (data.user) { user = data.user; + emails = data.props.emails; } onMount(async () => { @@ -48,6 +50,22 @@ addToast('error', $t('adventures.error_updating_regions')); } } + + async function removeEmail(email: { email: any; verified?: boolean; primary?: boolean }) { + let res = await fetch('/_allauth/browser/v1/account/email/', { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ email: email.email }) + }); + if (res.ok) { + addToast('success', 'Email removed'); + emails = emails.filter((e) => e.email !== email.email); + } else { + addToast('error', 'Error removing email'); + } + }

{$t('settings.settings_page')}

@@ -160,17 +178,21 @@

{$t('settings.email_change')}

-
- - -
+
+ {#each emails as email} +

+ {email.email} + {email.verified ? '✅' : '❌'} + {email.primary ? '🔑' : ''} + +

+ {/each} + {#if emails.length === 0} +

No emails

+ {/if} +
+ +
+
+
- +