creates application

This commit is contained in:
simon.franken
2026-02-16 10:15:27 +01:00
parent 791c661395
commit 7d678c1c4d
65 changed files with 10389 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
import { Request, Response, NextFunction } from 'express';
import { prisma } from '../prisma/client';
import type { AuthenticatedRequest, AuthenticatedUser } from '../types';
export function requireAuth(
req: AuthenticatedRequest,
res: Response,
next: NextFunction
): void {
if (!req.session?.user) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
req.user = req.session.user as AuthenticatedUser;
next();
}
export function optionalAuth(
req: AuthenticatedRequest,
res: Response,
next: NextFunction
): void {
if (req.session?.user) {
req.user = req.session.user as AuthenticatedUser;
}
next();
}
export async function syncUser(user: AuthenticatedUser): Promise<void> {
await prisma.user.upsert({
where: { id: user.id },
update: {
username: user.username,
email: user.email,
},
create: {
id: user.id,
username: user.username,
email: user.email,
},
});
}

View File

@@ -0,0 +1,54 @@
import { Request, Response, NextFunction } from 'express';
import { Prisma } from '@prisma/client';
export interface ApiError extends Error {
statusCode?: number;
code?: string;
}
export function errorHandler(
err: ApiError,
_req: Request,
res: Response,
_next: NextFunction
): void {
console.error('Error:', err);
// Prisma errors
if (err instanceof Prisma.PrismaClientKnownRequestError) {
switch (err.code) {
case 'P2002':
res.status(409).json({ error: 'Resource already exists' });
return;
case 'P2025':
res.status(404).json({ error: 'Resource not found' });
return;
case 'P2003':
res.status(400).json({ error: 'Invalid reference to related resource' });
return;
default:
res.status(500).json({ error: 'Database error' });
return;
}
}
if (err instanceof Prisma.PrismaClientValidationError) {
res.status(400).json({ error: 'Invalid data format' });
return;
}
const statusCode = err.statusCode || 500;
const message = err.message || 'Internal server error';
res.status(statusCode).json({
error: statusCode === 500 ? 'Internal server error' : message
});
}
export function notFoundHandler(
_req: Request,
res: Response,
_next: NextFunction
): void {
res.status(404).json({ error: 'Endpoint not found' });
}

View File

@@ -0,0 +1,51 @@
import { Request, Response, NextFunction } from 'express';
import { ZodSchema, ZodError } from 'zod';
export function validateBody<T>(schema: ZodSchema<T>) {
return (req: Request, res: Response, next: NextFunction): void => {
try {
req.body = schema.parse(req.body);
next();
} catch (error) {
if (error instanceof ZodError) {
const errors = error.errors.map(e => ({
path: e.path.join('.'),
message: e.message,
}));
res.status(400).json({ error: 'Validation failed', details: errors });
return;
}
next(error);
}
};
}
export function validateParams<T>(schema: ZodSchema<T>) {
return (req: Request, res: Response, next: NextFunction): void => {
try {
req.params = schema.parse(req.params) as typeof req.params;
next();
} catch (error) {
if (error instanceof ZodError) {
res.status(400).json({ error: 'Invalid parameters', details: error.errors });
return;
}
next(error);
}
};
}
export function validateQuery<T>(schema: ZodSchema<T>) {
return (req: Request, res: Response, next: NextFunction): void => {
try {
req.query = schema.parse(req.query) as typeof req.query;
next();
} catch (error) {
if (error instanceof ZodError) {
res.status(400).json({ error: 'Invalid query parameters', details: error.errors });
return;
}
next(error);
}
};
}