공개 상단 어나운스 바와 관리자 맞춤 설정을 추가하고, 스팸 필터에서 가입 금지 닉네임을 관리·검증한다. POST 설정 읽기 모드 비활성 토글과 설정 내비 아이콘 틀을 반영한다. Co-authored-by: Cursor <cursoragent@cursor.com>
111 lines
2.9 KiB
JavaScript
111 lines
2.9 KiB
JavaScript
/** @type {readonly string[]} 가입 금지 닉네임 기본값 */
|
|
export const DEFAULT_SIGNUP_BLOCKED_USERNAMES = Object.freeze([
|
|
'admin',
|
|
'master',
|
|
'zenn',
|
|
'sori',
|
|
'sori.studio'
|
|
])
|
|
|
|
/** @type {number} 금지 닉네임 최대 개수 */
|
|
export const MAX_SIGNUP_BLOCKED_USERNAME_COUNT = 100
|
|
|
|
/** @type {number} 금지 닉네임 한 항목 최대 길이 */
|
|
export const MAX_SIGNUP_BLOCKED_USERNAME_LENGTH = 60
|
|
|
|
/**
|
|
* 금지 닉네임 목록을 정리한다.
|
|
* @param {unknown} value - 원본 값
|
|
* @returns {string[]} 정리된 목록
|
|
*/
|
|
export const normalizeSignupBlockedUsernames = (value) => {
|
|
const source = Array.isArray(value) ? value : []
|
|
const seen = new Set()
|
|
const result = []
|
|
|
|
for (const item of source) {
|
|
const term = String(item || '').trim()
|
|
if (!term) {
|
|
continue
|
|
}
|
|
|
|
const key = term.toLowerCase()
|
|
if (seen.has(key)) {
|
|
continue
|
|
}
|
|
|
|
seen.add(key)
|
|
result.push(term.slice(0, MAX_SIGNUP_BLOCKED_USERNAME_LENGTH))
|
|
|
|
if (result.length >= MAX_SIGNUP_BLOCKED_USERNAME_COUNT) {
|
|
break
|
|
}
|
|
}
|
|
|
|
return result.length > 0 ? result : [...DEFAULT_SIGNUP_BLOCKED_USERNAMES]
|
|
}
|
|
|
|
/**
|
|
* DB에 저장된 금지 닉네임 JSON 문자열을 파싱한다.
|
|
* @param {unknown} raw - DB 값
|
|
* @returns {string[]} 정리된 목록
|
|
*/
|
|
export const parseSignupBlockedUsernamesFromDb = (raw) => {
|
|
if (Array.isArray(raw)) {
|
|
return normalizeSignupBlockedUsernames(raw)
|
|
}
|
|
|
|
if (typeof raw === 'string' && raw.trim()) {
|
|
try {
|
|
return normalizeSignupBlockedUsernames(JSON.parse(raw))
|
|
} catch {
|
|
return [...DEFAULT_SIGNUP_BLOCKED_USERNAMES]
|
|
}
|
|
}
|
|
|
|
return [...DEFAULT_SIGNUP_BLOCKED_USERNAMES]
|
|
}
|
|
|
|
/**
|
|
* 줄 단위 텍스트를 금지 닉네임 배열로 변환한다.
|
|
* @param {string} text - 줄바꿈 구분 입력
|
|
* @returns {string[]} 정리된 목록
|
|
*/
|
|
export const parseSignupBlockedUsernamesFromText = (text) => {
|
|
const lines = String(text || '').split(/\r?\n/)
|
|
return normalizeSignupBlockedUsernames(lines)
|
|
}
|
|
|
|
/**
|
|
* 닉네임에 금지 단어가 포함되는지 확인한다.
|
|
* @param {string} username - 닉네임
|
|
* @param {string[]} blockedList - 금지 목록
|
|
* @returns {string | null} 매칭된 금지 단어(표시용)
|
|
*/
|
|
export const getSignupBlockedUsernameMatch = (username, blockedList) => {
|
|
const normalized = username.trim().toLowerCase()
|
|
if (!normalized) {
|
|
return null
|
|
}
|
|
|
|
const terms = normalizeSignupBlockedUsernames(blockedList)
|
|
.slice()
|
|
.sort((left, right) => right.length - left.length)
|
|
|
|
for (const term of terms) {
|
|
const key = term.toLowerCase()
|
|
if (normalized === key || normalized.includes(key)) {
|
|
return term
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* 금지 닉네임 안내 문구를 만든다.
|
|
* @param {string} matchedTerm - 매칭된 금지 단어
|
|
* @returns {string} 안내 문구
|
|
*/
|
|
export const formatSignupBlockedUsernameMessage = (matchedTerm) => `${matchedTerm}은 사용할 수 없는 단어입니다.`
|