스팸 필터 설정 정규화 로직 분리
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
| lib/upload-size-limit.js | 관리자 미디어 업로드 종류별 최대 바이트 판별·1회 파일 수/총량 제한 정규화·용량 문구(서버·클라이언트 공용) |
|
||||
| lib/admin-social-settings-section.js | 관리자 설정 SNS 설정 필드 목록·기본 스냅샷·정규화·스냅샷 복사·변경 비교 helper |
|
||||
| lib/admin-post-settings-section.js | 관리자 설정 POST 설정 필드 목록·기본 스냅샷·정규화·스냅샷 복사·변경 비교 helper |
|
||||
| lib/admin-spam-settings-section.js | 관리자 설정 스팸 필터 필드 목록·기본 스냅샷·정규화·textarea 변환·스냅샷 복사·변경 비교 helper |
|
||||
| lib/post-card-thumbnail-url.js | 게시물 카드 썸네일 `thumbs/*-card.webp`, Pages 커버 썸네일 `thumbs/*-page-cover.webp`, 본문 표시용 `body-1600`/`body-720`·라이트박스용 `body-2560` 파생 이미지 URL 판별과 레거시 `body`/`lightbox` 판별, 자동 생성 이미지 캐시 버전 URL 생성(대표 이미지·본문 이미지·갤러리·Pages 커버 선택 및 서버 검증 공용) |
|
||||
| lib/external-favicon-url.js | 외부 URL origin 기준 Google `gstatic.com/faviconV2` 파비콘 URL 생성(내부 경로는 빈 문자열) |
|
||||
| lib/navigation-url.js | 네비게이션 URL 접두사 분리·합성·정규화(`https://` 자동 보정, `/` 내부 경로) |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
- 거대 컴포넌트 책임 분리 진행.
|
||||
- `components/admin/AdminMarkdownEditor.vue`: 미디어 모달, 슬래시 명령, 히스토리·선택 복원, 라이브/소스 전환 로직을 단계적으로 composable 또는 하위 컴포넌트로 분리.
|
||||
- `pages/admin/settings/index.vue`: 정규화가 필요한 설정 섹션(브랜드·어나운스 바·스팸 필터)의 편집 상태·스냅샷·저장 요청 로직을 composable 단위로 추가 분리.
|
||||
- `pages/admin/settings/index.vue`: 정규화가 필요한 설정 섹션(브랜드·어나운스 바)의 편집 상태·스냅샷·저장 요청 로직을 composable 단위로 추가 분리.
|
||||
- `components/content/ContentMarkdownRenderer.vue`: 파싱·블록 렌더링·라이브 편집 중개·라이트박스·광고 삽입 책임을 분리.
|
||||
- `components/admin/AdminPostForm.vue`: 태그 입력, 대표 이미지 선택, 발행/초안 저장 상태, 우측 설정 패널 책임을 분리.
|
||||
- `pages/admin/media/index.vue`: 미디어 목록, 폴더 트리, 상세 모달, 재생성 액션을 작은 단위로 분리.
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# 업데이트 이력
|
||||
|
||||
## v1.11.37
|
||||
|
||||
- 스팸 필터 설정 정규화·스냅샷 복사·변경 비교 로직을 `lib/admin-spam-settings-section.js`로 분리.
|
||||
- 설정 화면의 스팸 필터 textarea 변환과 편집 시작·취소·저장 로직을 공통 helper 기반으로 정리.
|
||||
- 설정 저장 payload의 스팸 필터 필드 정규화를 한 경로로 통합.
|
||||
|
||||
## v1.11.36
|
||||
|
||||
- SNS 설정 정규화·스냅샷 복사·변경 비교 로직을 `lib/admin-social-settings-section.js`로 분리.
|
||||
|
||||
66
lib/admin-spam-settings-section.js
Normal file
66
lib/admin-spam-settings-section.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
normalizeSignupBlockedUsernames,
|
||||
parseSignupBlockedUsernamesFromText
|
||||
} from './signup-blocked-usernames.js'
|
||||
|
||||
export const SPAM_SETTINGS_FIELDS = ['signupBlockedUsernames']
|
||||
|
||||
/**
|
||||
* 스팸 필터 설정 기본 스냅샷을 만든다.
|
||||
* @returns {Object} 기본 스팸 필터 설정 값
|
||||
*/
|
||||
export const createDefaultSpamSettingsSnapshot = () => ({
|
||||
signupBlockedUsernames: normalizeSignupBlockedUsernames()
|
||||
})
|
||||
|
||||
/**
|
||||
* 스팸 필터 설정 값을 저장·비교 가능한 형태로 정규화한다.
|
||||
* @param {Object} source - 원본 설정 값
|
||||
* @returns {Object} 정규화된 스팸 필터 설정 값
|
||||
*/
|
||||
export const normalizeSpamSettingsSection = (source = {}) => ({
|
||||
signupBlockedUsernames: normalizeSignupBlockedUsernames(source.signupBlockedUsernames)
|
||||
})
|
||||
|
||||
/**
|
||||
* 스팸 필터 설정 필드만 대상 객체에 복사한다.
|
||||
* @param {Object} target - 값을 받을 객체
|
||||
* @param {Object} source - 값을 제공할 객체
|
||||
* @returns {void}
|
||||
*/
|
||||
export const assignSpamSettingsSection = (target, source = {}) => {
|
||||
const normalized = normalizeSpamSettingsSection(source)
|
||||
SPAM_SETTINGS_FIELDS.forEach((field) => {
|
||||
target[field] = normalized[field]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 두 스팸 필터 설정 값이 다른지 확인한다.
|
||||
* @param {Object} current - 현재 설정 값
|
||||
* @param {Object} snapshot - 비교할 스냅샷
|
||||
* @returns {boolean} 변경 여부
|
||||
*/
|
||||
export const hasSpamSettingsSectionChanges = (current, snapshot) => {
|
||||
const normalizedCurrent = normalizeSpamSettingsSection(current)
|
||||
const normalizedSnapshot = normalizeSpamSettingsSection(snapshot)
|
||||
return JSON.stringify(normalizedCurrent.signupBlockedUsernames) !== JSON.stringify(normalizedSnapshot.signupBlockedUsernames)
|
||||
}
|
||||
|
||||
/**
|
||||
* 스팸 필터 금지 닉네임 배열을 textarea 값으로 변환한다.
|
||||
* @param {Object} source - 스팸 필터 설정 값
|
||||
* @returns {string} 줄바꿈 구분 textarea 값
|
||||
*/
|
||||
export const formatSpamBlockedUsernamesText = (source = {}) => {
|
||||
return normalizeSpamSettingsSection(source).signupBlockedUsernames.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* textarea 값을 스팸 필터 설정 값으로 변환한다.
|
||||
* @param {string} text - 줄바꿈 구분 입력
|
||||
* @returns {Object} 정규화된 스팸 필터 설정 값
|
||||
*/
|
||||
export const parseSpamBlockedUsernamesText = (text) => ({
|
||||
signupBlockedUsernames: parseSignupBlockedUsernamesFromText(text)
|
||||
})
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "1.11.36",
|
||||
"version": "1.11.37",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sori.studio",
|
||||
"version": "1.11.36",
|
||||
"version": "1.11.37",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "1.11.36",
|
||||
"version": "1.11.37",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"imports": {
|
||||
|
||||
@@ -6,10 +6,6 @@ import {
|
||||
normalizeAnnouncementBackgroundColor
|
||||
} from '~/lib/announcement-bar.js'
|
||||
import { DEFAULT_BRAND_COLOR, normalizeBrandColor } from '~/lib/brand-color.js'
|
||||
import {
|
||||
normalizeSignupBlockedUsernames,
|
||||
parseSignupBlockedUsernamesFromText
|
||||
} from '~/lib/signup-blocked-usernames.js'
|
||||
import {
|
||||
DISPLAY_COUNT_OPTIONS,
|
||||
POST_LIST_COUNT_OPTIONS
|
||||
@@ -33,6 +29,15 @@ import {
|
||||
hasSocialSettingsSectionChanges,
|
||||
normalizeSocialSettingsSection
|
||||
} from '~/lib/admin-social-settings-section.js'
|
||||
import {
|
||||
SPAM_SETTINGS_FIELDS,
|
||||
assignSpamSettingsSection,
|
||||
createDefaultSpamSettingsSnapshot,
|
||||
formatSpamBlockedUsernamesText,
|
||||
hasSpamSettingsSectionChanges,
|
||||
normalizeSpamSettingsSection,
|
||||
parseSpamBlockedUsernamesText
|
||||
} from '~/lib/admin-spam-settings-section.js'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'admin'
|
||||
@@ -181,9 +186,7 @@ const announcementSnapshot = reactive({
|
||||
announcementAlignment: DEFAULT_ANNOUNCEMENT_ALIGNMENT
|
||||
})
|
||||
/** 편집 시작 시점의 스팸 필터(취소 시 복원용) */
|
||||
const spamSnapshot = reactive({
|
||||
signupBlockedUsernames: []
|
||||
})
|
||||
const spamSnapshot = reactive(createDefaultSpamSettingsSnapshot())
|
||||
/** 편집 시작 시점의 사이트 코드(취소 시 복원용) */
|
||||
const siteCodeSnapshot = reactive({
|
||||
adsTxt: '',
|
||||
@@ -225,6 +228,7 @@ const {
|
||||
})
|
||||
const initialPostSettings = normalizePostSettingsSection(settings.value)
|
||||
const initialSocialSettings = normalizeSocialSettingsSection(settings.value)
|
||||
const initialSpamSettings = normalizeSpamSettingsSection(settings.value)
|
||||
|
||||
const form = reactive({
|
||||
title: settings.value?.title || 'sori.studio',
|
||||
@@ -246,7 +250,7 @@ const form = reactive({
|
||||
announcementUrl: settings.value?.announcementUrl || '',
|
||||
announcementBackgroundColor: normalizeAnnouncementBackgroundColor(settings.value?.announcementBackgroundColor || DEFAULT_ANNOUNCEMENT_BACKGROUND_COLOR),
|
||||
announcementAlignment: normalizeAnnouncementAlignment(settings.value?.announcementAlignment || DEFAULT_ANNOUNCEMENT_ALIGNMENT),
|
||||
signupBlockedUsernames: normalizeSignupBlockedUsernames(settings.value?.signupBlockedUsernames),
|
||||
...initialSpamSettings,
|
||||
adsTxt: settings.value?.adsTxt || '',
|
||||
customHeadCode: settings.value?.customHeadCode || '',
|
||||
customFooterCode: settings.value?.customFooterCode || '',
|
||||
@@ -329,7 +333,7 @@ const hasAnnouncementChanges = computed(() => customizeAnnouncement.value && (
|
||||
* @returns {boolean} 변경 여부
|
||||
*/
|
||||
const hasSpamChanges = computed(() => editSpam.value
|
||||
&& JSON.stringify(form.signupBlockedUsernames) !== JSON.stringify(spamSnapshot.signupBlockedUsernames))
|
||||
&& hasSpamSettingsSectionChanges(form, spamSnapshot))
|
||||
|
||||
/**
|
||||
* 사이트 코드 변경 여부
|
||||
@@ -599,9 +603,9 @@ const getPostExportProgressDescription = (job) => {
|
||||
* 가입 금지 닉네임 textarea 바인딩
|
||||
*/
|
||||
const signupBlockedUsernamesText = computed({
|
||||
get: () => form.signupBlockedUsernames.join('\n'),
|
||||
get: () => formatSpamBlockedUsernamesText(form),
|
||||
set: (value) => {
|
||||
form.signupBlockedUsernames = parseSignupBlockedUsernamesFromText(value)
|
||||
assignSpamSettingsSection(form, parseSpamBlockedUsernamesText(value))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1080,7 +1084,7 @@ const buildSiteSettingsPayload = (fields = null) => {
|
||||
announcementUrl: form.announcementUrl || '',
|
||||
announcementBackgroundColor: normalizeAnnouncementBackgroundColor(form.announcementBackgroundColor || DEFAULT_ANNOUNCEMENT_BACKGROUND_COLOR),
|
||||
announcementAlignment: normalizeAnnouncementAlignment(form.announcementAlignment || DEFAULT_ANNOUNCEMENT_ALIGNMENT),
|
||||
signupBlockedUsernames: normalizeSignupBlockedUsernames(form.signupBlockedUsernames),
|
||||
...normalizeSpamSettingsSection(form),
|
||||
adsTxt: form.adsTxt || '',
|
||||
customHeadCode: form.customHeadCode || '',
|
||||
customFooterCode: form.customFooterCode || '',
|
||||
@@ -1561,7 +1565,8 @@ const saveAnnouncementSection = async () => {
|
||||
* @returns {void}
|
||||
*/
|
||||
const beginEditSpam = () => {
|
||||
spamSnapshot.signupBlockedUsernames = [...form.signupBlockedUsernames]
|
||||
assignSpamSettingsSection(spamSnapshot, form)
|
||||
assignSpamSettingsSection(form, spamSnapshot)
|
||||
editSpam.value = true
|
||||
}
|
||||
|
||||
@@ -1570,7 +1575,7 @@ const beginEditSpam = () => {
|
||||
* @returns {void}
|
||||
*/
|
||||
const cancelEditSpam = () => {
|
||||
form.signupBlockedUsernames = [...spamSnapshot.signupBlockedUsernames]
|
||||
assignSpamSettingsSection(form, spamSnapshot)
|
||||
editSpam.value = false
|
||||
}
|
||||
|
||||
@@ -1586,11 +1591,12 @@ const saveSpamSection = async () => {
|
||||
const ok = await persistSiteSettings({
|
||||
successToast: '스팸 필터 설정이 저장되었습니다.',
|
||||
savingFlag: savingSpam,
|
||||
fields: ['signupBlockedUsernames']
|
||||
fields: SPAM_SETTINGS_FIELDS
|
||||
})
|
||||
|
||||
if (ok) {
|
||||
spamSnapshot.signupBlockedUsernames = [...form.signupBlockedUsernames]
|
||||
assignSpamSettingsSection(spamSnapshot, form)
|
||||
assignSpamSettingsSection(form, spamSnapshot)
|
||||
editSpam.value = false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user