Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth Migration, Calendar and Other Misc. Fixes #406

Merged
merged 28 commits into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d7baf69
Bump @sveltejs/kit
dependabot[bot] Nov 25, 2024
9bc20be
Initial migration to new session based auth system with AllAuth
seanmorley15 Nov 29, 2024
f119e6f
Refactor settings.py to remove unused authentication configurations a…
seanmorley15 Nov 29, 2024
b86c725
Enhance admin security by integrating secure_admin_login from AllAuth…
seanmorley15 Nov 29, 2024
c65fcc2
Migrate to session based auth
seanmorley15 Nov 29, 2024
84566b8
User profile settings API and remove old Dj-Rest-Auth code
seanmorley15 Nov 30, 2024
50dc042
Refactor user serializers, update Docker configurations, and remove u…
seanmorley15 Dec 1, 2024
4ecad7f
Update nl.json
seanmorley15 Dec 2, 2024
dc29f48
Add 'Northern Lights' theme and update localization and Tailwind conf…
seanmorley15 Dec 2, 2024
21d8b37
Merge pull request #395 from seanmorley15/update-nl
seanmorley15 Dec 2, 2024
88dbd41
Merge pull request #375 from seanmorley15/dependabot/npm_and_yarn/fro…
seanmorley15 Dec 3, 2024
a39e22b
Add new dashboard
seanmorley15 Dec 3, 2024
d44cb06
Refactor AdventureCard usage and integrate event calendar components
seanmorley15 Dec 4, 2024
6410580
Add calendar route and integrate calendar component in Navbar
seanmorley15 Dec 4, 2024
6a00a2e
Refactor Docker Compose configuration and enhance email management in…
seanmorley15 Dec 7, 2024
0272c6b
Refactor user admin settings and enhance email management functionality
seanmorley15 Dec 12, 2024
2ccbf4b
Update email verification and password reset flows; refactor Docker C…
seanmorley15 Dec 12, 2024
54d7a1a
Update forgot password link to point to user reset-password route
seanmorley15 Dec 12, 2024
673a56c
Add MFA to login screen
seanmorley15 Dec 12, 2024
7b7db1c
Refactor email management and localization; update requirements and s…
seanmorley15 Dec 13, 2024
1b54f8e
Implement TOTP 2FA modal; add QR code generation and recovery codes m…
seanmorley15 Dec 13, 2024
9bf0849
Add multi-factor authentication (MFA) support; update localization an…
seanmorley15 Dec 14, 2024
0376709
Enhance localization support; update error messages and add translati…
seanmorley15 Dec 14, 2024
c0fd91e
Add emoji picker to category dropdown; implement toggle functionality…
seanmorley15 Dec 14, 2024
4839edd
Update password reset form and add more localization for error messages
seanmorley15 Dec 14, 2024
0c27f4b
Add download adventures as ICS calendar
seanmorley15 Dec 14, 2024
c966b73
Fix formatting in README.md for better readability
seanmorley15 Dec 14, 2024
10dbafd
Update button label in NewTransportation component to Create
seanmorley15 Dec 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Refactor Docker Compose configuration and enhance email management in…
… settings
  • Loading branch information
seanmorley15 committed Dec 7, 2024
commit 6a00a2ed55134e65770f32b1f256eaf06840f17d
8 changes: 4 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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:
Expand Down
24 changes: 0 additions & 24 deletions frontend/src/lib/index.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 94 additions & 0 deletions frontend/src/routes/_allauth/[...path]/+server.ts
Original file line number Diff line number Diff line change
@@ -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 });
}
}
5 changes: 4 additions & 1 deletion frontend/src/routes/api/[...path]/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
21 changes: 20 additions & 1 deletion frontend/src/routes/calendar/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
19 changes: 1 addition & 18 deletions frontend/src/routes/calendar/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/routes/settings/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
};
};
Expand Down
48 changes: 36 additions & 12 deletions frontend/src/routes/settings/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -160,26 +178,32 @@

<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"
placeholder={$t('settings.new_email')}
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">
Expand Down
Loading