From d96b4ae09e7966d2b6bf73200693c8b9623344e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A2=E5=BA=9F=E7=89=A9?= Date: Mon, 5 Oct 2026 12:27:22 +0800 Subject: [PATCH] Allow Chinese media tags and document categories --- packages/wiki-shared/src/i18n/en.json | 8 ++++---- packages/wiki-shared/src/i18n/source.json | 8 ++++---- packages/wiki-shared/src/i18n/zh-CN.json | 8 ++++---- src/client/common.ts | 2 +- src/client/edit/autocomplete.ts | 2 +- src/client/edit/bulk-category.ts | 2 +- src/client/edit/main.ts | 6 +++--- src/client/permissions/permissions-modal.ts | 4 ++-- src/routes/admin-mcp.ts | 4 ++-- src/routes/admin.ts | 6 +++--- src/routes/wiki.ts | 6 +++--- src/utils/mediaTags.ts | 4 ++-- 12 files changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/wiki-shared/src/i18n/en.json b/packages/wiki-shared/src/i18n/en.json index abcb013..08fc260 100644 --- a/packages/wiki-shared/src/i18n/en.json +++ b/packages/wiki-shared/src/i18n/en.json @@ -628,7 +628,7 @@ "m_eba914ae07bfbadf": "Unable to load SVG sanitizer library. Please refresh the page and try again.", "m_16a254e99098adf0": "Spaces will - be converted, and leading/trailing spaces will be- removed. ", "m_5d704cd6c4bfa10f": "< > ^ / \\ . ? Cannot be used.", - "m_a1f6b16b2c3b2a38": "\n \n
\n \n
\n
Only Korean/English letters, numbers, spaces, _, ., - are allowed · up to 20
", + "m_a1f6b16b2c3b2a38": "\n \n
\n \n
\n
Only Chinese/Korean/English letters, numbers, spaces, _, ., - are allowed · up to 20
", "m_ba18fae2953eebf1": "\n
\n \n \n
__VIREO_SLOT_1__
\n __VIREO_SLOT_2__\n
\n ", "m_0a1ac7eec7b927f7": "Upload", "m_e6481000e178501b": "Saved file name: __VIREO_SLOT_0__", @@ -1406,7 +1406,7 @@ "m_00bbefb0317222b2": "You do not have permission to perform this action.", "m_fcfc6676d4778b53": "Edit image page", "m_a3030bf8f16dc63c": "Save", - "m_cbadc087b51a5373": "\n
\n \n \n \n
\n \n
\n
Only Korean/English letters, numbers, spaces, _, ., - are allowed · up to 20
\n
\n ", + "m_cbadc087b51a5373": "\n
\n \n \n \n
\n \n
\n
Only Chinese/Korean/English letters, numbers, spaces, _, ., - are allowed · up to 20
\n
\n ", "m_193f229df3d99e3a": "Saved.", "m_ac66748e68a838f4": "Document does not exist", "m_58d547bd560c8cb9": "\n
  • \n
  • \n ", @@ -1878,7 +1878,7 @@ "m_41e7be2dde07e3b9": "\n
    \n Permission\n
    \n __VIREO_SLOT_2__\n
    \n Membership threshold (N days) is in Admin Console > Wiki Settings Edit ACL membership days Follows global settings.
    Admin only Flag blocks general user edits in bulk.
    \n
    \n ", "m_8edcb1517374d55f": "\n
    \n \n
    \n
    \n Edit ACL\n \n \n \n
    \n __VIREO_SLOT_3__\n
    \n \n \n
    \n ", "m_c5ac0714fceb948d": "Document metadata load failed", - "m_0c53e3964dd0d9de": "\n
    \n
    \n
    \n
    Current document
    \n Apply to this document only\n
    \n __VIREO_SLOT_0__\n
    \n\n
    \n
    \n
    Apply to sub-documents in bulk
    \n Loading…\n
    \n
    \n prefix\n __VIREO_SLOT_1__/**\n
    \n
    \n __VIREO_SLOT_2__\n
    \n\n
    \n
    \n Private\n \n \n \n
    \n
    \n Edit ACL\n \n \n \n
    \n __VIREO_SLOT_3__\n
    \n Category\n \n \n \n \n
    \n
    \n Category\n
    \n \n
    \n Only Hangul/English/Numbers/Spaces/Underscore/Hyphen/Period/Comma are allowed. When saved with automatic rules, it will be automatically assigned to newly created documents under this document.\n
    \n
    \n

    \n The above action applies only to checked sub-documents. Leave as is These items will not change.\n

    \n \n
    \n\n
    \n
    \n
    Related automatic rules
    \n
    \n
    __VIREO_SLOT_4__
    \n
    \n
    \n ", + "m_0c53e3964dd0d9de": "\n
    \n
    \n
    \n
    Current document
    \n Apply to this document only\n
    \n __VIREO_SLOT_0__\n
    \n\n
    \n
    \n
    Apply to sub-documents in bulk
    \n Loading…\n
    \n
    \n prefix\n __VIREO_SLOT_1__/**\n
    \n
    \n __VIREO_SLOT_2__\n
    \n\n
    \n
    \n Private\n \n \n \n
    \n
    \n Edit ACL\n \n \n \n
    \n __VIREO_SLOT_3__\n
    \n Category\n \n \n \n \n
    \n
    \n Category\n
    \n \n
    \n Only Chinese/Hangul/English/Numbers/Spaces/Underscore/Hyphen/Period/Comma are allowed. When saved with automatic rules, it will be automatically assigned to newly created documents under this document.\n
    \n
    \n

    \n The above action applies only to checked sub-documents. Leave as is These items will not change.\n

    \n \n
    \n\n
    \n
    \n
    Related automatic rules
    \n
    \n
    __VIREO_SLOT_4__
    \n
    \n
    \n ", "m_e549e8950801ce52": "'Apply ACL below' was selected but the flag is empty.", "m_93b58fbaf5e77126": "No Changes", "m_88daaeedfe4c69c6": "Saving…", @@ -2335,7 +2335,7 @@ "m_5a85f5d54b42fdd3": "The prefix can be up to 200 characters long.", "m_496b6b1ade3a4e6a": "Control characters cannot be used in the prefix.", "m_1aee0ae203eabd47": "scanPrefixSubpages: disallowed column '__VIREO_SLOT_0__'", - "m_6246136884f91732": "Categories can only contain Korean/English letters/numbers/spaces/commas.", + "m_6246136884f91732": "Categories can only contain Chinese/Korean/English letters/numbers/spaces/commas.", "m_bc7101092992ca04": "Save auto category rule: __VIREO_SLOT_0__ → __VIREO_SLOT_1__", "m_b9e3e99701625ec3": "Rule not found.", "m_865de98deb7295d1": "Delete auto category rule: __VIREO_SLOT_0__", diff --git a/packages/wiki-shared/src/i18n/source.json b/packages/wiki-shared/src/i18n/source.json index 168e5fd..ce7be2f 100644 --- a/packages/wiki-shared/src/i18n/source.json +++ b/packages/wiki-shared/src/i18n/source.json @@ -628,7 +628,7 @@ "m_eba914ae07bfbadf": "SVG 보안 정제 라이브러리를 로드할 수 없습니다. 페이지를 새로고침 후 다시 시도해주세요.", "m_16a254e99098adf0": "공백은 - 로 변환되고, 양 끝의 공백·- 는 제거됩니다. ", "m_5d704cd6c4bfa10f": "< > ^ / \\ . ? 는 사용할 수 없습니다.", - "m_a1f6b16b2c3b2a38": "\n \n
    \n \n
    \n
    한글/영문/숫자/공백/_/./- 만 사용 가능 · 최대 20개
    ", + "m_a1f6b16b2c3b2a38": "\n \n
    \n \n
    \n
    중국어/한글/영문/숫자/공백/_/./- 만 사용 가능 · 최대 20개
    ", "m_ba18fae2953eebf1": "\n
    \n \n \n
    __VIREO_SLOT_1__
    \n __VIREO_SLOT_2__\n
    \n ", "m_0a1ac7eec7b927f7": "업로드", "m_e6481000e178501b": "저장될 파일명: __VIREO_SLOT_0__", @@ -1406,7 +1406,7 @@ "m_00bbefb0317222b2": "이 작업을 수행할 권한이 없습니다.", "m_fcfc6676d4778b53": "이미지 编辑页面", "m_a3030bf8f16dc63c": "保存", - "m_cbadc087b51a5373": "\n
    \n \n \n \n
    \n \n
    \n
    한글/영문/숫자/공백/_/./- 만 사용 가능 · 최대 20개
    \n
    \n ", + "m_cbadc087b51a5373": "\n
    \n \n \n \n
    \n \n
    \n
    중국어/한글/영문/숫자/공백/_/./- 만 사용 가능 · 최대 20개
    \n
    \n ", "m_193f229df3d99e3a": "已保存。", "m_ac66748e68a838f4": "존재하지 않는 문서입니다", "m_58d547bd560c8cb9": "\n
  • \n
  • \n ", @@ -1878,7 +1878,7 @@ "m_41e7be2dde07e3b9": "\n
    \n 권한\n
    \n __VIREO_SLOT_2__\n
    \n 가입일 임계값(N일)은 관리자 콘솔 > 위키 설정의 편집 ACL 가입 일수 전역 설정을 따릅니다.
    관리자 전용 플래그는 일반 사용자 편집을 일괄 차단합니다.
    \n
    \n ", "m_8edcb1517374d55f": "\n
    \n \n
    \n
    \n 편집 ACL\n \n \n \n
    \n __VIREO_SLOT_3__\n
    \n \n \n
    \n ", "m_c5ac0714fceb948d": "문서 메타 로드 실패", - "m_0c53e3964dd0d9de": "\n
    \n
    \n
    \n
    현재 문서
    \n 이 문서에만 적용\n
    \n __VIREO_SLOT_0__\n
    \n\n
    \n
    \n
    하위 문서 일괄 적용
    \n 불러오는 중…\n
    \n
    \n prefix\n __VIREO_SLOT_1__/**\n
    \n
    \n __VIREO_SLOT_2__\n
    \n\n
    \n
    \n 비공개\n \n \n \n
    \n
    \n 편집 ACL\n \n \n \n
    \n __VIREO_SLOT_3__\n
    \n 카테고리\n \n \n \n \n
    \n
    \n 카테고리\n
    \n \n
    \n 한글/영문/숫자/공백/언더바/하이픈/마침표/쉼표만 입력 가능. 자동 규칙으로 저장 시, 이후 이 문서 하위에 새로 생성되는 문서에 자동 부여됩니다.\n
    \n
    \n

    \n 체크된 하위 문서에만 위 액션이 적용됩니다. 그대로 인 항목은 변경되지 않습니다.\n

    \n \n
    \n\n
    \n
    \n
    관련 자동 규칙
    \n
    \n
    __VIREO_SLOT_4__
    \n
    \n
    \n ", + "m_0c53e3964dd0d9de": "\n
    \n
    \n
    \n
    현재 문서
    \n 이 문서에만 적용\n
    \n __VIREO_SLOT_0__\n
    \n\n
    \n
    \n
    하위 문서 일괄 적용
    \n 불러오는 중…\n
    \n
    \n prefix\n __VIREO_SLOT_1__/**\n
    \n
    \n __VIREO_SLOT_2__\n
    \n\n
    \n
    \n 비공개\n \n \n \n
    \n
    \n 편집 ACL\n \n \n \n
    \n __VIREO_SLOT_3__\n
    \n 카테고리\n \n \n \n \n
    \n
    \n 카테고리\n
    \n \n
    \n 중국어/한글/영문/숫자/공백/언더바/하이픈/마침표/쉼표만 입력 가능. 자동 규칙으로 저장 시, 이후 이 문서 하위에 새로 생성되는 문서에 자동 부여됩니다.\n
    \n
    \n

    \n 체크된 하위 문서에만 위 액션이 적용됩니다. 그대로 인 항목은 변경되지 않습니다.\n

    \n \n
    \n\n
    \n
    \n
    관련 자동 규칙
    \n
    \n
    __VIREO_SLOT_4__
    \n
    \n
    \n ", "m_e549e8950801ce52": "'아래 ACL 적용' 을 선택했지만 플래그가 비어 있습니다.", "m_93b58fbaf5e77126": "변경 사항이 없습니다.", "m_88daaeedfe4c69c6": "저장 중…", @@ -2335,7 +2335,7 @@ "m_5a85f5d54b42fdd3": "prefix 는 최대 200자까지 입력할 수 있습니다.", "m_496b6b1ade3a4e6a": "prefix 에 제어문자를 사용할 수 없습니다.", "m_1aee0ae203eabd47": "scanPrefixSubpages: disallowed column '__VIREO_SLOT_0__'", - "m_6246136884f91732": "카테고리에는 한글/영문/숫자/공백/쉼표만 사용할 수 있습니다.", + "m_6246136884f91732": "카테고리에는 중국어/한글/영문/숫자/공백/쉼표만 사용할 수 있습니다.", "m_bc7101092992ca04": "자동 카테고리 규칙 저장: __VIREO_SLOT_0__ → __VIREO_SLOT_1__", "m_b9e3e99701625ec3": "규칙을 찾을 수 없습니다.", "m_865de98deb7295d1": "자동 카테고리 규칙 삭제: __VIREO_SLOT_0__", diff --git a/packages/wiki-shared/src/i18n/zh-CN.json b/packages/wiki-shared/src/i18n/zh-CN.json index ebc37de..d404a73 100644 --- a/packages/wiki-shared/src/i18n/zh-CN.json +++ b/packages/wiki-shared/src/i18n/zh-CN.json @@ -628,7 +628,7 @@ "m_eba914ae07bfbadf": "无法加载 SVG 安全清理库。请刷新页面后再试。", "m_16a254e99098adf0": "空格将 - 转换为,并且两端的空格·- 将被删除。 ", "m_5d704cd6c4bfa10f": "< > ^ / \\ . ? 不可使用。", - "m_a1f6b16b2c3b2a38": "\n \n
    \n \n
    \n
    仅可使用韩文/英文/数字/空格/_/./- · 最多 20 个
    ", + "m_a1f6b16b2c3b2a38": "\n \n
    \n \n
    \n
    仅可使用中文/韩文/英文/数字/空格/_/./- · 最多 20 个
    ", "m_ba18fae2953eebf1": "\n
    \n \n \n
    __VIREO_SLOT_1__
    \n __VIREO_SLOT_2__\n
    \n ", "m_0a1ac7eec7b927f7": "上传", "m_e6481000e178501b": "保存的文件名: __VIREO_SLOT_0__", @@ -1406,7 +1406,7 @@ "m_00bbefb0317222b2": "无权执行此操作。", "m_fcfc6676d4778b53": "图片编辑页面", "m_a3030bf8f16dc63c": "保存", - "m_cbadc087b51a5373": "\n
    \n \n \n \n
    \n \n
    \n
    仅可使用韩文/英文/数字/空格/_/./- · 最多 20 个
    \n
    \n ", + "m_cbadc087b51a5373": "\n
    \n \n \n \n
    \n \n
    \n
    仅可使用中文/韩文/英文/数字/空格/_/./- · 最多 20 个
    \n
    \n ", "m_193f229df3d99e3a": "已保存。", "m_ac66748e68a838f4": "文档不存在", "m_58d547bd560c8cb9": "\n
  • \n
  • \n ", @@ -1878,7 +1878,7 @@ "m_41e7be2dde07e3b9": "\n
    \n 权限\n
    \n __VIREO_SLOT_2__\n
    \n 注册日期阈值(N天)在管理员控制台>维基设置中 编辑ACL注册天数 遵循全局设置。
    管理员专用 标记将批量阻止普通用户编辑。
    \n
    \n ", "m_8edcb1517374d55f": "\n
    \n \n
    \n
    \n 编辑ACL\n \n \n \n
    \n __VIREO_SLOT_3__\n
    \n \n \n
    \n ", "m_c5ac0714fceb948d": "文档元加载失败", - "m_0c53e3964dd0d9de": "\n
    \n
    \n
    \n
    当前文档
    \n 仅应用于此文档\n
    \n __VIREO_SLOT_0__\n
    \n\n
    \n
    \n
    批量应用到子文档
    \n 正在加载…\n
    \n
    \n 前缀\n __VIREO_SLOT_1__/**\n
    \n
    \n __VIREO_SLOT_2__\n
    \n\n
    \n
    \n 私密\n \n \n \n
    \n
    \n 编辑ACL\n \n \n \n
    \n __VIREO_SLOT_3__\n
    \n 分类\n \n \n \n \n
    \n
    \n 分类\n
    \n \n
    \n 仅可输入韩文/英文/数字/空格/下划线/连字符/句点/逗号。保存为自动规则时,将自动应用于之后在此文档下新创建的文档。\n
    \n
    \n

    \n 仅对选中的子文档应用上述操作。 保持原样 这些项目不会更改。\n

    \n \n
    \n\n
    \n
    \n
    相关自动规则
    \n
    \n
    __VIREO_SLOT_4__
    \n
    \n
    \n ", + "m_0c53e3964dd0d9de": "\n
    \n
    \n
    \n
    当前文档
    \n 仅应用于此文档\n
    \n __VIREO_SLOT_0__\n
    \n\n
    \n
    \n
    批量应用到子文档
    \n 正在加载…\n
    \n
    \n 前缀\n __VIREO_SLOT_1__/**\n
    \n
    \n __VIREO_SLOT_2__\n
    \n\n
    \n
    \n 私密\n \n \n \n
    \n
    \n 编辑ACL\n \n \n \n
    \n __VIREO_SLOT_3__\n
    \n 分类\n \n \n \n \n
    \n
    \n 分类\n
    \n \n
    \n 仅可输入中文/韩文/英文/数字/空格/下划线/连字符/句点/逗号。保存为自动规则时,将自动应用于之后在此文档下新创建的文档。\n
    \n
    \n

    \n 仅对选中的子文档应用上述操作。 保持原样 这些项目不会更改。\n

    \n \n
    \n\n
    \n
    \n
    相关自动规则
    \n
    \n
    __VIREO_SLOT_4__
    \n
    \n
    \n ", "m_e549e8950801ce52": "选择了“应用下列ACL”,但标记为空。", "m_93b58fbaf5e77126": "无更改", "m_88daaeedfe4c69c6": "正在保存…", @@ -2335,7 +2335,7 @@ "m_5a85f5d54b42fdd3": "前缀最多可输入 200 个字符。", "m_496b6b1ade3a4e6a": "前缀不能使用控制字符。", "m_1aee0ae203eabd47": "scanPrefixSubpages:不允许的列 '__VIREO_SLOT_0__'", - "m_6246136884f91732": "类别只能使用韩文/英文/数字/空格/逗号。", + "m_6246136884f91732": "类别只能使用中文/韩文/英文/数字/空格/逗号。", "m_bc7101092992ca04": "自动分类规则保存: __VIREO_SLOT_0__ → __VIREO_SLOT_1__", "m_b9e3e99701625ec3": "找不到规则。", "m_865de98deb7295d1": "自动分类规则删除: __VIREO_SLOT_0__", diff --git a/src/client/common.ts b/src/client/common.ts index 5e37edf..9fc5b74 100644 --- a/src/client/common.ts +++ b/src/client/common.ts @@ -100,7 +100,7 @@ function escapeHtml(str) { // ── 미디어 태그 입력 위젯 (카테고리 입력과 동일한 UX) ── // 컨테이너에 버블 UI + 자동완성(/api/media/search-tags)을 장착한다. // 업로드 모달, 이미지 검색 모달, 이미지 문서 편집 모달에서 공통 사용한다. -const MEDIA_TAG_VALID_RE = /^[가-힣a-zA-Z0-9 _.-]+$/; +const MEDIA_TAG_VALID_RE = /^[\p{Script=Han}가-힣a-zA-Z0-9 _.-]+$/u; function mountMediaTagInput({ container, input, initial }) { const tags = Array.isArray(initial) ? initial.slice() : []; diff --git a/src/client/edit/autocomplete.ts b/src/client/edit/autocomplete.ts index 409b53c..a3532e5 100644 --- a/src/client/edit/autocomplete.ts +++ b/src/client/edit/autocomplete.ts @@ -1580,7 +1580,7 @@ async function addCategoryTag(tag: string): Promise { if (!window.categoryTags) window.categoryTags = []; if (!window.categoryAclChoices) window.categoryAclChoices = {}; - if (!/^[가-힣a-zA-Z0-9\s_.-]+$/.test(cleanTag)) { + if (!/^[\p{Script=Han}가-힣a-zA-Z0-9\s_.-]+$/u.test(cleanTag)) { window.Swal?.fire({ icon: 'warning', title: ui("m_63d43c7f9b539745"), diff --git a/src/client/edit/bulk-category.ts b/src/client/edit/bulk-category.ts index 1cfe133..3e48cbd 100644 --- a/src/client/edit/bulk-category.ts +++ b/src/client/edit/bulk-category.ts @@ -150,7 +150,7 @@ function hookRuleDeleteButtons(container: HTMLElement, relatedTo: string) { // 카테고리 칩 한 항목의 형식 검증 — 백엔드 CATEGORY_PATTERN 이 콤마 포함 문자열 // 전체에 한글/영문/숫자/공백/쉼표만 허용하므로, 칩 단위로는 콤마를 제외한 // 한글/영문/숫자/공백만 허용한다. -const BULK_CAT_TAG_RE = /^[가-힣a-zA-Z0-9\s]+$/; +const BULK_CAT_TAG_RE = /^[\p{Script=Han}가-힣a-zA-Z0-9\s]+$/u; interface BulkCatAcState { visible: boolean; diff --git a/src/client/edit/main.ts b/src/client/edit/main.ts index 1802919..1cce8da 100644 --- a/src/client/edit/main.ts +++ b/src/client/edit/main.ts @@ -2871,7 +2871,7 @@ document.addEventListener('DOMContentLoaded', async () => { const _autoSlugCat = (() => { if (!slug?.startsWith('카테고리:')) return null; const n = slug.slice('카테고리:'.length).trim(); - return (n && /^[가-힣a-zA-Z0-9\s]+$/.test(n)) ? n : null; + return (n && /^[\p{Script=Han}가-힣a-zA-Z0-9\s]+$/u.test(n)) ? n : null; })(); if (_autoSlugCat && !categoryTags.includes(_autoSlugCat)) { categoryTags.unshift(_autoSlugCat); @@ -3041,7 +3041,7 @@ document.addEventListener('DOMContentLoaded', async () => { // 카테고리 문서(카테고리:이름)는 신규 생성 시에도 해당 카테고리를 미리 주입 if (slug?.startsWith('카테고리:')) { const _autoNewCat = slug.slice('카테고리:'.length).trim(); - if (_autoNewCat && /^[가-힣a-zA-Z0-9\s]+$/.test(_autoNewCat) && !categoryTags.includes(_autoNewCat)) { + if (_autoNewCat && /^[\p{Script=Han}가-힣a-zA-Z0-9\s]+$/u.test(_autoNewCat) && !categoryTags.includes(_autoNewCat)) { categoryTags.unshift(_autoNewCat); document.getElementById('categoryInput').value = categoryTags.join(','); syncStateToWindow(); @@ -3954,7 +3954,7 @@ async function savePage() { return; } - if (category && !/^[가-힣a-zA-Z0-9\s,]+$/.test(category)) { + if (category && !/^[\p{Script=Han}가-힣a-zA-Z0-9\s,]+$/u.test(category)) { window.Swal.fire(ui("m_0bc1fb72ae1be5c5"), ui("m_a21df18dbb6dce0c"), 'warning'); return; } diff --git a/src/client/permissions/permissions-modal.ts b/src/client/permissions/permissions-modal.ts index 6f08814..d5f0855 100644 --- a/src/client/permissions/permissions-modal.ts +++ b/src/client/permissions/permissions-modal.ts @@ -696,7 +696,7 @@ export async function openPermissionsModal(rawSlug: string): Promise { const addTag = (tag: string) => { const cleanTag = tag.trim(); if (!cleanTag) return; - if (!/^[가-힣a-zA-Z0-9\s_.-]+$/.test(cleanTag)) { + if (!/^[\p{Script=Han}가-힣a-zA-Z0-9\s_.-]+$/u.test(cleanTag)) { swal.fire({ icon: 'warning', title: ui("m_4907df8dbe60e7cc"), @@ -791,7 +791,7 @@ export async function openPermissionsModal(rawSlug: string): Promise { } if (categoriesAction === 'add' || categoriesAction === 'set') { const raw = readBulkCatInput(); - if (!/^[가-힣a-zA-Z0-9\s_.,-]+$/.test(raw)) { + if (!/^[\p{Script=Han}가-힣a-zA-Z0-9\s_.,-]+$/u.test(raw)) { swal.showValidationMessage(ui("m_35926634b2949979")); return false; } diff --git a/src/routes/admin-mcp.ts b/src/routes/admin-mcp.ts index 4f1e42c..b960460 100644 --- a/src/routes/admin-mcp.ts +++ b/src/routes/admin-mcp.ts @@ -1022,7 +1022,7 @@ export async function dispatchAdminEditTool(c: Context, user: User, toolNam if (slug.startsWith('이미지:')) return asTextResult(ui("m_e02821f6bffe9ca0"), true); if (slug.startsWith('map:')) return asTextResult(ui("m_51f6566a2dc68dac"), true); if (typeof args.content !== 'string') return asTextResult(ui("m_b3104adc425b92b3"), true); if (args.category && typeof args.category === 'string') { - if (!/^[가-힣a-zA-Z0-9\s,]+$/.test(args.category)) { + if (!/^[\p{Script=Han}가-힣a-zA-Z0-9\s,]+$/u.test(args.category)) { return asTextResult(ui("m_ecc6b4ed43d3fa11"), true); } } @@ -2178,7 +2178,7 @@ export async function dispatchAdminEditTool(c: Context, user: User, toolNam const trimmedCategory = (args.category as string).trim(); const newCategory = trimmedCategory ? trimmedCategory : null; - if (newCategory && !/^[가-힣a-zA-Z0-9\s,]+$/.test(newCategory)) { + if (newCategory && !/^[\p{Script=Han}가-힣a-zA-Z0-9\s,]+$/u.test(newCategory)) { return asTextResult(ui("m_ecc6b4ed43d3fa11"), true); } diff --git a/src/routes/admin.ts b/src/routes/admin.ts index 66ebf00..b1300a2 100644 --- a/src/routes/admin.ts +++ b/src/routes/admin.ts @@ -1198,7 +1198,7 @@ adminRoutes.put('/category-acl/:name', async (c) => { const currentUser = c.get('user')!; const name = c.req.param('name'); if (!name) return c.json({ error: ui("m_fd04e6fd149684a1") }, 400); - if (!/^[가-힣a-zA-Z0-9\s_.-]+$/.test(name)) { + if (!/^[\p{Script=Han}가-힣a-zA-Z0-9\s_.-]+$/u.test(name)) { return c.json({ error: ui("m_592c3fe662fb1c68") }, 400); } @@ -1304,7 +1304,7 @@ adminRoutes.post('/category-acl/:name/bulk-apply', async (c) => { const currentUser = c.get('user')!; const name = c.req.param('name'); if (!name) return c.json({ error: ui("m_fd04e6fd149684a1") }, 400); - if (!/^[가-힣a-zA-Z0-9\s_.-]+$/.test(name)) { + if (!/^[\p{Script=Han}가-힣a-zA-Z0-9\s_.-]+$/u.test(name)) { return c.json({ error: ui("m_592c3fe662fb1c68") }, 400); } @@ -2095,7 +2095,7 @@ adminRoutes.get('/pages/deleted', async (c) => { // 관리자 전용 (이 라우터 자체가 requireAdmin 미들웨어로 보호됨). const PREFIX_FORBIDDEN_CHARS = /[\x00-\x1F\x7F]/; -const CATEGORY_PATTERN = /^[가-힣a-zA-Z0-9\s,]+$/; +const CATEGORY_PATTERN = /^[\p{Script=Han}가-힣a-zA-Z0-9\s,]+$/u; function normalizeCategoryString(s: string): string { return s diff --git a/src/routes/wiki.ts b/src/routes/wiki.ts index b626fee..daab07a 100644 --- a/src/routes/wiki.ts +++ b/src/routes/wiki.ts @@ -329,7 +329,7 @@ export function getCategoryDocAutoCategory(slug: string): string | null { if (!slug.startsWith(prefix)) return null; const name = slug.slice(prefix.length).trim(); if (!name) return null; - if (!/^[가-힣a-zA-Z0-9\s]+$/.test(name)) return null; + if (!/^[\p{Script=Han}가-힣a-zA-Z0-9\s]+$/u.test(name)) return null; return name; } @@ -1094,7 +1094,7 @@ wiki.post('/w/check-category', requireAuth, async (c) => { if (!raw) { return c.json({ ok: false, reason: 'invalid' as const }); } - if (!/^[가-힣a-zA-Z0-9\s_.-]+$/.test(raw)) { + if (!/^[\p{Script=Han}가-힣a-zA-Z0-9\s_.-]+$/u.test(raw)) { return c.json({ ok: false, reason: 'invalid' as const }); } @@ -2189,7 +2189,7 @@ wiki.put('/w/:slug', requireAuth, async (c) => { // 보안: 카테고리 특수문자 금지 (한글, 영문, 숫자, 공백, 쉼표만 허용) if (body.category) { - const categoryPattern = /^[가-힣a-zA-Z0-9\s,]+$/; + const categoryPattern = /^[\p{Script=Han}가-힣a-zA-Z0-9\s,]+$/u; if (!categoryPattern.test(body.category)) { return c.json({ error: ui("m_a21df18dbb6dce0c") }, 400); } diff --git a/src/utils/mediaTags.ts b/src/utils/mediaTags.ts index 7ea9ce2..f5d5f2a 100644 --- a/src/utils/mediaTags.ts +++ b/src/utils/mediaTags.ts @@ -5,11 +5,11 @@ */ /** - * 태그 문자열 규칙: 카테고리와 동일 (한글/영숫자/공백/_/./-). + * 태그 문자열 규칙: 중국어/한글/영숫자/공백/_/./-. * 최대 20개, 각 50자 이내. trim, 중복 제거, 정규식 통과 항목만 유효. * 입력은 배열 / JSON 배열 문자열 / 쉼표구분 문자열을 모두 허용한다. */ -const TAG_VALID_RE = /^[가-힣a-zA-Z0-9 _.-]+$/; +const TAG_VALID_RE = /^[\p{Script=Han}가-힣a-zA-Z0-9 _.-]+$/u; const TAG_MAX_COUNT = 20; const TAG_MAX_LENGTH = 50;