페이지 형식 선택과 접속 IP 기록 수정 v1.5.7

This commit is contained in:
2026-05-26 16:44:52 +09:00
parent a5ae2c3fce
commit e78e09f3fd
15 changed files with 52 additions and 23 deletions

View File

@@ -1,6 +1,6 @@
import { randomBytes } from 'node:crypto'
import { z } from 'zod'
import { createError, getRequestIP, readBody } from 'h3'
import { createError, readBody } from 'h3'
import { getPostgresClient } from '../../../repositories/postgres-client'
import { getMemberBootstrapState, getUserByEmail } from '../../../repositories/member-repository'
import {
@@ -14,6 +14,7 @@ import {
import { generateSixDigitOtp, hashOtpCode, normalizeOtpEmail } from '../../../utils/email-otp'
import { isResendConfigured, sendResendEmail } from '../../../utils/resend-mail'
import { getRuntimeEnvValue } from '../../../utils/runtime-env'
import { getClientIp } from '../../../utils/request-ip'
const bodySchema = z.object({
email: z.string().trim().email(),
@@ -113,7 +114,7 @@ export default defineEventHandler(async (event) => {
if (!user) {
const dummyHash = randomBytes(32).toString('hex')
const expiresAt = new Date(Date.now() + OTP_TTL_MS)
const createdIp = String(getRequestIP(event) || '')
const createdIp = getClientIp(event)
await invalidatePendingOtpChallenges(sql, email, purpose)
await insertOtpChallenge(sql, {
email,
@@ -130,7 +131,7 @@ export default defineEventHandler(async (event) => {
const code = generateSixDigitOtp()
const codeHash = hashOtpCode({ pepper, email, purpose, code })
const expiresAt = new Date(Date.now() + OTP_TTL_MS)
const createdIp = String(getRequestIP(event) || '')
const createdIp = getClientIp(event)
const challengeId = await insertOtpChallenge(sql, {
email,

View File

@@ -1,8 +1,9 @@
import bcrypt from 'bcrypt'
import { z } from 'zod'
import { createError, getRequestIP, readBody } from 'h3'
import { createError, readBody } from 'h3'
import { getUserByEmail, touchUserActivity } from '../../repositories/member-repository'
import { setMemberSession } from '../../utils/member-auth'
import { getClientIp } from '../../utils/request-ip'
const loginSchema = z.object({
email: z.string().trim().email(),
@@ -38,7 +39,7 @@ export default defineEventHandler(async (event) => {
setMemberSession(event, { userId: user.id, email: user.email })
await touchUserActivity({
userId: user.id,
ip: String(getRequestIP(event) || '')
ip: getClientIp(event)
})
return {
@@ -48,4 +49,3 @@ export default defineEventHandler(async (event) => {
avatarUrl: user.avatarUrl || ''
}
})

View File

@@ -1,6 +1,6 @@
import bcrypt from 'bcrypt'
import { z } from 'zod'
import { createError, getRequestIP, readBody } from 'h3'
import { createError, readBody } from 'h3'
import { createUser, getUserByEmail, getMemberBootstrapState, isUsernameTaken, touchUserActivity } from '../../repositories/member-repository'
import { verifyAndConsumeEmailOtp } from '../../repositories/email-otp-repository'
import { setMemberSession } from '../../utils/member-auth'
@@ -8,6 +8,7 @@ import { setAdminSession } from '../../utils/admin-auth'
import { isResendConfigured } from '../../utils/resend-mail'
import { getRuntimeEnvValue } from '../../utils/runtime-env'
import { assertSignupUsernameAllowed } from '../../utils/member-username-policy'
import { getClientIp } from '../../utils/request-ip'
const signupSchema = z.object({
username: z.string().trim().min(1),
@@ -112,7 +113,7 @@ export default defineEventHandler(async (event) => {
}
await touchUserActivity({
userId: created.id,
ip: String(getRequestIP(event) || '')
ip: getClientIp(event)
})
return {

View File

@@ -1,8 +1,9 @@
import { createError, getRequestIP, readBody } from 'h3'
import { createError, readBody } from 'h3'
import { z } from 'zod'
import { createComment } from '../../../repositories/comment-repository'
import { touchUserActivity } from '../../../repositories/member-repository'
import { requireMemberSession } from '../../../utils/member-auth'
import { getClientIp } from '../../../utils/request-ip'
const createCommentSchema = z.object({
body: z.string().trim().min(1).max(5000),
@@ -34,11 +35,10 @@ export default defineEventHandler(async (event) => {
})
await touchUserActivity({
userId: session.userId,
ip: String(getRequestIP(event) || '')
ip: getClientIp(event)
})
return {
comment
}
})

View File

@@ -1,7 +1,7 @@
import { getRequestIP } from 'h3'
import { toggleCommentLike } from '../../../../../repositories/comment-repository'
import { touchUserActivity } from '../../../../../repositories/member-repository'
import { requireMemberSession } from '../../../../../utils/member-auth'
import { getClientIp } from '../../../../../utils/request-ip'
/**
* 댓글 좋아요 토글 API
@@ -21,7 +21,7 @@ export default defineEventHandler(async (event) => {
await touchUserActivity({
userId: session.userId,
ip: String(getRequestIP(event) || '')
ip: getClientIp(event)
})
return result

View File

@@ -1,10 +1,11 @@
import { z } from 'zod'
import { createError, getRequestIP, readBody } from 'h3'
import { createError, readBody } from 'h3'
import bcrypt from 'bcrypt'
import { safeCompare, setAdminSession } from '../../../../utils/admin-auth'
import { getAdminUserByEmail, getMemberBootstrapState, touchUserActivity, upsertBootstrapOwner } from '../../../../repositories/member-repository'
import { setMemberSession } from '../../../../utils/member-auth'
import { getRuntimeEnvValue } from '../../../../utils/runtime-env'
import { getClientIp } from '../../../../utils/request-ip'
const loginSchema = z.object({
email: z.string().email(),
@@ -86,7 +87,7 @@ export default defineEventHandler(async (event) => {
})
await touchUserActivity({
userId: adminUser.id,
ip: String(getRequestIP(event) || '')
ip: getClientIp(event)
})
return {

View File

@@ -0,0 +1,8 @@
import { getRequestIP } from 'h3'
/**
* 프록시 헤더를 포함해 요청 IP를 조회한다.
* @param {import('h3').H3Event} event - 요청 이벤트
* @returns {string} 요청 IP
*/
export const getClientIp = (event) => String(getRequestIP(event, { xForwardedFor: true }) || '')