diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-30 08:28:13 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-30 08:28:13 +0000 |
| commit | 5b6313f16f508882a0ea67716b7dbaa1c6967f04 (patch) | |
| tree | 3d1d8dafea2f31274ace3fbda08333e889e06d1c /lib/users/service.ts | |
| parent | 3f0fad18483a5c800c79c5e33946d9bb384c10e2 (diff) | |
(대표님) 20250630 16시 - 유저 도메인별 라우터 분리와 보안성검토 대응
Diffstat (limited to 'lib/users/service.ts')
| -rw-r--r-- | lib/users/service.ts | 250 |
1 files changed, 246 insertions, 4 deletions
diff --git a/lib/users/service.ts b/lib/users/service.ts index ad01c22a..9671abfb 100644 --- a/lib/users/service.ts +++ b/lib/users/service.ts @@ -6,15 +6,15 @@ import { getAllUsers, createUser, getUserById, updateUser, deleteUser, getUserBy import logger from '@/lib/logger'; import { Role, userRoles, users, userView, type User } from '@/db/schema/users'; import { saveDocument } from '../storage'; -import { GetUsersSchema } from '../admin-users/validations'; -import { revalidateTag, unstable_cache, unstable_noStore } from 'next/cache'; +import { GetSimpleUsersSchema, GetUsersSchema } from '../admin-users/validations'; +import { revalidatePath, revalidateTag, unstable_cache, unstable_noStore } from 'next/cache'; import { filterColumns } from '../filter-columns'; -import { countUsers, selectUsersWithCompanyAndRoles } from '../admin-users/repository'; +import { countUsers, countUsersSimple, selectUsers, selectUsersWithCompanyAndRoles } from '../admin-users/repository'; import db from "@/db/db"; import { getErrorMessage } from "@/lib/handle-error"; import { getServerSession } from "next-auth/next" import { authOptions } from "@/app/api/auth/[...nextauth]/route" -import { and, or, desc, asc, ilike, eq, isNull, sql, count, inArray } from "drizzle-orm"; +import { and, or, desc, asc, ilike, eq, isNull, sql, count, inArray, ne } from "drizzle-orm"; interface AssignUsersArgs { roleId: number @@ -340,6 +340,82 @@ export async function getUsersEVCP(input: GetUsersSchema) { )(); } + +export async function getUsersNotPartners(input: GetSimpleUsersSchema) { + return unstable_cache( + async () => { + try { + const offset = (input.page - 1) * input.perPage; + + // (1) advancedWhere + const advancedWhere = filterColumns({ + table: users, + filters: input.filters, + joinOperator: input.joinOperator, + }); + + // (2) globalWhere + let globalWhere; + if (input.search) { + const s = `%${input.search}%`; + globalWhere = or( + ilike(users.name, s), + ilike(users.email, s), + ilike(users.deptName, s), + ); + } + + // (3) 디폴트 domainWhere = eq(userView.domain, "partners") + // 다만, 사용자가 이미 domain 필터를 줬다면 적용 X + let domainWhere; + const hasDomainFilter = input.filters?.some((f) => f.id === "domain"); + if (!hasDomainFilter) { + domainWhere = ne(users.domain, "partners"); + } + + // (4) 최종 where + const finalWhere = and(advancedWhere, globalWhere, domainWhere); + + // (5) 정렬 + const orderBy = + input.sort.length > 0 + ? input.sort.map((item) => + item.desc ? desc(users[item.id]) : asc(users[item.id]) + ) + : [desc(users.createdAt)]; + + // ... + const { data, total } = await db.transaction(async (tx) => { + const data = await selectUsers(tx, { + where: finalWhere, + orderBy, + offset, + limit: input.perPage, + }); + + + console.log(data) + + const total = await countUsersSimple(tx, finalWhere); + return { data, total }; + }); + + const pageCount = Math.ceil(total / input.perPage); + return { data, pageCount }; + } catch (err) { + + console.log(err) + return { data: [], pageCount: 0 }; + } + }, + [JSON.stringify(input)], + { + revalidate: 3600, + tags: ["users-access-control"], + } + )(); +} + export async function getAllRoles(): Promise<Role[]> { try { return await findAllRoles(); @@ -507,3 +583,169 @@ export async function assignUsersToRole(roleId: number, userIds: number[]) { } } + + +export type UserDomain = "pending" | "evcp" | "procurement" | "sales" | "engineering" | "partners" + +/** + * 여러 사용자에게 도메인을 일괄 할당하는 함수 + */ +export async function assignUsersDomain( + userIds: number[], + domain: UserDomain +) { + try { + if (!userIds.length) { + return { + success: false, + message: "할당할 사용자가 없습니다." + } + } + + if (!domain) { + return { + success: false, + message: "도메인을 선택해주세요." + } + } + + // 사용자들의 도메인 업데이트 + const result = await db + .update(users) + .set({ + domain, + updatedAt: new Date(), + }) + .where(inArray(users.id, userIds)) + .returning({ + id: users.id, + name: users.name, + email: users.email, + domain: users.domain, + }) + + // 관련 페이지들 revalidate + revalidatePath("/evcp/user-management") + revalidatePath("/") + + return { + success: true, + message: `${result.length}명의 사용자 도메인이 업데이트되었습니다.`, + data: result + } + } catch (error) { + console.error("사용자 도메인 할당 오류:", error) + return { + success: false, + message: "도메인 할당 중 오류가 발생했습니다." + } + } +} + +/** + * 단일 사용자의 도메인을 변경하는 함수 + */ +export async function assignUserDomain( + userId: number, + domain: UserDomain +) { + try { + const result = await db + .update(users) + .set({ + domain, + updatedAt: new Date(), + }) + .where(eq(users.id, userId)) + .returning({ + id: users.id, + name: users.name, + email: users.email, + domain: users.domain, + }) + + if (result.length === 0) { + return { + success: false, + message: "사용자를 찾을 수 없습니다." + } + } + + revalidatePath("/evcp/user-management") + revalidatePath("/evcp/users") + + return { + success: true, + message: `${result[0].name}님의 도메인이 ${domain}으로 변경되었습니다.`, + data: result[0] + } + } catch (error) { + console.error("사용자 도메인 할당 오류:", error) + return { + success: false, + message: "도메인 할당 중 오류가 발생했습니다." + } + } +} + +/** + * 도메인별 사용자 통계를 조회하는 함수 + */ +export async function getUserDomainStats() { + try { + const stats = await db + .select({ + domain: users.domain, + count: count(), + }) + .from(users) + .where(eq(users.isActive, true)) + .groupBy(users.domain) + + return { + success: true, + data: stats + } + } catch (error) { + console.error("도메인 통계 조회 오류:", error) + return { + success: false, + message: "통계 조회 중 오류가 발생했습니다.", + data: [] + } + } +} + +/** + * pending 도메인 사용자 목록을 조회하는 함수 (관리자용) + */ +export async function getPendingUsers() { + try { + const pendingUsers = await db + .select({ + id: users.id, + name: users.name, + email: users.email, + createdAt: users.createdAt, + domain: users.domain, + }) + .from(users) + .where(and( + eq(users.domain, "pending"), + eq(users.isActive, true) + )) + .orderBy(desc(users.createdAt)) + + return { + success: true, + data: pendingUsers + } + } catch (error) { + console.error("pending 사용자 조회 오류:", error) + return { + success: false, + message: "사용자 조회 중 오류가 발생했습니다.", + data: [] + } + } +}
\ No newline at end of file |
