fix: fix ui reports
This commit is contained in:
@@ -208,7 +208,12 @@ func (h *AdminHandler) ResolveReport(w http.ResponseWriter, r *http.Request) {
|
|||||||
response.Error(w, http.StatusBadRequest, "type must be post, comment, or user")
|
response.Error(w, http.StatusBadRequest, "type must be post, comment, or user")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resolved, err := h.svc.ResolveReport(r.Context(), reportType, id)
|
var in struct {
|
||||||
|
Note string `json:"note"`
|
||||||
|
}
|
||||||
|
// Body is optional/best-effort; an empty/missing note resolves with no note.
|
||||||
|
_ = json.NewDecoder(r.Body).Decode(&in)
|
||||||
|
resolved, err := h.svc.ResolveReport(r.Context(), reportType, id, strings.TrimSpace(in.Note))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(w, http.StatusInternalServerError, "could not resolve report")
|
response.Error(w, http.StatusInternalServerError, "could not resolve report")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -84,6 +84,13 @@ type AdminReport struct {
|
|||||||
ReporterHandle *string `json:"reporter_handle"`
|
ReporterHandle *string `json:"reporter_handle"`
|
||||||
ReporterName *string `json:"reporter_name"`
|
ReporterName *string `json:"reporter_name"`
|
||||||
|
|
||||||
|
// ResolutionNote is the admin's note left when resolving (nil while open).
|
||||||
|
ResolutionNote *string `json:"resolution_note"`
|
||||||
|
// TargetRemoved is true when the reported content is currently removed —
|
||||||
|
// a soft-deleted post/comment, or a banned account — so the UI can offer
|
||||||
|
// "Undo" instead of "Delete"/"Ban".
|
||||||
|
TargetRemoved bool `json:"target_removed"`
|
||||||
|
|
||||||
// Target of the report. TargetID is the post/comment/user id; TargetText
|
// Target of the report. TargetID is the post/comment/user id; TargetText
|
||||||
// is the post caption, comment body, or reported user's handle so the
|
// is the post caption, comment body, or reported user's handle so the
|
||||||
// admin sees what was flagged without a second fetch. TargetAuthorHandle
|
// admin sees what was flagged without a second fetch. TargetAuthorHandle
|
||||||
|
|||||||
@@ -219,6 +219,7 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
|
|||||||
SELECT r.id, r.category, r.reason, r.created_at, r.resolved_at,
|
SELECT r.id, r.category, r.reason, r.created_at, r.resolved_at,
|
||||||
ru.handle, ru.name,
|
ru.handle, ru.name,
|
||||||
r.post_id, p.caption, pu.handle,
|
r.post_id, p.caption, pu.handle,
|
||||||
|
r.resolution_note, COALESCE(p.deleted_at IS NOT NULL, false),
|
||||||
COUNT(*) OVER() AS total
|
COUNT(*) OVER() AS total
|
||||||
FROM post_reports r
|
FROM post_reports r
|
||||||
LEFT JOIN users ru ON ru.id = r.reporter_id
|
LEFT JOIN users ru ON ru.id = r.reporter_id
|
||||||
@@ -234,6 +235,7 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
|
|||||||
SELECT r.id, r.category, r.reason, r.created_at, r.resolved_at,
|
SELECT r.id, r.category, r.reason, r.created_at, r.resolved_at,
|
||||||
ru.handle, ru.name,
|
ru.handle, ru.name,
|
||||||
r.comment_id, c.body, cu.handle,
|
r.comment_id, c.body, cu.handle,
|
||||||
|
r.resolution_note, COALESCE(c.deleted_at IS NOT NULL, false),
|
||||||
COUNT(*) OVER() AS total
|
COUNT(*) OVER() AS total
|
||||||
FROM comment_reports r
|
FROM comment_reports r
|
||||||
LEFT JOIN users ru ON ru.id = r.reporter_id
|
LEFT JOIN users ru ON ru.id = r.reporter_id
|
||||||
@@ -248,8 +250,9 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
|
|||||||
sql = `
|
sql = `
|
||||||
SELECT r.id, r.category, r.reason, r.created_at, r.resolved_at,
|
SELECT r.id, r.category, r.reason, r.created_at, r.resolved_at,
|
||||||
ru.handle, ru.name,
|
ru.handle, ru.name,
|
||||||
r.reported_id, tu.handle, tu.handle
|
r.reported_id, tu.handle, tu.handle,
|
||||||
, COUNT(*) OVER() AS total
|
r.resolution_note, COALESCE(tu.banned_at IS NOT NULL, false),
|
||||||
|
COUNT(*) OVER() AS total
|
||||||
FROM user_reports r
|
FROM user_reports r
|
||||||
LEFT JOIN users ru ON ru.id = r.reporter_id
|
LEFT JOIN users ru ON ru.id = r.reporter_id
|
||||||
LEFT JOIN users tu ON tu.id = r.reported_id
|
LEFT JOIN users tu ON tu.id = r.reported_id
|
||||||
@@ -274,7 +277,8 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
|
|||||||
rep := model.AdminReport{Type: reportType}
|
rep := model.AdminReport{Type: reportType}
|
||||||
if err := rows.Scan(&rep.ID, &rep.Category, &rep.Reason, &rep.CreatedAt, &rep.ResolvedAt,
|
if err := rows.Scan(&rep.ID, &rep.Category, &rep.Reason, &rep.CreatedAt, &rep.ResolvedAt,
|
||||||
&rep.ReporterHandle, &rep.ReporterName,
|
&rep.ReporterHandle, &rep.ReporterName,
|
||||||
&rep.TargetID, &rep.TargetText, &rep.TargetAuthorHandle, &total); err != nil {
|
&rep.TargetID, &rep.TargetText, &rep.TargetAuthorHandle,
|
||||||
|
&rep.ResolutionNote, &rep.TargetRemoved, &total); err != nil {
|
||||||
return nil, 0, fmt.Errorf("scan admin report: %w", err)
|
return nil, 0, fmt.Errorf("scan admin report: %w", err)
|
||||||
}
|
}
|
||||||
out = append(out, rep)
|
out = append(out, rep)
|
||||||
@@ -282,10 +286,10 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
|
|||||||
return out, total, rows.Err()
|
return out, total, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveReport stamps resolved_at=now() on a still-open report of the given
|
// ResolveReport stamps resolved_at=now() and stores the admin's note on a
|
||||||
// type. Idempotent and one-way (the WHERE guard). Returns true when this call
|
// still-open report of the given type. Idempotent and one-way (the WHERE
|
||||||
// is the one that resolved it.
|
// guard). Returns true when this call is the one that resolved it.
|
||||||
func (r *AdminRepo) ResolveReport(ctx context.Context, reportType, id string) (bool, error) {
|
func (r *AdminRepo) ResolveReport(ctx context.Context, reportType, id, note string) (bool, error) {
|
||||||
var table string
|
var table string
|
||||||
switch reportType {
|
switch reportType {
|
||||||
case "post":
|
case "post":
|
||||||
@@ -299,8 +303,8 @@ func (r *AdminRepo) ResolveReport(ctx context.Context, reportType, id string) (b
|
|||||||
}
|
}
|
||||||
// table is from a fixed whitelist above — safe to interpolate.
|
// table is from a fixed whitelist above — safe to interpolate.
|
||||||
tag, err := r.pg.Pool.Exec(ctx,
|
tag, err := r.pg.Pool.Exec(ctx,
|
||||||
fmt.Sprintf(`UPDATE %s SET resolved_at = now() WHERE id = $1 AND resolved_at IS NULL`, table),
|
fmt.Sprintf(`UPDATE %s SET resolved_at = now(), resolution_note = $2 WHERE id = $1 AND resolved_at IS NULL`, table),
|
||||||
id,
|
id, note,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("admin resolve %s report: %w", reportType, err)
|
return false, fmt.Errorf("admin resolve %s report: %w", reportType, err)
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ func (s *AdminService) ReplyFeedback(ctx context.Context, id, message string) (f
|
|||||||
// ResolveReport stamps a report resolved. found=true when this call resolved a
|
// ResolveReport stamps a report resolved. found=true when this call resolved a
|
||||||
// previously-open report (idempotent: a no-op on an already-resolved/unknown id
|
// previously-open report (idempotent: a no-op on an already-resolved/unknown id
|
||||||
// returns false).
|
// returns false).
|
||||||
func (s *AdminService) ResolveReport(ctx context.Context, reportType, id string) (bool, error) {
|
func (s *AdminService) ResolveReport(ctx context.Context, reportType, id, note string) (bool, error) {
|
||||||
return s.repo.ResolveReport(ctx, reportType, id)
|
return s.repo.ResolveReport(ctx, reportType, id, note)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Moderation actions ──────────────────────────────────────────────────────
|
// ─── Moderation actions ──────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
ALTER TABLE post_reports DROP COLUMN IF EXISTS resolution_note;
|
||||||
|
ALTER TABLE comment_reports DROP COLUMN IF EXISTS resolution_note;
|
||||||
|
ALTER TABLE user_reports DROP COLUMN IF EXISTS resolution_note;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-- A resolution note captured when an admin resolves a report (shown in the
|
||||||
|
-- admin reports table). Additive nullable column on each report table.
|
||||||
|
ALTER TABLE post_reports ADD COLUMN IF NOT EXISTS resolution_note TEXT;
|
||||||
|
ALTER TABLE comment_reports ADD COLUMN IF NOT EXISTS resolution_note TEXT;
|
||||||
|
ALTER TABLE user_reports ADD COLUMN IF NOT EXISTS resolution_note TEXT;
|
||||||
Reference in New Issue
Block a user