UNPKG

@restnfeel/agentc-starter-kit

Version:

한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템

559 lines (495 loc) 16.7 kB
import { NextRequest, NextResponse } from "next/server"; import { getServerSession } from "next-auth"; import { PrismaClient } from "@prisma/client"; import { z } from "zod"; import { UserService } from "./service"; import { authConfig } from "@/auth/config"; import { hasPermission } from "@/auth/rbac"; import { CreateUserRequest, UpdateUserRequest, UserListFilters, UserListSort, PaginationParams, PasswordResetRequest, PasswordResetConfirm, EmailVerificationRequest, EmailVerificationConfirm, AccountUnlockRequest, ProfileUpdateRequest, BulkUserOperation, Role, } from "./types"; const prisma = new PrismaClient(); const userService = new UserService(prisma); // 유효성 검사 스키마 const createUserSchema = z.object({ email: z.string().email("유효한 이메일을 입력해주세요"), password: z.string().min(8, "비밀번호는 최소 8자 이상이어야 합니다"), name: z.string().min(1, "이름을 입력해주세요"), role: z.nativeEnum(Role).optional(), tenantId: z.string().optional(), sendVerificationEmail: z.boolean().optional(), }); const updateUserSchema = z.object({ name: z.string().min(1).optional(), email: z.string().email().optional(), role: z.nativeEnum(Role).optional(), isActive: z.boolean().optional(), emailVerified: z.date().nullable().optional(), tenantId: z.string().optional(), }); const passwordResetRequestSchema = z.object({ email: z.string().email("유효한 이메일을 입력해주세요"), }); const passwordResetConfirmSchema = z.object({ token: z.string().min(1, "토큰이 필요합니다"), newPassword: z.string().min(8, "비밀번호는 최소 8자 이상이어야 합니다"), }); const emailVerificationRequestSchema = z.object({ email: z.string().email("유효한 이메일을 입력해주세요"), }); const emailVerificationConfirmSchema = z.object({ token: z.string().min(1, "토큰이 필요합니다"), }); const profileUpdateSchema = z.object({ name: z.string().min(1).optional(), email: z.string().email().optional(), currentPassword: z.string().optional(), newPassword: z.string().min(8).optional(), }); const bulkOperationSchema = z.object({ userIds: z.array(z.string()), operation: z.enum([ "activate", "deactivate", "delete", "unlock", "change_role", ]), data: z .object({ role: z.nativeEnum(Role).optional(), }) .optional(), }); // 사용자 목록 조회 export async function getUsersHandler(request: NextRequest) { try { const session = await getServerSession(authConfig); if (!session?.user) { return NextResponse.json({ error: "인증이 필요합니다" }, { status: 401 }); } if (!hasPermission(session.user, "USER_READ")) { return NextResponse.json({ error: "권한이 없습니다" }, { status: 403 }); } const { searchParams } = new URL(request.url); // 필터 파라미터 파싱 const filters: UserListFilters = { search: searchParams.get("search") || undefined, role: (searchParams.get("role") as Role) || undefined, isActive: searchParams.get("isActive") ? searchParams.get("isActive") === "true" : undefined, emailVerified: searchParams.get("emailVerified") ? searchParams.get("emailVerified") === "true" : undefined, tenantId: searchParams.get("tenantId") || undefined, createdAfter: searchParams.get("createdAfter") ? new Date(searchParams.get("createdAfter")!) : undefined, createdBefore: searchParams.get("createdBefore") ? new Date(searchParams.get("createdBefore")!) : undefined, lastLoginAfter: searchParams.get("lastLoginAfter") ? new Date(searchParams.get("lastLoginAfter")!) : undefined, lastLoginBefore: searchParams.get("lastLoginBefore") ? new Date(searchParams.get("lastLoginBefore")!) : undefined, }; // 정렬 파라미터 파싱 const sort: UserListSort = { field: (searchParams.get("sortField") as keyof UserListSort["field"]) || "createdAt", direction: (searchParams.get("sortDirection") as "asc" | "desc") || "desc", }; // 페이지네이션 파라미터 파싱 const pagination: PaginationParams = { page: parseInt(searchParams.get("page") || "1"), limit: parseInt(searchParams.get("limit") || "20"), }; const result = await userService.getUsers(filters, sort, pagination); return NextResponse.json(result); } catch (error) { console.error("사용자 목록 조회 오류:", error); return NextResponse.json( { error: "사용자 목록을 조회하는 중 오류가 발생했습니다" }, { status: 500 } ); } } // 사용자 생성 export async function createUserHandler(request: NextRequest) { try { const session = await getServerSession(authConfig); if (!session?.user) { return NextResponse.json({ error: "인증이 필요합니다" }, { status: 401 }); } if (!hasPermission(session.user, "USER_CREATE")) { return NextResponse.json({ error: "권한이 없습니다" }, { status: 403 }); } const body = await request.json(); const validatedData = createUserSchema.parse(body); const user = await userService.createUser( validatedData as CreateUserRequest, session.user.id ); return NextResponse.json(user, { status: 201 }); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: "유효하지 않은 데이터", details: error.errors }, { status: 400 } ); } console.error("사용자 생성 오류:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "사용자 생성 중 오류가 발생했습니다", }, { status: 500 } ); } } // 사용자 조회 export async function getUserHandler( request: NextRequest, { params }: { params: { id: string } } ) { try { const session = await getServerSession(authConfig); if (!session?.user) { return NextResponse.json({ error: "인증이 필요합니다" }, { status: 401 }); } if (!hasPermission(session.user, "USER_READ")) { return NextResponse.json({ error: "권한이 없습니다" }, { status: 403 }); } const { searchParams } = new URL(request.url); const includeTenant = searchParams.get("includeTenant") === "true"; const includeStats = searchParams.get("includeStats") === "true"; const user = await userService.getUserById(params.id, { includeTenant, includeStats, }); if (!user) { return NextResponse.json( { error: "사용자를 찾을 수 없습니다" }, { status: 404 } ); } return NextResponse.json(user); } catch (error) { console.error("사용자 조회 오류:", error); return NextResponse.json( { error: "사용자 조회 중 오류가 발생했습니다" }, { status: 500 } ); } } // 사용자 업데이트 export async function updateUserHandler( request: NextRequest, { params }: { params: { id: string } } ) { try { const session = await getServerSession(authConfig); if (!session?.user) { return NextResponse.json({ error: "인증이 필요합니다" }, { status: 401 }); } if (!hasPermission(session.user, "USER_UPDATE")) { return NextResponse.json({ error: "권한이 없습니다" }, { status: 403 }); } const body = await request.json(); const validatedData = updateUserSchema.parse(body); const user = await userService.updateUser( params.id, validatedData as UpdateUserRequest, session.user.id ); return NextResponse.json(user); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: "유효하지 않은 데이터", details: error.errors }, { status: 400 } ); } console.error("사용자 업데이트 오류:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "사용자 업데이트 중 오류가 발생했습니다", }, { status: 500 } ); } } // 사용자 삭제 export async function deleteUserHandler( request: NextRequest, { params }: { params: { id: string } } ) { try { const session = await getServerSession(authConfig); if (!session?.user) { return NextResponse.json({ error: "인증이 필요합니다" }, { status: 401 }); } if (!hasPermission(session.user, "USER_DELETE")) { return NextResponse.json({ error: "권한이 없습니다" }, { status: 403 }); } await userService.deleteUser(params.id, session.user.id); return NextResponse.json({ message: "사용자가 삭제되었습니다" }); } catch (error) { console.error("사용자 삭제 오류:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "사용자 삭제 중 오류가 발생했습니다", }, { status: 500 } ); } } // 비밀번호 재설정 요청 export async function requestPasswordResetHandler(request: NextRequest) { try { const body = await request.json(); const validatedData = passwordResetRequestSchema.parse(body); await userService.requestPasswordReset( validatedData as PasswordResetRequest ); return NextResponse.json({ message: "비밀번호 재설정 이메일이 발송되었습니다", }); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: "유효하지 않은 데이터", details: error.errors }, { status: 400 } ); } console.error("비밀번호 재설정 요청 오류:", error); return NextResponse.json( { error: "비밀번호 재설정 요청 중 오류가 발생했습니다" }, { status: 500 } ); } } // 비밀번호 재설정 확인 export async function confirmPasswordResetHandler(request: NextRequest) { try { const body = await request.json(); const validatedData = passwordResetConfirmSchema.parse(body); await userService.confirmPasswordReset( validatedData as PasswordResetConfirm ); return NextResponse.json({ message: "비밀번호가 재설정되었습니다" }); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: "유효하지 않은 데이터", details: error.errors }, { status: 400 } ); } console.error("비밀번호 재설정 확인 오류:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "비밀번호 재설정 중 오류가 발생했습니다", }, { status: 500 } ); } } // 이메일 인증 요청 export async function requestEmailVerificationHandler(request: NextRequest) { try { const body = await request.json(); const validatedData = emailVerificationRequestSchema.parse(body); await userService.requestEmailVerification( validatedData as EmailVerificationRequest ); return NextResponse.json({ message: "이메일 인증 메일이 발송되었습니다" }); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: "유효하지 않은 데이터", details: error.errors }, { status: 400 } ); } console.error("이메일 인증 요청 오류:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "이메일 인증 요청 중 오류가 발생했습니다", }, { status: 500 } ); } } // 이메일 인증 확인 export async function confirmEmailVerificationHandler(request: NextRequest) { try { const body = await request.json(); const validatedData = emailVerificationConfirmSchema.parse(body); await userService.confirmEmailVerification( validatedData as EmailVerificationConfirm ); return NextResponse.json({ message: "이메일이 인증되었습니다" }); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: "유효하지 않은 데이터", details: error.errors }, { status: 400 } ); } console.error("이메일 인증 확인 오류:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "이메일 인증 중 오류가 발생했습니다", }, { status: 500 } ); } } // 계정 잠금 해제 export async function unlockAccountHandler(request: NextRequest) { try { const session = await getServerSession(authConfig); if (!session?.user) { return NextResponse.json({ error: "인증이 필요합니다" }, { status: 401 }); } if (!hasPermission(session.user, "USER_UPDATE")) { return NextResponse.json({ error: "권한이 없습니다" }, { status: 403 }); } const body = await request.json(); const { userId, reason } = body; await userService.unlockAccount( { userId, reason } as AccountUnlockRequest, session.user.id ); return NextResponse.json({ message: "계정 잠금이 해제되었습니다" }); } catch (error) { console.error("계정 잠금 해제 오류:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "계정 잠금 해제 중 오류가 발생했습니다", }, { status: 500 } ); } } // 프로필 업데이트 export async function updateProfileHandler(request: NextRequest) { try { const session = await getServerSession(authConfig); if (!session?.user) { return NextResponse.json({ error: "인증이 필요합니다" }, { status: 401 }); } const body = await request.json(); const validatedData = profileUpdateSchema.parse(body); const user = await userService.updateProfile( session.user.id, validatedData as ProfileUpdateRequest ); return NextResponse.json(user); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: "유효하지 않은 데이터", details: error.errors }, { status: 400 } ); } console.error("프로필 업데이트 오류:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "프로필 업데이트 중 오류가 발생했습니다", }, { status: 500 } ); } } // 사용자 통계 조회 export async function getUserStatsHandler(request: NextRequest) { try { const session = await getServerSession(authConfig); if (!session?.user) { return NextResponse.json({ error: "인증이 필요합니다" }, { status: 401 }); } if (!hasPermission(session.user, "USER_READ")) { return NextResponse.json({ error: "권한이 없습니다" }, { status: 403 }); } const stats = await userService.getUserStats(); return NextResponse.json(stats); } catch (error) { console.error("사용자 통계 조회 오류:", error); return NextResponse.json( { error: "사용자 통계 조회 중 오류가 발생했습니다" }, { status: 500 } ); } } // 대량 사용자 작업 export async function bulkUserOperationHandler(request: NextRequest) { try { const session = await getServerSession(authConfig); if (!session?.user) { return NextResponse.json({ error: "인증이 필요합니다" }, { status: 401 }); } if (!hasPermission(session.user, "USER_UPDATE")) { return NextResponse.json({ error: "권한이 없습니다" }, { status: 403 }); } const body = await request.json(); const validatedData = bulkOperationSchema.parse(body); const result = await userService.bulkUserOperation( validatedData as BulkUserOperation, session.user.id ); return NextResponse.json(result); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: "유효하지 않은 데이터", details: error.errors }, { status: 400 } ); } console.error("대량 사용자 작업 오류:", error); return NextResponse.json( { error: "대량 사용자 작업 중 오류가 발생했습니다" }, { status: 500 } ); } }