export const BANNED_WORDS_PATH = '/system/banned-words'; export const BANNED_WORD_NEW_PATH = `${BANNED_WORDS_PATH}/new`; /** 엑셀 업로드 양식 다운로드 — 파일 응답이라 라우트 핸들러가 받는다. */ export const BANNED_WORD_EXCEL_TEMPLATE_PATH = `${BANNED_WORDS_PATH}/excel-template`; export const BANNED_WORD_PAGE_SIZE_OPTIONS = [10, 30, 50] as const; export type BannedWordPageSize = (typeof BANNED_WORD_PAGE_SIZE_OPTIONS)[number]; export const DEFAULT_BANNED_WORD_PAGE_SIZE: BannedWordPageSize = 10; const DEFAULT_PAGE = 1; const MAX_KEYWORD_LENGTH = 100; export type BannedWordQuery = { keyword: string; page: number; pageSize: BannedWordPageSize; }; type RawSearchParams = Record; function readParam(params: RawSearchParams, key: string): string | undefined { const value = params[key]; return Array.isArray(value) ? value[0] : value; } function isBannedWordPageSize(value: number): value is BannedWordPageSize { return (BANNED_WORD_PAGE_SIZE_OPTIONS as readonly number[]).includes(value); } export function parseBannedWordQuery( searchParams: RawSearchParams ): BannedWordQuery { const keywordRaw = readParam(searchParams, 'keyword'); const pageRaw = Number(readParam(searchParams, 'page')); const pageSizeRaw = Number(readParam(searchParams, 'pageSize')); return { keyword: (keywordRaw ?? '').trim().slice(0, MAX_KEYWORD_LENGTH), page: Number.isInteger(pageRaw) && pageRaw > 0 ? pageRaw : DEFAULT_PAGE, pageSize: isBannedWordPageSize(pageSizeRaw) ? pageSizeRaw : DEFAULT_BANNED_WORD_PAGE_SIZE, }; } export function buildBannedWordHref( query: BannedWordQuery, overrides: Partial = {} ): string { const merged = { ...query, ...overrides }; const params = new URLSearchParams(); if (merged.keyword) { params.set('keyword', merged.keyword); } if (merged.pageSize !== DEFAULT_BANNED_WORD_PAGE_SIZE) { params.set('pageSize', String(merged.pageSize)); } if (merged.page !== DEFAULT_PAGE) { params.set('page', String(merged.page)); } const queryString = params.toString(); return queryString ? `${BANNED_WORDS_PATH}?${queryString}` : BANNED_WORDS_PATH; }