v1.12.23 대시보드 간소화 및 멤버 작성 이력 탭 추가

This commit is contained in:
2026-10-21 16:37:55 +09:00
parent c8e00c1190
commit 7c95797fd2
18 changed files with 287 additions and 251 deletions

View File

@@ -68,7 +68,7 @@
- 호환 정책은 모든 경로에서 `Content-Security-Policy-Report-Only`로 유지한다. 사용자 사이트 코드·광고·외부 미디어를 차단하지 않은 채 script/style/img/font/media/frame/connect/worker/manifest 위반을 `/api/csp-report`로 보고하며 `Reporting-Endpoints`와 `Report-To`도 같은 origin 수집 주소를 가리킨다.
- `POST /api/csp-report`는 `application/csp-report`, `application/reports+json`, `application/json`, `application/*+json`만 허용하고 요청 본문은 32KB, Reporting API 배열은 최대 20건으로 제한한다. IP 원문을 보관하지 않는 해시 키 기준으로 분당 60회까지만 받는다.
- CSP 위반은 정책 버전, 쿼리·fragment를 제거한 문서 경로, 지시어, 경로·쿼리를 제거한 차단 origin, 브라우저 계열만 `csp_violation_aggregates`에 누적한다. 원문 URL과 User-Agent는 저장하지 않으며 저장 실패가 일반 응답을 실패시키지 않는다. 차단 출처가 비어 있으면 `empty`, `about:blank` 계열은 `about:`, 브라우저 확장 프로그램은 `chrome-extension:`·`moz-extension:`·`safari-web-extension:` 같은 scheme 단위로 분류한다.
- 관리자 대시보드는 CSP Report-Only 위반 집계가 1건 이상 있을 때만 `CSP 관찰` 표를 표시한다. 표는 최근 30일 내 보고된 상위 항목의 지시어, 보고된 출처, 문서 경로, 브라우저 계열, 누적 횟수, 최근 관찰 시각을 보여준다. 횟수는 최초 수집부터 누적이며 실제 최근 30일 발생 수가 아니다. 관찰 목적과 차단 없이 보고만 수집한다는 설명, `empty`·`unknown`의 의미, HTTPS 출처를 폭넓게 허용하는 현재 정책의 관찰 한계를 안내한다. `script-src-elem`, `style-src-attr`, `child-src` 같은 세부 지시어는 각각 `script-src`, `style-src`, `frame-src` 후보로 묶고, `http(s)` origin만 허용 후보로 별도 표시한다. 후보가 없어도 안전성이나 정책 강화 가능성을 확정하지 않는다.
- v1.12.23부터 대시보드의 CSP 관찰 표와 조회·자동 갱신은 제거한다. 기존 보안 헤더·Report-Only 수집·집계·관리자 진단 API는 유지한다. 누적 보고만으로 정책 강화 가능성을 확정하지 않는다.
### 홈 Featured (인덱스)
@@ -1089,10 +1089,15 @@ APP_PORT=43080
- 첫 커밋 이후 변경사항을 커밋할 때마다 패치 버전 증가
- 메이저/마이너 버전은 구조 변경 또는 기능 묶음 단위로 결정
## 관리자 댓글 조회 (v1.12.22)
## 관리자 댓글·회원 작성 이력 조회 (v1.12.23)
- `GET /admin/api/comments`: 관리자 세션 필수. `memberId`(선택 UUID), `page`(기본 1, 1~1000000 정수), `pageSize`(기본 10, 1~100 정수). 잘못된 조건은 400, 미인증은 401.
- `GET /admin/api/comments`: 관리자 세션 필수. `memberId`(선택 UUID), `q`(기본 빈 문자열, trim, 최대 200자, 댓글 내용·게시물 제목 부분 검색), `page`(기본 1, 1~1000000 정수), `pageSize`(기본 10, 1~100 정수). 잘못된 조건은 400, 미인증은 401.
- 응답 `{ items, pagination: { page, pageSize, total, pageCount } }`. 항목은 `id`, `body`, `status`, `parentId`, `createdAt`, `userId`, `username`, `postId`, `postTitle`, `postSlug`, `postStatus` 제공. 모든 상태의 댓글·답글 포함, 작성 시각·ID 내림차순. 범위 초과 페이지는 마지막 페이지로 보정.
- 멤버 상세는 10개씩 이전·다음 이동. 대시보드는 전체 최신 댓글 5개를 30초마다 갱신. 조회 오류는 재시도, 조회 중은 로딩 표시.
- 멤버 상세는 `정보 / 작성한 글 / 작성한 댓글` 탭을 제공하며 정보로 진입한다. 정보 편집값은 탭 이동 시 유지한다. 이력은 선택한 탭에서만 조회하고, 10개씩 번호형 페이지네이션·검색 버튼/Enter로 검색한다. 검색 시 첫 페이지로 돌아가며 최신 작성순으로 전체 이력을 조회한다.
- 대시보드는 최신 댓글 5개를 30초마다 갱신한다. 게시물·작성자·짧은 시각·말줄임 한 줄 내용만 작은 행으로 표시한다. 조회 오류는 재시도하며 이력 조회 중은 로딩 표시.
- 댓글 내용은 일반 텍스트로 표시. 작성자는 멤버 상세, 게시물 제목은 관리자 편집으로 이동. 공개·멤버십 글은 새 창의 `/post/:slug#comments` 링크 제공(공개 화면의 기존 접근 권한 적용).
- 게시글 목록 댓글 열은 기존 `commentCount` 사용. 공개 상태 댓글·답글만 집계.
- `GET /admin/api/members/:id/posts`: 관리자 세션 필수. 회원 UUID와 `q`(trim, 최대 200자), `page`(1~1000000, 기본 1), `pageSize`(1~100, 기본 10) 검증. 잘못된 조건 400, 미인증 401.
- 작성 글은 `posts.author_id`로 필터하고 초안·비공개·멤버십·공개 전체 포함, `created_at DESC, id DESC` 순서. `q`는 제목·주소·요약·본문의 문자 그대로 부분 검색이며 댓글 검색도 `%`, `_`를 와일드카드로 취급하지 않는다.
- 작성 글 응답 `{ items: [{ id, title, slug, status, createdAt, publishedAt }], pagination: { page, pageSize, total, pageCount } }`. 범위 초과 페이지는 마지막으로 보정하고 미작성·검색 결과 없음은 빈 목록 반환.