Skip to content

feat: add /api/admin/users/:addr/notes admin endpoint#453

Open
ValJnr-dev1 wants to merge 1 commit into
Predictify-org:mainfrom
ValJnr-dev1:feature/admin-user-notes
Open

feat: add /api/admin/users/:addr/notes admin endpoint#453
ValJnr-dev1 wants to merge 1 commit into
Predictify-org:mainfrom
ValJnr-dev1:feature/admin-user-notes

Conversation

@ValJnr-dev1

Copy link
Copy Markdown

Summary

Implements #289 — per-user admin notes on user accounts.

Adds three endpoints under /api/admin/users/:addr/notes:

Method Path Description
GET /api/admin/users/:addr/notes List all notes for a user (newest-first)
POST /api/admin/users/:addr/notes Create a note ({ note: string, 1–2000 chars }) → 201
DELETE /api/admin/users/:addr/notes/:id Delete a note scoped to both ID + address → 200 / 404

Changes

  • src/routes/admin/users/notes.ts — new route, factory pattern createAdminNotesRouter(opts) matching the freeze/impersonate conventions
  • src/db/schema.tsadminUserNotes table with index on (target_address, created_at DESC)
  • drizzle/migrations/0023_admin_user_notes.sql — migration SQL
  • src/index.ts — mounts the router at /api/admin/users
  • tests/adminUserNotes.test.ts — 30 focused tests

Security

  • All routes require a valid admin JWT (requireAdmin middleware)
  • 60 rpm rate limit per admin token, keyed on Authorization header
  • Zod validation on Stellar address, note body (strict schema, max 2 000 chars), and note UUID
  • DELETE is scoped to both id AND target_address — prevents cross-user note deletion by ID guessing
  • POST and DELETE both write to admin_audit_log for full traceability
  • Structured pino logging with reqId on every handler

Tests

PASS tests/adminUserNotes.test.ts
  requireAdmin guard
    ✓ returns 403 with no Authorization header (GET)
    ✓ returns 403 with a non-admin JWT on GET
    ✓ returns 403 with a JWT signed by the wrong secret on GET
    ✓ returns 403 with an expired JWT on POST
    ✓ returns 403 with no Authorization header (DELETE)
  GET /api/admin/users/:addr/notes
    ✓ returns 200 with an array of notes
    ✓ returns notes with all expected fields
    ✓ returns createdAt as an ISO-8601 string
    ✓ returns an empty array when the user has no notes
    ✓ returns 400 for an invalid Stellar address
    ✓ propagates DB errors as 500 via errorHandler
  POST /api/admin/users/:addr/notes
    ✓ returns 201 with the created note
    ✓ persists the note with the correct targetAddress and adminAddress
    ✓ writes an audit log row on success
    ✓ returns 400 for an invalid Stellar address
    ✓ returns 400 when note is missing
    ✓ returns 400 when note is an empty string
    ✓ returns 400 when note exceeds 2000 characters
    ✓ accepts a note of exactly 2000 characters
    ✓ rejects unknown body fields (strict schema)
    ✓ propagates DB insert errors as 500
  DELETE /api/admin/users/:addr/notes/:id
    ✓ returns 200 with { deleted: true } on success
    ✓ writes an audit log row on success
    ✓ returns 404 when the note does not exist for that address
    ✓ does not write audit log when note is not found
    ✓ returns 400 for an invalid Stellar address
    ✓ returns 400 for an invalid note UUID
    ✓ propagates DB delete errors as 500
  rate limiting
    ✓ returns 429 after the per-admin limit is exceeded
    ✓ includes RateLimit headers on successful responses

Tests: 30 passed, 30 total

Lint: ✅ clean (eslint src/**/*.ts exit 0)

Closes #289

Add GET/POST/DELETE /api/admin/users/:addr/notes endpoint.

- New admin_user_notes table (migration 0023) with index on
  (target_address, created_at DESC) for efficient listing
- Route: src/routes/admin/users/notes.ts — factory pattern,
  requireAdmin guard, zod param+body validation, 60 rpm rate limit
- POST returns 201 with the created note; DELETE is scoped to both
  note ID and target address to prevent cross-user deletion
- Both mutating operations write to admin_audit_log
- Structured pino logging with correlation IDs on every handler
- 30 focused tests covering auth guard, happy paths, validation,
  audit trail, 404, 500 error propagation, and rate limiting
- Lint: clean; all 30 new tests pass; pre-existing suite failures
  unchanged (confirmed on main before this change)

Closes Predictify-org#289
@drips-wave

drips-wave Bot commented Jul 24, 2026

Copy link
Copy Markdown

@ValJnr-dev1 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add /api/admin/users/:addr/notes admin endpoint

1 participant