refactor: consolidate formatting utilities and add testing infrastructure
- Replace 8 duplicate formatCurrency functions with shared utility - Add comprehensive formatter utilities (currency, date, number, etc.) - Set up Vitest for unit testing with 38 passing tests - Set up Playwright for E2E testing - Consolidate database connection to single source (src/utils/db.ts) - Add auth middleware helpers to reduce code duplication - Fix display bug: remove spurious minus sign in recent activity amounts - Add path aliases for cleaner imports ($utils, $models) - Add project documentation (CODEMAP.md, REFACTORING_PLAN.md) Test coverage: 38 unit tests passing Build: successful with no breaking changes
This commit is contained in:
81
src/lib/server/middleware/auth.ts
Normal file
81
src/lib/server/middleware/auth.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestEvent } from '@sveltejs/kit';
|
||||
|
||||
/**
|
||||
* User session information extracted from Auth.js
|
||||
*/
|
||||
export interface AuthenticatedUser {
|
||||
nickname: string;
|
||||
name?: string;
|
||||
email?: string;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require authentication for an API route.
|
||||
* Returns the authenticated user or throws an unauthorized response.
|
||||
*
|
||||
* @param locals - The RequestEvent locals object containing auth()
|
||||
* @returns The authenticated user
|
||||
* @throws Response with 401 status if not authenticated
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* export const GET: RequestHandler = async ({ locals }) => {
|
||||
* const user = await requireAuth(locals);
|
||||
* // user.nickname is guaranteed to exist here
|
||||
* return json({ message: `Hello ${user.nickname}` });
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
export async function requireAuth(
|
||||
locals: RequestEvent['locals']
|
||||
): Promise<AuthenticatedUser> {
|
||||
const session = await locals.auth();
|
||||
|
||||
if (!session || !session.user?.nickname) {
|
||||
throw json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
|
||||
return {
|
||||
nickname: session.user.nickname,
|
||||
name: session.user.name,
|
||||
email: session.user.email,
|
||||
image: session.user.image
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional authentication - returns user if authenticated, null otherwise.
|
||||
* Useful for routes that have different behavior for authenticated users.
|
||||
*
|
||||
* @param locals - The RequestEvent locals object containing auth()
|
||||
* @returns The authenticated user or null
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* export const GET: RequestHandler = async ({ locals }) => {
|
||||
* const user = await optionalAuth(locals);
|
||||
* if (user) {
|
||||
* return json({ message: `Hello ${user.nickname}`, isAuthenticated: true });
|
||||
* }
|
||||
* return json({ message: 'Hello guest', isAuthenticated: false });
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
export async function optionalAuth(
|
||||
locals: RequestEvent['locals']
|
||||
): Promise<AuthenticatedUser | null> {
|
||||
const session = await locals.auth();
|
||||
|
||||
if (!session || !session.user?.nickname) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
nickname: session.user.nickname,
|
||||
name: session.user.name,
|
||||
email: session.user.email,
|
||||
image: session.user.image
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user