Skip to content

Feat/notification service 122#140

Open
iyanumajekodunmi756 wants to merge 2 commits into
Lumina-eX:mainfrom
iyanumajekodunmi756:feat/notification-service-122
Open

Feat/notification service 122#140
iyanumajekodunmi756 wants to merge 2 commits into
Lumina-eX:mainfrom
iyanumajekodunmi756:feat/notification-service-122

Conversation

@iyanumajekodunmi756

Copy link
Copy Markdown

Problem Statement
Description
Implement a backend notification system to handle important group and contract-related events, ensuring real-time delivery and persistent storage.

Events

Contract creation
Milestone submission
Escrow release
Dispute creation
Requirements

Store notifications in database with fields: id, userId, eventType, message, timestamp, read
Support read/unread status updates
Provide API endpoints to fetch notifications (with pagination and filtering)
Deliver notifications in real time via WebSocket or push events
Ensure proper error handling and logging for failed delivery attempts
Tasks

Define notification schema in database
Implement notification creation logic triggered by events
Build API endpoints for fetching and updating notifications
Integrate WebSocket/push service for real-time delivery
Add unit tests for notification creation, retrieval, and status updates
Acceptance Criteria

Notifications stored reliably in database with correct event mapping
Read/unread status supported and persisted
Notifications delivered in real time to connected clients
API endpoints documented for frontend integration
Pagination supported for large notification lists
Unit tests pass for backend logic and API endpoints
📈 Expected Impact
High — Would significantly improve user experience

closes #122

Implements the Freelancer Discovery API described in Lumina-eX#121:

  GET /api/freelancers
    ?q=<text>&skills=react,nodejs&minRating=4&page=2&limit=20
    &sort=rating&order=desc
  GET /api/freelancers/<id>

Highlights:
  * lib/freelancerDiscovery.ts encapsulates the SQL builder, pagination,
    sort/filter validation, and row -> API shape mapping.
  * Single-template WHERE fragment with `<null/0 IS 0 OR <pred>>` lets the
    query issue exactly one DB round-trip per request, with every value
    bound as a Postgres parameter (no runtime SQL interpolation of the
    sort column or column name).
  * ORDER BY is a fully-static switch on (sort, order) with literal
    ASC/DESC inlined per branch.
  * Backward compatible with the existing /freelancers page: legacy
    ?rating= alias accepted, response shape unchanged, ?skills accepts
    both repeating and comma-separated values.
  * Structured error responses ({error, code}) for invalid query params
    and downstream failures (400 / 503).
  * GET /api/freelancers/[id] returns { freelancer, reputation } with
    reputation loaded best-effort; null on lookup failure so the page
    still renders.
  * scripts/010-freelancer-discovery-indexes.sql adds a GIN index on
    users.skills (skill-overlap + ILIKE search), a btree on users.rating
    DESC, and a partial index on user_type IN (freelancer, both).
  * 22 unit tests covering parse, validation, list/detail happy paths,
    pagination, fallback COUNT, validation errors, and 503 on DB error.
Implements the Notification Service Backend described in Lumina-eX#122.

Endpoints
  GET    /api/notifications              list w/ pagination, type filter, unread toggle
  PATCH  /api/notifications/[id]/read    mark single read
  POST   /api/notifications/read-all     mark all read (returns updatedCount)
  GET    /api/notifications/stream       SSE real-time push + 25s keep-alive

Layers
  lib/notifications.ts
    * typed NOTIFICATION_EVENT_TYPES whitelist + LEGACY aliases
    * NotificationError with stable {code, message} -> 400 mapping
    * parseNotificationQuery validation (page, limit, type, unreadOnly)
    * mapNotificationRow (snake -> camel, Date -> ISO)
    * createNotification persists + best-effort hub fan-out
    * listNotificationsForUser uses COUNT(*) OVER() so data + total
      arrive in a single round-trip
    * markNotificationRead returns {notification|null} so callers can
      distinguish 404 from 200 without an extra read
    * markAllNotificationsRead uses a CTE so updatedCount only counts
      rows flipped by THIS call (previously inflated by prior reads)
    * NotificationHub singleton: subscribe / publish / unsubscribe /
      subscriberCount for the SSE channel
    * notifyContractCreated / notifyMilestoneSubmitted / Approved /
      notifyEscrowReleased / notifyDisputeCreated event helpers
  lib/auth/middleware.ts
    * new withAuthCtx<Ctx> wrapper for handlers that need the Next.js
      route context (params)
    * new resolveUserIdByWallet(wallet) helper, shared across the
      four notification routes (no more inline duplication)
  app/api/notifications/{route, [id]/read/route, read-all/route,
  stream/route}.ts
    * withAuth / withAuthCtx wrappers
    * granular status mapping (200/400/401/404/500/503)
    * SSE stream with shared cleanup path reachable from BOTH
      request.signal abort and consumer cancel()

Schema (scripts/011-notification-service.sql)
  * event_type CHECK constraint covering worker legacy types
    (info/success/warning) + domain events (contract_created,
    milestone_submitted, milestone_approved, escrow_released,
    escrow_refunded, dispute_created, dispute_resolved)
  * payload JSONB + channel + delivered_at columns
  * composite indexes (user_id, is_read, created_at) and
    (user_id, event_type, created_at) for the GET filters
  * CASE-based backfill so a stray legacy type cannot break
    ADD CONSTRAINT CHECK

Tests
  __tests__/api/notifications.test.ts (34 tests)
  * parseNotificationQuery edge cases
  * mapNotificationRow shape
  * createNotification persists + publishes + tolerates broken
    subscribers + handles empty insert
  * listNotificationsForUser pagination + COUNT(*) OVER() + invalid
    user id
  * markNotificationRead owner-mismatch -> null, success path
  * markAllNotificationsRead CTE returns updated_count
  * NotificationHub subscribe/unsubscribe/isolated-fanout
  * GET /api/notifications happy path, 400 invalid type, 503 DB fail
  * PATCH /api/notifications/[id]/read 200, 404, 400 invalid id,
    404 when wallet does not map to a user
  * POST /api/notifications/read-all 200 + updatedCount
  * GET /api/notifications/stream SSE headers + cancel cleanup
  * event-creation helpers (one notification per recipient)

Validation
  * npx tsc --noEmit -> clean for changed files
  * npx eslint -> 0 errors / 0 warnings for changed files
  * npm run test -- --run __tests__/api/{notifications,
    freelancers}.test.ts __tests__/rbac.test.ts -> 89/89 passing
  * git merge-tree against upstream/main -> no conflicts

Out of scope (intentional, follow-up PRs)
  * wiring notifyContractCreated/notifyMilestoneSubmitted/... into
    escrow.create, milestones.submit, escrow.release and
    dispute.resolve route handlers
  * multi-instance fan-out (Redis pub/sub backplane) for
    horizontally-scaled deployments

Refs Lumina-eX#122
@drips-wave

drips-wave Bot commented Jun 27, 2026

Copy link
Copy Markdown

@iyanumajekodunmi756 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.

[Feature]: Notification Service Backend

1 participant