@jjdenhertog/ai-driven-development
Version:
AI-driven development workflow with learning capabilities for Claude
110 lines (90 loc) • 3.33 kB
text/typescript
import { NextRequest, NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { prisma } from '@/lib/prisma';
import { redis } from '@/lib/redis';
import { z } from 'zod';
// Always define Zod schemas for validation
const createUserSchema = z.object({
name: z.string().min(1, 'Name is required').max(100),
email: z.string().email('Invalid email address'),
role: z.enum(['admin', 'user', 'guest']).default('user'),
active: z.boolean().default(true),
});
const updateUserSchema = createUserSchema.partial();
// Type inference from Zod schema
type CreateUserInput = z.infer<typeof createUserSchema>;
type UpdateUserInput = z.infer<typeof updateUserSchema>;
// GET /api/users - List all users with caching
export async function GET(request: NextRequest) {
try {
const session = await getServerSession(authOptions);
if (!session)
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
// Try cache first
const cached = await redis.get('users:all');
if (cached)
return NextResponse.json(JSON.parse(cached));
// Fetch from database
const users = await prisma.user.findMany({
where: { active: true },
orderBy: { createdAt: 'desc' },
select: {
id: true,
name: true,
email: true,
role: true,
active: true,
createdAt: true,
},
});
// Cache for 5 minutes
await redis.setex('users:all', 300, JSON.stringify(users));
return NextResponse.json(users);
} catch (error) {
console.error('Error fetching users:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}
// POST /api/users - Create new user
export async function POST(request: NextRequest) {
try {
const session = await getServerSession(authOptions);
if (!session)
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
// Only admins can create users
if (session.user.role !== 'admin')
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
const body = await request.json();
const validatedData = createUserSchema.parse(body);
// Check if email already exists
const existing = await prisma.user.findUnique({
where: { email: validatedData.email },
});
if (existing)
return NextResponse.json({ error: 'Email already exists' }, { status: 409 });
// Create user
const user = await prisma.user.create({
data: {
...validatedData,
createdBy: session.user.id,
},
});
// Invalidate cache
await redis.del('users:all');
return NextResponse.json(user, { status: 201 });
} catch (error) {
if (error instanceof z.ZodError)
return NextResponse.json({ error: 'Validation failed', details: error.errors }, { status: 400 });
console.error('Error creating user:', error);
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
}
}
// Helper function for rate limiting
async function checkRateLimit(identifier: string) {
const key = `rate_limit:api:users:${identifier}`;
const count = await redis.incr(key);
if (count === 1)
await redis.expire(key, 60);
return count <= 10; // 10 requests per minute
}