관리자와 회원 설정 계정 작업 정리
This commit is contained in:
@@ -27,6 +27,7 @@ const getMemberRoleLabel = (roleCode) => roleCode === MEMBER_ROLE.OWNER
|
||||
*/
|
||||
const mapAdminMemberRow = (row) => {
|
||||
const lastSeenAt = row.lastSeenAt ? row.lastSeenAt.toISOString() : null
|
||||
const previousLastSeenAt = row.previousLastSeenAt ? row.previousLastSeenAt.toISOString() : null
|
||||
const isActive = row.lastSeenAt
|
||||
? Date.now() - new Date(row.lastSeenAt).getTime() <= 1000 * 60 * 60 * 24 * 30
|
||||
: false
|
||||
@@ -45,6 +46,8 @@ const mapAdminMemberRow = (row) => {
|
||||
updatedAt: row.updatedAt.toISOString(),
|
||||
lastSeenAt,
|
||||
lastSeenIp: row.lastSeenIp || '',
|
||||
previousLastSeenAt,
|
||||
previousLastSeenIp: row.previousLastSeenIp || '',
|
||||
commentCount: Number(row.commentCount || 0),
|
||||
activityStatus: isActive ? '활성' : '비활성',
|
||||
role: getMemberRoleLabel(roleCode)
|
||||
@@ -64,6 +67,8 @@ const mapAdminMemberRow = (row) => {
|
||||
* @property {string} updatedAt - 수정 시각(ISO)
|
||||
* @property {string | null} lastSeenAt - 최근 접속 시각(ISO)
|
||||
* @property {string} lastSeenIp - 최근 접속 IP
|
||||
* @property {string | null} previousLastSeenAt - 이전 로그인 시각(ISO)
|
||||
* @property {string} previousLastSeenIp - 이전 로그인 IP
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -100,7 +105,9 @@ export const getUserByEmail = async (email) => {
|
||||
created_at AS "createdAt",
|
||||
updated_at AS "updatedAt",
|
||||
last_seen_at AS "lastSeenAt",
|
||||
last_seen_ip AS "lastSeenIp"
|
||||
last_seen_ip AS "lastSeenIp",
|
||||
previous_last_seen_at AS "previousLastSeenAt",
|
||||
previous_last_seen_ip AS "previousLastSeenIp"
|
||||
FROM users
|
||||
WHERE lower(email) = lower(${email})
|
||||
LIMIT 1
|
||||
@@ -127,7 +134,9 @@ export const getUserById = async (id) => {
|
||||
created_at AS "createdAt",
|
||||
updated_at AS "updatedAt",
|
||||
last_seen_at AS "lastSeenAt",
|
||||
last_seen_ip AS "lastSeenIp"
|
||||
last_seen_ip AS "lastSeenIp",
|
||||
previous_last_seen_at AS "previousLastSeenAt",
|
||||
previous_last_seen_ip AS "previousLastSeenIp"
|
||||
FROM users
|
||||
WHERE id = ${id}
|
||||
LIMIT 1
|
||||
@@ -136,6 +145,38 @@ export const getUserById = async (id) => {
|
||||
return rows?.[0] || null
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 설정 화면용 회원 프로필을 조회한다.
|
||||
* @param {string} id - 사용자 ID
|
||||
* @returns {Promise<(Omit<MemberUser, 'passwordHash'> & { commentCount: number }) | null>} 회원 프로필
|
||||
*/
|
||||
export const getUserProfileById = async (id) => {
|
||||
const sql = requireSql()
|
||||
const rows = await sql`
|
||||
SELECT
|
||||
users.id,
|
||||
users.username,
|
||||
users.email,
|
||||
users.avatar_url AS "avatarUrl",
|
||||
users.is_admin AS "isAdmin",
|
||||
users.user_role AS "role",
|
||||
users.created_at AS "createdAt",
|
||||
users.updated_at AS "updatedAt",
|
||||
users.last_seen_at AS "lastSeenAt",
|
||||
users.last_seen_ip AS "lastSeenIp",
|
||||
users.previous_last_seen_at AS "previousLastSeenAt",
|
||||
users.previous_last_seen_ip AS "previousLastSeenIp",
|
||||
COALESCE(count(comments.id), 0)::int AS "commentCount"
|
||||
FROM users
|
||||
LEFT JOIN comments ON comments.user_id = users.id AND comments.status = 'published'
|
||||
WHERE users.id = ${id}
|
||||
GROUP BY users.id
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
return rows?.[0] || null
|
||||
}
|
||||
|
||||
/**
|
||||
* ID로 회원 조회(비밀번호 포함)
|
||||
* @param {string} id - 사용자 ID
|
||||
@@ -155,7 +196,9 @@ export const getUserByIdWithPassword = async (id) => {
|
||||
created_at AS "createdAt",
|
||||
updated_at AS "updatedAt",
|
||||
last_seen_at AS "lastSeenAt",
|
||||
last_seen_ip AS "lastSeenIp"
|
||||
last_seen_ip AS "lastSeenIp",
|
||||
previous_last_seen_at AS "previousLastSeenAt",
|
||||
previous_last_seen_ip AS "previousLastSeenIp"
|
||||
FROM users
|
||||
WHERE id = ${id}
|
||||
LIMIT 1
|
||||
@@ -198,7 +241,9 @@ export const createUser = async (input) => {
|
||||
created_at AS "createdAt",
|
||||
updated_at AS "updatedAt",
|
||||
last_seen_at AS "lastSeenAt",
|
||||
last_seen_ip AS "lastSeenIp"
|
||||
last_seen_ip AS "lastSeenIp",
|
||||
previous_last_seen_at AS "previousLastSeenAt",
|
||||
previous_last_seen_ip AS "previousLastSeenIp"
|
||||
`
|
||||
})
|
||||
|
||||
@@ -223,6 +268,8 @@ export const touchUserActivity = async (input) => {
|
||||
await sql`
|
||||
UPDATE users
|
||||
SET
|
||||
previous_last_seen_at = last_seen_at,
|
||||
previous_last_seen_ip = last_seen_ip,
|
||||
last_seen_at = now(),
|
||||
last_seen_ip = ${input.ip},
|
||||
updated_at = now()
|
||||
@@ -254,7 +301,9 @@ export const updateMemberProfile = async (input) => {
|
||||
created_at AS "createdAt",
|
||||
updated_at AS "updatedAt",
|
||||
last_seen_at AS "lastSeenAt",
|
||||
last_seen_ip AS "lastSeenIp"
|
||||
last_seen_ip AS "lastSeenIp",
|
||||
previous_last_seen_at AS "previousLastSeenAt",
|
||||
previous_last_seen_ip AS "previousLastSeenIp"
|
||||
`
|
||||
|
||||
return rows?.[0] || null
|
||||
@@ -308,6 +357,38 @@ export const deleteMember = async (userId) => {
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 화면에서 회원을 삭제한다.
|
||||
* @param {{ actorUserId: string, targetUserId: string }} input - 삭제 정보
|
||||
* @returns {Promise<Object>} 삭제된 회원
|
||||
*/
|
||||
export const deleteMemberByAdmin = async (input) => {
|
||||
const target = await getMemberForAdmin(input.targetUserId)
|
||||
if (!target) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
message: '회원을 찾을 수 없습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
if (target.id === input.actorUserId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '현재 로그인한 계정은 여기서 삭제할 수 없습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
if (target.roleCode === MEMBER_ROLE.OWNER && (await countOwnerMembers()) <= 1) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '최소 1명의 소유자 권한은 유지되어야 합니다.'
|
||||
})
|
||||
}
|
||||
|
||||
await deleteMember(input.targetUserId)
|
||||
return target
|
||||
}
|
||||
|
||||
/**
|
||||
* 소유자 권한 회원 수를 조회한다.
|
||||
* @returns {Promise<number>} 소유자 회원 수
|
||||
@@ -375,7 +456,7 @@ export const isEmailTaken = async (input) => {
|
||||
|
||||
/**
|
||||
* 관리자용 회원 목록 조회(댓글 활동 포함)
|
||||
* @returns {Promise<Array<{ id: string, username: string, email: string, avatarUrl: string, isAdmin: boolean, roleCode: string, createdAt: string, updatedAt: string, lastSeenAt: string | null, lastSeenIp: string, commentCount: number, activityStatus: string, role: string }>>} 회원 목록
|
||||
* @returns {Promise<Array<{ id: string, username: string, email: string, avatarUrl: string, isAdmin: boolean, roleCode: string, createdAt: string, updatedAt: string, lastSeenAt: string | null, lastSeenIp: string, previousLastSeenAt: string | null, previousLastSeenIp: string, commentCount: number, activityStatus: string, role: string }>>} 회원 목록
|
||||
*/
|
||||
export const listMembersForAdmin = async () => {
|
||||
const sql = requireSql()
|
||||
@@ -393,6 +474,8 @@ export const listMembersForAdmin = async () => {
|
||||
users.updated_at AS "updatedAt",
|
||||
users.last_seen_at AS "lastSeenAt",
|
||||
users.last_seen_ip AS "lastSeenIp",
|
||||
users.previous_last_seen_at AS "previousLastSeenAt",
|
||||
users.previous_last_seen_ip AS "previousLastSeenIp",
|
||||
COALESCE(count(comments.id), 0)::int AS "commentCount"
|
||||
FROM users
|
||||
LEFT JOIN comments ON comments.user_id = users.id AND comments.status = 'published'
|
||||
@@ -424,6 +507,8 @@ export const getMemberForAdmin = async (memberId) => {
|
||||
users.updated_at AS "updatedAt",
|
||||
users.last_seen_at AS "lastSeenAt",
|
||||
users.last_seen_ip AS "lastSeenIp",
|
||||
users.previous_last_seen_at AS "previousLastSeenAt",
|
||||
users.previous_last_seen_ip AS "previousLastSeenIp",
|
||||
COALESCE(count(comments.id), 0)::int AS "commentCount"
|
||||
FROM users
|
||||
LEFT JOIN comments ON comments.user_id = users.id AND comments.status = 'published'
|
||||
@@ -519,7 +604,9 @@ export const getAdminUserByEmail = async (email) => {
|
||||
created_at AS "createdAt",
|
||||
updated_at AS "updatedAt",
|
||||
last_seen_at AS "lastSeenAt",
|
||||
last_seen_ip AS "lastSeenIp"
|
||||
last_seen_ip AS "lastSeenIp",
|
||||
previous_last_seen_at AS "previousLastSeenAt",
|
||||
previous_last_seen_ip AS "previousLastSeenIp"
|
||||
FROM users
|
||||
WHERE lower(email) = lower(${email})
|
||||
AND user_role = ANY(${PRIVILEGED_ROLES})
|
||||
|
||||
Reference in New Issue
Block a user