설정 제한 보정
This commit is contained in:
@@ -30,7 +30,35 @@ const { isReservedNickname } = require('../lib/user-validation')
|
||||
const router = express.Router()
|
||||
const EMAIL_VERIFICATION_TTL_MS = 24 * 60 * 60 * 1000
|
||||
const PASSWORD_RESET_TTL_MS = 60 * 60 * 1000
|
||||
const NICKNAME_CHANGE_INTERVAL_MS = 14 * 24 * 60 * 60 * 1000
|
||||
|
||||
function resolveNicknameChangeIntervalMs() {
|
||||
const rawMs = String(process.env.NICKNAME_CHANGE_INTERVAL_MS || '').trim()
|
||||
if (rawMs) {
|
||||
const parsed = Number(rawMs)
|
||||
if (Number.isFinite(parsed) && parsed >= 0) return parsed
|
||||
}
|
||||
|
||||
const rawDays = String(process.env.NICKNAME_CHANGE_INTERVAL_DAYS || '').trim()
|
||||
if (rawDays) {
|
||||
const parsed = Number(rawDays)
|
||||
if (Number.isFinite(parsed) && parsed >= 0) return parsed * 24 * 60 * 60 * 1000
|
||||
}
|
||||
|
||||
return 14 * 24 * 60 * 60 * 1000
|
||||
}
|
||||
|
||||
function formatNicknameChangeIntervalLabel(intervalMs) {
|
||||
if (!intervalMs || intervalMs <= 0) return '제한 없음'
|
||||
const oneDayMs = 24 * 60 * 60 * 1000
|
||||
const wholeDays = intervalMs / oneDayMs
|
||||
if (Number.isInteger(wholeDays) && wholeDays >= 7 && wholeDays % 7 === 0) {
|
||||
return `${wholeDays / 7}주`
|
||||
}
|
||||
if (Number.isInteger(wholeDays)) {
|
||||
return `${wholeDays}일`
|
||||
}
|
||||
return `${Math.ceil(wholeDays)}일`
|
||||
}
|
||||
|
||||
const signupSchema = z.object({
|
||||
email: z.string().email(),
|
||||
@@ -83,13 +111,17 @@ async function serializeUser(user) {
|
||||
if (!user) return null
|
||||
const primaryAdmin = await findPrimaryAdminUser()
|
||||
const isPrimaryAdmin = !!user.isAdmin && primaryAdmin?.id === user.id
|
||||
const nicknameChangeIntervalMs = resolveNicknameChangeIntervalMs()
|
||||
const nicknameChangeAvailableAt = nicknameChangeIntervalMs > 0 ? (user.nicknameUpdatedAt || 0) + nicknameChangeIntervalMs : 0
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
nickname: user.nickname || '',
|
||||
nicknameUpdatedAt: user.nicknameUpdatedAt || 0,
|
||||
nicknameChangeAvailableAt: (user.nicknameUpdatedAt || 0) + NICKNAME_CHANGE_INTERVAL_MS,
|
||||
nicknameChangeAvailableAt,
|
||||
nicknameChangeIntervalMs,
|
||||
nicknameChangeIntervalLabel: formatNicknameChangeIntervalLabel(nicknameChangeIntervalMs),
|
||||
isAdmin: !!user.isAdmin,
|
||||
isPrimaryAdmin,
|
||||
isOperator: !!user.isAdmin && !isPrimaryAdmin,
|
||||
@@ -363,12 +395,15 @@ router.post('/profile', requireAuth, upload.single('avatar'), async (req, res) =
|
||||
if (!user) return res.status(404).json({ error: 'not_found' })
|
||||
const normalizedNickname = parsed.data.nickname.trim()
|
||||
const nicknameChanged = normalizedNickname !== (user.nickname || '').trim()
|
||||
const nicknameChangeIntervalMs = resolveNicknameChangeIntervalMs()
|
||||
|
||||
if (isReservedNickname(normalizedNickname)) return res.status(400).json({ error: 'nickname_reserved' })
|
||||
if (nicknameChanged && user.nicknameUpdatedAt && Date.now() < user.nicknameUpdatedAt + NICKNAME_CHANGE_INTERVAL_MS) {
|
||||
if (nicknameChanged && nicknameChangeIntervalMs > 0 && user.nicknameUpdatedAt && Date.now() < user.nicknameUpdatedAt + nicknameChangeIntervalMs) {
|
||||
return res.status(429).json({
|
||||
error: 'nickname_change_locked',
|
||||
nicknameChangeAvailableAt: user.nicknameUpdatedAt + NICKNAME_CHANGE_INTERVAL_MS,
|
||||
nicknameChangeAvailableAt: user.nicknameUpdatedAt + nicknameChangeIntervalMs,
|
||||
nicknameChangeIntervalMs,
|
||||
nicknameChangeIntervalLabel: formatNicknameChangeIntervalLabel(nicknameChangeIntervalMs),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user