MeetUp/src/app/api/search/user/route.ts
Dominik Stahl 476114ca87
Some checks failed
container-scan / Container Scan (pull_request) Failing after 7m35s
docker-build / docker (pull_request) Failing after 11m56s
feat(api): add user search endpoint and normalize response data and validation
2025-06-17 21:50:52 +02:00

154 lines
4 KiB
TypeScript

import { auth } from '@/auth';
import { NextResponse } from 'next/server';
import { prisma } from '@/prisma';
import { z } from 'zod/v4';
export const getSearchUserSchema = z.object({
query: z.string().optional().default(''),
count: z.int().min(1).max(100).default(10),
page: z.int().min(1).default(1),
sort_by: z
.enum(['created_at', 'name', 'first_name', 'last_name', 'id'])
.optional()
.default('created_at'),
sort_order: z.enum(['asc', 'desc']).optional().default('desc'),
});
/**
* @swagger
* /api/search/user:
* get:
* summary: Search for users
* description: Search for users by name, first name, or last name with pagination and sorting.
* tags:
* - Search
* parameters:
* - in: query
* name: query
* required: false
* schema:
* type: string
* description: The search query to filter users by name, first name, or last name.
* - in: query
* name: count
* required: false
* schema:
* type: integer
* description: The number of users to return per page (default is 10).
* - in: query
* name: page
* required: false
* schema:
* type: integer
* description: The page number to return (default is 1).
* - in: query
* name: sort_by
* required: false
* schema:
* type: string
* enum: ['created_at', 'name', 'first_name', 'last_name', 'id']
* description: The field to sort by (default is 'created_at').
* - in: query
* name: sort_order
* required: false
* schema:
* type: string
* enum: ['asc', 'desc']
* description: The order to sort by, either 'asc' or 'desc' (default is 'desc').
* responses:
* 200:
* description: Users found successfully.
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* users:
* type: array
* items:
* $ref: '#/components/schemas/PublicUser'
* count:
* type: integer
* description: The number of users returned.
* 400:
* description: Invalid request data.
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ZodErrorResponse'
* 401:
* description: User is not authenticated.
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* example:
* success: false
* message: 'Not authenticated'
* 404:
* description: User not found.
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* example:
* success: false
* message: 'User not found'
*/
export const GET = auth(async function GET(req) {
if (!req.auth)
return NextResponse.json(
{ success: false, message: 'Not authenticated' },
{ status: 401 },
);
if (!req.auth.user || !req.auth.user.id)
return NextResponse.json(
{ success: false, message: 'User not found' },
{ status: 404 },
);
const dataRaw = new URL(req.url).searchParams;
const data = await getSearchUserSchema.safeParseAsync(dataRaw);
if (!data.success) {
return NextResponse.json(
{
success: false,
message: 'Invalid request data',
errors: data.error.issues,
},
{ status: 400 },
);
}
const { query, count, page, sort_by, sort_order } = data.data;
const dbUsers = await prisma.user.findMany({
where: {
OR: [
{ name: { contains: query } },
{ first_name: { contains: query } },
{ last_name: { contains: query } },
],
},
orderBy: {
[sort_by]: sort_order,
},
skip: (page - 1) * count,
take: count,
select: {
id: true,
name: true,
first_name: true,
last_name: true,
timezone: true,
image: true,
},
});
return NextResponse.json({
success: true,
users: dbUsers,
count: dbUsers.length,
});
});