feat: Basic password reset form
This commit is contained in:
		@@ -2,7 +2,7 @@
 | 
			
		||||
	import type { Load } from '@sveltejs/kit';
 | 
			
		||||
	import { publicPaths } from '$lib/settings';
 | 
			
		||||
 | 
			
		||||
	export const load: Load = async ({ fetch, url, params, session }) => {
 | 
			
		||||
	export const load: Load = async ({ fetch, url, session }) => {
 | 
			
		||||
		if (!session.userId && !publicPaths.includes(url.pathname)) {
 | 
			
		||||
			return {
 | 
			
		||||
				status: 302,
 | 
			
		||||
 
 | 
			
		||||
@@ -67,6 +67,7 @@
 | 
			
		||||
						class:text-stone-600={loading}
 | 
			
		||||
						class:bg-coollabs={!loading}>{loading ? 'Authenticating...' : 'Login'}</button
 | 
			
		||||
					>
 | 
			
		||||
					<button on:click|preventDefault={() => goto('/reset')}>Reset password</button>
 | 
			
		||||
				</div>
 | 
			
		||||
			</form>
 | 
			
		||||
		</div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								src/routes/reset/index.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/routes/reset/index.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
			
		||||
import * as db from '$lib/database';
 | 
			
		||||
 | 
			
		||||
export const get: RequestHandler = async () => {
 | 
			
		||||
	const users = await db.prisma.user.findMany({});
 | 
			
		||||
	return {
 | 
			
		||||
		status: 200,
 | 
			
		||||
		body: {
 | 
			
		||||
			users
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
export const post: RequestHandler = async (event) => {
 | 
			
		||||
	const { secretKey } = await event.request.json();
 | 
			
		||||
	if (secretKey !== process.env.COOLIFY_SECRET_KEY) {
 | 
			
		||||
		return {
 | 
			
		||||
			status: 500,
 | 
			
		||||
			body: {
 | 
			
		||||
				error: 'Invalid secret key.'
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
	return {
 | 
			
		||||
		status: 200
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										96
									
								
								src/routes/reset/index.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/routes/reset/index.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	import { goto } from '$app/navigation';
 | 
			
		||||
	import { get, post } from '$lib/api';
 | 
			
		||||
	import CopyPasswordField from '$lib/components/CopyPasswordField.svelte';
 | 
			
		||||
	import { errorNotification } from '$lib/form';
 | 
			
		||||
	import { toast } from '@zerodevx/svelte-toast';
 | 
			
		||||
 | 
			
		||||
	let secretKey;
 | 
			
		||||
	let password = false;
 | 
			
		||||
	let users = [];
 | 
			
		||||
 | 
			
		||||
	async function handleSubmit() {
 | 
			
		||||
		try {
 | 
			
		||||
			await post(`/reset.json`, { secretKey });
 | 
			
		||||
			password = true;
 | 
			
		||||
			const data = await get('/reset.json');
 | 
			
		||||
			users = data.users;
 | 
			
		||||
			return;
 | 
			
		||||
		} catch ({ error }) {
 | 
			
		||||
			return errorNotification(error);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	async function resetPassword(user) {
 | 
			
		||||
		try {
 | 
			
		||||
			await post(`/reset/password.json`, { secretKey, user });
 | 
			
		||||
			toast.push('Password reset done.');
 | 
			
		||||
			return;
 | 
			
		||||
		} catch ({ error }) {
 | 
			
		||||
			return errorNotification(error);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="icons fixed top-0 left-0 m-3 cursor-pointer" on:click={() => goto('/')}>
 | 
			
		||||
	<svg
 | 
			
		||||
		xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
		class="h-6 w-6"
 | 
			
		||||
		viewBox="0 0 24 24"
 | 
			
		||||
		stroke-width="1.5"
 | 
			
		||||
		stroke="currentColor"
 | 
			
		||||
		fill="none"
 | 
			
		||||
		stroke-linecap="round"
 | 
			
		||||
		stroke-linejoin="round"
 | 
			
		||||
	>
 | 
			
		||||
		<path stroke="none" d="M0 0h24v24H0z" fill="none" />
 | 
			
		||||
		<line x1="5" y1="12" x2="19" y2="12" />
 | 
			
		||||
		<line x1="5" y1="12" x2="11" y2="18" />
 | 
			
		||||
		<line x1="5" y1="12" x2="11" y2="6" />
 | 
			
		||||
	</svg>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="pb-10 pt-24 text-center text-4xl font-bold">Reset Password</div>
 | 
			
		||||
<div class="flex items-center justify-center">
 | 
			
		||||
	{#if password}
 | 
			
		||||
		<table class="mx-2 text-left">
 | 
			
		||||
			<thead class="mb-2">
 | 
			
		||||
				<tr>
 | 
			
		||||
					<th class="px-2">Email</th>
 | 
			
		||||
					<th>New password</th>
 | 
			
		||||
				</tr>
 | 
			
		||||
			</thead>
 | 
			
		||||
			<tbody>
 | 
			
		||||
				{#each users as user}
 | 
			
		||||
					<tr>
 | 
			
		||||
						<td class="px-2">{user.email}</td>
 | 
			
		||||
						<td class="flex space-x-2">
 | 
			
		||||
							<input
 | 
			
		||||
								id="newPassword"
 | 
			
		||||
								name="newPassword"
 | 
			
		||||
								bind:value={user.newPassword}
 | 
			
		||||
								placeholder="Super secure new password"
 | 
			
		||||
							/>
 | 
			
		||||
							<button
 | 
			
		||||
								class="mx-auto my-4 w-32 bg-coollabs hover:bg-coollabs-100"
 | 
			
		||||
								on:click={() => resetPassword(user)}>Reset</button
 | 
			
		||||
							></td
 | 
			
		||||
						>
 | 
			
		||||
					</tr>
 | 
			
		||||
				{/each}
 | 
			
		||||
			</tbody>
 | 
			
		||||
		</table>
 | 
			
		||||
	{:else}
 | 
			
		||||
		<form class="flex flex-col" on:submit|preventDefault={handleSubmit}>
 | 
			
		||||
			<div class="text-center text-2xl py-2 font-bold">Secret Key</div>
 | 
			
		||||
			<CopyPasswordField
 | 
			
		||||
				isPasswordField={true}
 | 
			
		||||
				id="secretKey"
 | 
			
		||||
				name="secretKey"
 | 
			
		||||
				bind:value={secretKey}
 | 
			
		||||
				placeholder="You can find it in ~/coolify/.env (COOLIFY_SECRET_KEY)"
 | 
			
		||||
			/>
 | 
			
		||||
			<button type="submit" class="bg-coollabs hover:bg-coollabs-100 mx-auto w-32 my-4"
 | 
			
		||||
				>Submit</button
 | 
			
		||||
			>
 | 
			
		||||
		</form>
 | 
			
		||||
	{/if}
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										27
									
								
								src/routes/reset/password.json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/routes/reset/password.json.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
import type { RequestHandler } from '@sveltejs/kit';
 | 
			
		||||
import * as db from '$lib/database';
 | 
			
		||||
import { ErrorHandler, hashPassword } from '$lib/database';
 | 
			
		||||
 | 
			
		||||
export const post: RequestHandler = async (event) => {
 | 
			
		||||
	const { secretKey, user } = await event.request.json();
 | 
			
		||||
	if (secretKey !== process.env.COOLIFY_SECRET_KEY) {
 | 
			
		||||
		return {
 | 
			
		||||
			status: 500,
 | 
			
		||||
			body: {
 | 
			
		||||
				error: 'Invalid secret key.'
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
	try {
 | 
			
		||||
		const hashedPassword = await hashPassword(user.newPassword);
 | 
			
		||||
		await db.prisma.user.update({
 | 
			
		||||
			where: { email: user.email },
 | 
			
		||||
			data: { password: hashedPassword }
 | 
			
		||||
		});
 | 
			
		||||
		return {
 | 
			
		||||
			status: 200
 | 
			
		||||
		};
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		return ErrorHandler(error);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user