v1.10.88 품질·보안 개선과 미디어 카탈로그 database 전환을 반영한다.

세션·로그인 제한, ZIP/업로드 스트리밍, CSP 최소 강제와 Report-Only, Playwright 격리 검증을 추가하고 미디어 목록을 reconcile 후 database 모드로 고정한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-22 11:05:40 +09:00
parent 9bc0f0a90e
commit de767095ba
116 changed files with 8128 additions and 2933 deletions

View File

@@ -0,0 +1,93 @@
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)
})