mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-19 04:49:37 +02:00
Refactor Docker Compose configuration and enhance email management in settings
This commit is contained in:
parent
64105808b5
commit
6a00a2ed55
8 changed files with 172 additions and 62 deletions
|
@ -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:
|
||||
|
|
|
@ -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: '/'
|
||||
// });
|
||||
}
|
||||
};
|
||||
|
|
94
frontend/src/routes/_allauth/[...path]/+server.ts
Normal file
94
frontend/src/routes/_allauth/[...path]/+server.ts
Normal file
|
@ -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 });
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1 class="text-center font-extrabold text-4xl mb-6">{$t('settings.settings_page')}</h1>
|
||||
|
@ -160,17 +178,21 @@
|
|||
|
||||
<h1 class="text-center font-extrabold text-xl mt-4 mb-2">{$t('settings.email_change')}</h1>
|
||||
<div class="flex justify-center">
|
||||
<form action="?/changeEmail" method="post" class="w-full max-w-xs">
|
||||
<label for="current_email">{$t('settings.current_email')}</label>
|
||||
<input
|
||||
type="email"
|
||||
name="current_email"
|
||||
placeholder={user.email || $t('settings.no_email_set')}
|
||||
id="current_email"
|
||||
readonly
|
||||
class="block mb-2 input input-bordered w-full max-w-xs"
|
||||
/>
|
||||
<br />
|
||||
<div>
|
||||
{#each emails as email}
|
||||
<p>
|
||||
{email.email}
|
||||
{email.verified ? '✅' : '❌'}
|
||||
{email.primary ? '🔑' : ''}
|
||||
<button class="btn btn-sm btn-warning" on:click={() => removeEmail(email)}>Remove</button>
|
||||
</p>
|
||||
{/each}
|
||||
{#if emails.length === 0}
|
||||
<p>No emails</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
type="email"
|
||||
name="new_email"
|
||||
|
@ -178,8 +200,10 @@
|
|||
id="new_email"
|
||||
class="block mb-2 input input-bordered w-full max-w-xs"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<button class="py-2 px-4 btn btn-primary mt-2">{$t('settings.email_change')}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center mt-4">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue