@restnfeel/agentc-starter-kit
Version:
한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템
206 lines (170 loc) • 5.3 kB
text/typescript
// 타입 정의
export * from "./types";
// 서비스 클래스
export { UserService } from "./service";
// API 핸들러
export * from "./api";
// React 컴포넌트
export { UserList, UserForm, UserStatsComponent } from "./components";
// React 훅
export {
useUsers,
useUser,
useCreateUser,
useUpdateUser,
useDeleteUser,
useUserStats,
useBulkUserOperation,
usePasswordReset,
useEmailVerification,
useProfileUpdate,
useAccountUnlock,
} from "./hooks";
// 기본 설정
export const USER_MANAGEMENT_CONFIG = {
// 페이지네이션 기본값
DEFAULT_PAGE_SIZE: 20,
MAX_PAGE_SIZE: 100,
// 비밀번호 정책
PASSWORD_MIN_LENGTH: 8,
PASSWORD_MAX_LENGTH: 128,
// 계정 잠금 정책
MAX_FAILED_ATTEMPTS: 5,
ACCOUNT_LOCK_DURATION: 30 * 60 * 1000, // 30분
// 토큰 만료 시간
PASSWORD_RESET_TOKEN_EXPIRY: 60 * 60 * 1000, // 1시간
EMAIL_VERIFICATION_TOKEN_EXPIRY: 24 * 60 * 60 * 1000, // 24시간
// 감사 로그 보존 기간
AUDIT_LOG_RETENTION_DAYS: 90,
// 사용자 세션 설정
SESSION_TIMEOUT: 30 * 24 * 60 * 60 * 1000, // 30일
// 이메일 설정
EMAIL_VERIFICATION_REQUIRED: true,
SEND_WELCOME_EMAIL: true,
// 역할 기본값
DEFAULT_USER_ROLE: "VIEWER" as const,
// 검색 설정
SEARCH_MIN_LENGTH: 2,
SEARCH_DEBOUNCE_MS: 300,
// 파일 업로드 설정
AVATAR_MAX_SIZE: 5 * 1024 * 1024, // 5MB
AVATAR_ALLOWED_TYPES: ["image/jpeg", "image/png", "image/gif", "image/webp"],
// 대량 작업 제한
BULK_OPERATION_MAX_USERS: 100,
// 통계 캐시 시간
STATS_CACHE_DURATION: 5 * 60 * 1000, // 5분
} as const;
// 유틸리티 함수
export const userUtils = {
// 사용자 이름 표시 형식
getDisplayName: (user: { name: string; email: string }) => {
return user.name || user.email.split("@")[0];
},
// 사용자 아바타 이니셜
getInitials: (name: string) => {
return name
.split(" ")
.map((part) => part.charAt(0).toUpperCase())
.slice(0, 2)
.join("");
},
// 계정 상태 확인
isAccountLocked: (user: { accountLockedUntil: Date | null }) => {
return (
user.accountLockedUntil && new Date(user.accountLockedUntil) > new Date()
);
},
// 이메일 인증 상태 확인
isEmailVerified: (user: { emailVerified: Date | null }) => {
return user.emailVerified !== null;
},
// 마지막 로그인 시간 포맷
formatLastLogin: (lastLogin: Date | null) => {
if (!lastLogin) return "없음";
const now = new Date();
const diff = now.getTime() - new Date(lastLogin).getTime();
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
if (days === 0) return "오늘";
if (days === 1) return "어제";
if (days < 7) return `${days}일 전`;
if (days < 30) return `${Math.floor(days / 7)}주 전`;
if (days < 365) return `${Math.floor(days / 30)}개월 전`;
return `${Math.floor(days / 365)}년 전`;
},
// 역할 표시 이름
getRoleDisplayName: (role: string) => {
const roleNames = {
ADMIN: "관리자",
EDITOR: "에디터",
VIEWER: "뷰어",
GUEST: "게스트",
};
return roleNames[role as keyof typeof roleNames] || role;
},
// 역할 권한 레벨
getRoleLevel: (role: string) => {
const roleLevels = {
GUEST: 0,
VIEWER: 1,
EDITOR: 2,
ADMIN: 3,
};
return roleLevels[role as keyof typeof roleLevels] || 0;
},
// 비밀번호 강도 검사
validatePasswordStrength: (password: string) => {
const checks = {
length: password.length >= USER_MANAGEMENT_CONFIG.PASSWORD_MIN_LENGTH,
uppercase: /[A-Z]/.test(password),
lowercase: /[a-z]/.test(password),
number: /\d/.test(password),
special: /[!@#$%^&*(),.?":{}|<>]/.test(password),
};
const score = Object.values(checks).filter(Boolean).length;
return {
score,
checks,
strength: score < 2 ? "weak" : score < 4 ? "medium" : "strong",
};
},
// 이메일 유효성 검사
validateEmail: (email: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
},
// 사용자 검색 필터링
filterUsers: (users: any[], searchTerm: string) => {
if (
!searchTerm ||
searchTerm.length < USER_MANAGEMENT_CONFIG.SEARCH_MIN_LENGTH
) {
return users;
}
const term = searchTerm.toLowerCase();
return users.filter(
(user) =>
user.name.toLowerCase().includes(term) ||
user.email.toLowerCase().includes(term)
);
},
// 사용자 정렬
sortUsers: (users: any[], field: string, direction: "asc" | "desc") => {
return [...users].sort((a, b) => {
let aValue = a[field];
let bValue = b[field];
// 날짜 필드 처리
if (field.includes("Date") || field === "lastLogin") {
aValue = aValue ? new Date(aValue).getTime() : 0;
bValue = bValue ? new Date(bValue).getTime() : 0;
}
// 문자열 필드 처리
if (typeof aValue === "string") {
aValue = aValue.toLowerCase();
bValue = bValue.toLowerCase();
}
if (aValue < bValue) return direction === "asc" ? -1 : 1;
if (aValue > bValue) return direction === "asc" ? 1 : -1;
return 0;
});
},
};