export interface UserRecord { id: string; username: string; displayName: string; roles: string[]; sanctions: UserSanctions; oauthType: 'NONE' | 'KAKAO'; oauthId?: string; email?: string; oauthInfo?: UserOAuthInfo; picture: string; imageServer: number; iconUpdatedAt?: string; iconRevision?: string; profileIconResetAt?: string; thirdPartyUse: boolean; termsAcceptedAt?: string; privacyAcceptedAt?: string; kakaoVerifiedAt?: string; kakaoGraceStartedAt: string; deleteAfter?: string; passwordHash: string; passwordSalt: string; createdAt: string; legacyMemberNo?: number; legacyGrade?: number; } export interface PublicUser { id: string; username: string; displayName: string; roles: string[]; picture: string; kakaoVerified: boolean; kakaoGraceStartedAt: string; createdAt: string; } export interface UserSanctions { bannedUntil?: string; mutedUntil?: string; suspendedUntil?: string; warningCount?: number; flags?: string[]; notes?: string; serverRestrictions?: Record; legacyPenalty?: Record; } export interface UserServerRestriction { blockedFeatures?: string[]; until?: string; reason?: string; notes?: string; } export const toPublicUser = (user: UserRecord): PublicUser => ({ id: user.id, username: user.username, displayName: user.displayName, roles: user.roles, picture: user.picture, kakaoVerified: user.oauthType === 'KAKAO' && Boolean(user.kakaoVerifiedAt), kakaoGraceStartedAt: user.kakaoGraceStartedAt, createdAt: user.createdAt, }); export interface CreateUserInput { username: string; password: string; displayName?: string; termsAcceptedAt?: Date; privacyAcceptedAt?: Date; thirdPartyUse?: boolean; oauth?: { type: 'KAKAO'; id: string; email: string; info: UserOAuthInfo; }; } export interface UserRepository { findById(id: string): Promise; findByIds(ids: string[]): Promise; findByUsername(username: string): Promise; findByDisplayName(displayName: string): Promise; findByOauthId(type: 'KAKAO', oauthId: string): Promise; findByEmail(email: string): Promise; createUser(input: CreateUserInput): Promise; verifyPassword(user: UserRecord, password: string): Promise; updatePassword(userId: string, password: string): Promise; updateOAuthInfo(userId: string, oauthInfo: UserOAuthInfo): Promise; linkKakao( userId: string, input: { oauthId: string; email: string; oauthInfo: UserOAuthInfo; verifiedAt: Date; } ): Promise; updateRoles(userId: string, roles: string[]): Promise; updateSanctions(userId: string, sanctions: UserSanctions): Promise; updateIcon(userId: string, picture: string, imageServer: number, updatedAt: Date): Promise; updateIconForDay( userId: string, picture: string, imageServer: number, updatedAt: Date, dayStart: Date, consumeDailyQuota: boolean ): Promise; resetProfileIcon(userId: string, requestedAt: Date): Promise; setThirdPartyUse(userId: string, allowed: boolean): Promise; scheduleDeletion(userId: string, deleteAfter: Date): Promise; deleteUser(userId: string): Promise; } export interface UserOAuthInfo { accessToken?: string; refreshToken?: string; accessTokenValidUntil?: string; refreshTokenValidUntil?: string; nextPasswordChange?: string; }