@drop-in/new
Version:
A SvelteKit Svelte 5 PocketBase Starter Kit with a CLI
235 lines (211 loc) • 6.14 kB
text/typescript
import type { RequestEvent, Handle } from '@sveltejs/kit';
import { sign_up } from './sign_up.js';
import { login } from './login.js';
import { logout } from './logout.js';
import { parseFormData } from 'parse-nested-form-data';
import { cookie_options, jwt_cookie_options } from './cookies.js';
import { create_expiring_auth_digest } from './utils.js';
import { eq } from 'drizzle-orm';
import { db } from './db.js';
import { user } from './schema.js';
import { send_verification_email } from './email.js';
type FormData = {
email?: string;
password?: string;
user_id?: string;
token?: string;
expire?: number;
};
export async function sign_up_route(event: RequestEvent, data: FormData) {
if (!data.email || !data.password) {
return new Response(JSON.stringify({ error: 'Email and password are required' }), {
status: 400,
headers: {
'Content-Type': 'application/json',
},
});
}
const sign_up_response = await sign_up(data.email, data.password);
if (sign_up_response?.refresh_token && sign_up_response?.jwt) {
const { refresh_token, jwt } = sign_up_response;
const refresh_token_cookie = event.cookies.serialize(
'refresh_token',
refresh_token,
cookie_options,
);
return new Response(JSON.stringify({ status: 'success', jwt }), {
status: 200,
headers: {
'Content-Type': 'application/json',
'Set-Cookie': refresh_token_cookie,
},
});
}
return new Response(JSON.stringify({ status: 'error', error: 'Signup Failed' }), {
status: 400,
headers: {
'Content-Type': 'application/json',
},
});
}
export async function login_route(event: RequestEvent, data: FormData) {
if (!data.email || !data.password) {
return new Response(
JSON.stringify({ status: 'error', error: 'Email and password are required' }),
{
headers: {
'Content-Type': 'application/json',
},
status: 400,
},
);
}
const login_response = await login(data.email, data.password);
if (login_response?.refresh_token && login_response?.jwt) {
const { refresh_token, jwt } = login_response;
const refresh_token_cookie = event.cookies.serialize(
'refresh_token',
refresh_token,
cookie_options,
);
return new Response(JSON.stringify({ status: 'success', jwt }), {
status: 200,
headers: {
'Content-Type': 'application/json',
'Set-Cookie': refresh_token_cookie,
},
});
}
return new Response(JSON.stringify({ status: 'error', error: 'Login Failed' }), {
status: 400,
headers: {
'Content-Type': 'application/json',
},
});
}
export async function logout_route(event: RequestEvent) {
// Get the refresh_token from the request
const refresh_token = event.cookies.get('refresh_token');
const jwt = event.cookies.get('jwt');
if (!refresh_token || !jwt) {
return new Response('No refresh token or JWT found', {
status: 400,
});
}
await logout(refresh_token, jwt);
const refresh_token_cookie = event.cookies.serialize('refresh_token', '', {
...cookie_options,
maxAge: 0,
});
const jwt_cookie = event.cookies.serialize('jwt', '', {
...jwt_cookie_options,
maxAge: 0,
});
return new Response('Success', {
status: 200,
headers: {
'Content-Type': 'text/plain',
'Set-Cookie': `${refresh_token_cookie}, ${jwt_cookie}`,
},
});
}
/**
* Handles the email verification route.
*
* @param event - The event object
* @param data - The form data
* @returns The response object
*/
export async function verify_email_route(event: RequestEvent, data: FormData) {
const verification_token = data.token;
const email = data.email;
const expire = data.expire;
if (!verification_token || !email || !expire) {
return new Response('Invalid token', {
status: 400,
});
}
const test_token = create_expiring_auth_digest(email, expire);
console.log('test_token', test_token);
if (verification_token !== test_token) {
return new Response('Invalid token', {
status: 400,
});
}
// Check to make sure it's not expired.
if (Date.now() > expire) {
return new Response('Token expired', {
status: 400,
});
}
// Verify the user.
await db
.update(user)
.set({ verified: true, verification_token: null })
.where(eq(user.email, email))
.execute();
// Redirect to home.
return new Response('Success', {
status: 302,
headers: {
Location: '/',
},
});
}
export async function send_verify_email_route(event: RequestEvent, data: FormData) {
const user_id = data.user_id;
if (!user_id) {
return new Response('User ID is required', {
status: 400,
});
}
try {
await send_verification_email(user_id);
} catch (error) {
return new Response('Error', {
status: 500,
});
}
return new Response('Success', {
status: 200,
});
}
/**
* Handles the authentication routes.
*
* @param event - The event object
* @param resolve - The resolve function
* @returns The response object
*/
export const pass_routes: Handle = async ({ event, resolve }) => {
const { url } = event;
// Check if the URL matches your auth routes
if (url.pathname.startsWith('/api/auth')) {
// Make a clone to prevent error in already read body
const request_2 = event.request.clone();
let data = {};
// Check if the content type is multipart/form-data
if (request_2.headers.get('content-type')?.includes('multipart/form-data')) {
// Get form data
const form_data = await request_2.formData();
// Parse that ish
data = parseFormData(form_data);
}
if (url.pathname === '/api/auth/login') {
return login_route(event, data);
} else if (url.pathname === '/api/auth/register') {
return sign_up_route(event, data);
} else if (url.pathname === '/api/auth/logout') {
return logout_route(event);
} else if (url.pathname === '/api/auth/verify-email') {
return verify_email_route(event, data);
} else if (url.pathname === '/api/auth/send-verify-email') {
return send_verify_email_route(event, data);
}
// Return 404 for unhandled auth routes
return new Response(JSON.stringify({ error: 'Not Found' }), { status: 404 });
}
// If it's not an auth route, continue with the next handler
return resolve(event);
};
// TODO magic link auth?