Google 계정 연동 관리 개선
This commit is contained in:
@@ -4,6 +4,7 @@ import { createError, getQuery, sendRedirect } from 'h3'
|
||||
import {
|
||||
createGoogleUser,
|
||||
getUserByEmail,
|
||||
getUserById,
|
||||
getUserByGoogleSub,
|
||||
isPrivilegedMemberSession,
|
||||
linkGoogleIdentityToUser,
|
||||
@@ -123,6 +124,17 @@ export default defineEventHandler(async (event) => {
|
||||
return sendRedirect(event, appendGoogleStatus('/signin', 'session-mismatch'), 302)
|
||||
}
|
||||
|
||||
const currentUser = await getUserById(session.userId)
|
||||
if (!currentUser) {
|
||||
clearMemberSession(event)
|
||||
clearAdminSession(event)
|
||||
return sendRedirect(event, appendGoogleStatus('/signin', 'session-mismatch'), 302)
|
||||
}
|
||||
|
||||
if (currentUser.googleLinkedAt) {
|
||||
return sendRedirect(event, appendGoogleStatus(state.redirectTo, 'already-connected'), 302)
|
||||
}
|
||||
|
||||
if (linkedUser && String(linkedUser.id) !== String(session.userId)) {
|
||||
return sendRedirect(event, appendGoogleStatus(state.redirectTo, 'already-linked'), 302)
|
||||
}
|
||||
|
||||
@@ -115,6 +115,10 @@ const mapAdminMemberRow = (row) => {
|
||||
lastSeenIp: row.lastSeenIp || '',
|
||||
previousLastSeenAt,
|
||||
previousLastSeenIp: row.previousLastSeenIp || '',
|
||||
googleEmail: row.googleEmail || '',
|
||||
googleName: row.googleName || '',
|
||||
googleAvatarUrl: row.googleAvatarUrl || '',
|
||||
googleLinkedAt: row.googleLinkedAt ? row.googleLinkedAt.toISOString() : null,
|
||||
commentCount: Number(row.commentCount || 0),
|
||||
activityStatus: isActive ? '활성' : '비활성',
|
||||
role: getMemberRoleLabel(roleCode)
|
||||
@@ -826,6 +830,10 @@ export const listMembersForAdmin = async () => {
|
||||
users.member_note AS "note",
|
||||
users.is_admin AS "isAdmin",
|
||||
users.user_role AS "roleCode",
|
||||
users.google_email AS "googleEmail",
|
||||
users.google_name AS "googleName",
|
||||
users.google_avatar_url AS "googleAvatarUrl",
|
||||
users.google_linked_at AS "googleLinkedAt",
|
||||
users.created_at AS "createdAt",
|
||||
users.updated_at AS "updatedAt",
|
||||
users.last_seen_at AS "lastSeenAt",
|
||||
@@ -859,6 +867,10 @@ export const getMemberForAdmin = async (memberId) => {
|
||||
users.member_note AS "note",
|
||||
users.is_admin AS "isAdmin",
|
||||
users.user_role AS "roleCode",
|
||||
users.google_email AS "googleEmail",
|
||||
users.google_name AS "googleName",
|
||||
users.google_avatar_url AS "googleAvatarUrl",
|
||||
users.google_linked_at AS "googleLinkedAt",
|
||||
users.created_at AS "createdAt",
|
||||
users.updated_at AS "updatedAt",
|
||||
users.last_seen_at AS "lastSeenAt",
|
||||
@@ -946,6 +958,45 @@ export const updateMemberByAdmin = async (input) => {
|
||||
return rows?.[0] ? getMemberForAdmin(rows[0].id) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 소유자 권한으로 회원 Google 계정 연결을 해제한다.
|
||||
* @param {{ actorUserId: string, memberId: string }} input - 해제 정보
|
||||
* @returns {Promise<Object | null>} 수정된 회원
|
||||
*/
|
||||
export const unlinkGoogleIdentityByOwner = async (input) => {
|
||||
const actor = await getMemberForAdmin(input.actorUserId)
|
||||
if (!actor || actor.roleCode !== MEMBER_ROLE.OWNER) {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: 'Google 연동 해제는 소유자만 할 수 있습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
const target = await getMemberForAdmin(input.memberId)
|
||||
if (!target) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
message: '회원을 찾을 수 없습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
const sql = requireSql()
|
||||
const rows = await sql`
|
||||
UPDATE users
|
||||
SET
|
||||
google_sub = NULL,
|
||||
google_email = NULL,
|
||||
google_name = NULL,
|
||||
google_avatar_url = NULL,
|
||||
google_linked_at = NULL,
|
||||
updated_at = now()
|
||||
WHERE id = ${input.memberId}
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
return rows?.[0] ? getMemberForAdmin(rows[0].id) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 화면에서 회원 썸네일만 수정한다.
|
||||
* @param {{ actorUserId: string, memberId: string, avatarUrl: string }} input - 수정 값
|
||||
|
||||
34
server/routes/admin/api/members/[id]/google.delete.js
Normal file
34
server/routes/admin/api/members/[id]/google.delete.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { createError, getRouterParam } from 'h3'
|
||||
import { requireAdminSession } from '../../../../../utils/admin-auth'
|
||||
import { unlinkGoogleIdentityByOwner } from '../../../../../repositories/member-repository'
|
||||
|
||||
/**
|
||||
* 관리자 회원 Google 계정 연결 해제 API
|
||||
* @param {import('h3').H3Event} event - 요청 이벤트
|
||||
* @returns {Promise<Object>} 수정된 회원
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const session = requireAdminSession(event)
|
||||
const memberId = String(getRouterParam(event, 'id') || '')
|
||||
|
||||
if (!memberId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '회원 ID가 필요합니다.'
|
||||
})
|
||||
}
|
||||
|
||||
const updated = await unlinkGoogleIdentityByOwner({
|
||||
actorUserId: session.userId,
|
||||
memberId
|
||||
})
|
||||
|
||||
if (!updated) {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: 'Google 연동 해제에 실패했습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
return updated
|
||||
})
|
||||
Reference in New Issue
Block a user