Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2761,7 +2761,8 @@ func main() {
v1handler.OverrideIPForAdmin(transformed, comments)
}
for i, comment := range comments {
if comment.WrEditCount > 0 {
// 삭제 댓글 tombstone 에는 부가 메타를 얹지 않는다 (#13174 후속)
if comment.WrEditCount > 0 && comment.WrDeletedAt == nil {
transformed[i]["edit_count"] = comment.WrEditCount
}
}
Expand Down
17 changes: 14 additions & 3 deletions internal/handler/v1/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,20 @@ func TransformToV1PostsSummary(posts []*gnuboard.G5Write, noticeIDs map[int]bool
// TransformToV1Comment converts G5Write (comment) to v1 API response format
func TransformToV1Comment(w *gnuboard.G5Write) map[string]any {
depth := len(w.WrCommentReply)
// #13174 후속: 삭제 댓글은 tombstone 만 — 원문·작성자·IP 가 응답에 실리면
// 클라이언트 가림과 무관하게 유출이다. 글(maskedDeletedV1Post)과 동일 원칙.
// depth·post_id·created_at 은 스레드 구조 유지에 필요해 남긴다.
if w.WrDeletedAt != nil {
return map[string]any{
"id": w.WrID,
"post_id": w.WrParent,
"content": "",
"depth": depth,
"created_at": w.WrDatetime.Format(time.RFC3339),
"deleted_at": w.WrDeletedAt.Format(time.RFC3339),
"self_deleted": w.WrDeletedBy != nil && *w.WrDeletedBy == w.MbID,
}
}
result := map[string]any{
"id": w.WrID,
"post_id": w.WrParent,
Expand All @@ -364,9 +378,6 @@ func TransformToV1Comment(w *gnuboard.G5Write) map[string]any {
"updated_at": parseWrLast(w.WrLast, w.WrDatetime),
"is_secret": strings.Contains(w.WrOption, "secret"),
}
if w.WrDeletedAt != nil {
result["deleted_at"] = w.WrDeletedAt.Format(time.RFC3339)
}
if w.WrDeletedBy != nil {
result["deleted_by"] = *w.WrDeletedBy
}
Expand Down
27 changes: 27 additions & 0 deletions internal/handler/v1/transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,30 @@ func TestTransformToV1PostDetailUnmaskedKeepsOriginal(t *testing.T) {
t.Error("일반 상세 tombstone 에 content 가 실렸다")
}
}

// #13174 후속: 삭제 댓글 tombstone — 원문·작성자·IP 서버 drop
func TestTransformToV1CommentDeletedTombstone(t *testing.T) {
deletedAt := time.Now()
author := "writer"
c := &gnuboard.G5Write{
WrID: 3, WrParent: 100, WrContent: "원문", WrName: "닉", MbID: author,
WrIP: "1.2.3.4", WrDatetime: time.Now(),
WrDeletedAt: &deletedAt, WrDeletedBy: &author,
}
result := TransformToV1Comment(c)
want := map[string]bool{
"id": true, "post_id": true, "content": true, "depth": true,
"created_at": true, "deleted_at": true, "self_deleted": true,
}
for k := range result {
if !want[k] {
t.Errorf("삭제 댓글 tombstone 에 예상 밖 키 %q (값 %v)", k, result[k])
}
}
if result["content"] != "" {
t.Errorf("삭제 댓글 원문이 남았다: %v", result["content"])
}
if result["self_deleted"] != true {
t.Errorf("자진삭제인데 self_deleted=%v", result["self_deleted"])
}
}
Loading