세션·로그인 제한, ZIP/업로드 스트리밍, CSP 최소 강제와 Report-Only, Playwright 격리 검증을 추가하고 미디어 목록을 reconcile 후 database 모드로 고정한다. Co-authored-by: Cursor <cursoragent@cursor.com>
94 lines
2.4 KiB
JavaScript
94 lines
2.4 KiB
JavaScript
import { createError, readBody } from 'h3'
|
|
import { requireAdminSession } from '../../../utils/admin-auth'
|
|
import { parseAdminSiteSettingsInput } from '../../../utils/admin-site-settings-input'
|
|
import {
|
|
getSiteSettings,
|
|
updateSiteSettingsPartial
|
|
} from '../../../repositories/content-repository'
|
|
|
|
const ALLOWED_SITE_SETTINGS_FIELDS = new Set([
|
|
'title',
|
|
'description',
|
|
'siteUrl',
|
|
'logoText',
|
|
'logoUrl',
|
|
'faviconUrl',
|
|
'copyrightText',
|
|
'socialLinks',
|
|
'showPostUpdatedAt',
|
|
'postTagLimit',
|
|
'homeLatestPostLimit',
|
|
'postListPageSize',
|
|
'tagPostListPageSize',
|
|
'featuredPostLimit',
|
|
'pageSectionItemLimit',
|
|
'uploadMaxFileCount',
|
|
'uploadMaxTotalSize',
|
|
'homeCoverImageUrl',
|
|
'homeCoverDarkImageUrl',
|
|
'homeCoverTitle',
|
|
'homeCoverText',
|
|
'brandColor',
|
|
'announcementEnabled',
|
|
'announcementText',
|
|
'announcementUrl',
|
|
'announcementBackgroundColor',
|
|
'announcementAlignment',
|
|
'signupBlockedUsernames',
|
|
'adsTxt',
|
|
'customHeadCode',
|
|
'customFooterCode',
|
|
'adHomeFeedCode',
|
|
'adHomeInfeedCode',
|
|
'adSidebarCode',
|
|
'adPostSidebarCode',
|
|
'adPostTopCode',
|
|
'adPostInArticleCode',
|
|
'adPostBottomCode'
|
|
])
|
|
|
|
/**
|
|
* 관리자 사이트 설정의 전달된 필드만 수정한다.
|
|
* @param {import('h3').H3Event} event - 요청 이벤트
|
|
* @returns {Promise<Object>} 수정된 사이트 설정
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
requireAdminSession(event)
|
|
const body = await readBody(event)
|
|
|
|
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: '사이트 설정 입력 형식이 올바르지 않습니다.'
|
|
})
|
|
}
|
|
|
|
const fields = Object.keys(body)
|
|
if (!fields.length || fields.some((field) => !ALLOWED_SITE_SETTINGS_FIELDS.has(field))) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: '수정할 수 없는 사이트 설정 필드가 포함되어 있습니다.'
|
|
})
|
|
}
|
|
|
|
const currentSettings = await getSiteSettings()
|
|
const parsedBody = parseAdminSiteSettingsInput({
|
|
...currentSettings,
|
|
...body
|
|
})
|
|
|
|
if (!parsedBody.success) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: '사이트 설정 입력 형식이 올바르지 않습니다.'
|
|
})
|
|
}
|
|
|
|
const validatedPatch = Object.fromEntries(fields.map((field) => [
|
|
field,
|
|
parsedBody.data[field]
|
|
]))
|
|
|
|
return updateSiteSettingsPartial(validatedPatch)
|
|
})
|