feat: add search and analytics modules (closes #1012, #1013, #1014, #1015) - #1125
Closed
devdianax wants to merge 154 commits into
Closed
feat: add search and analytics modules (closes #1012, #1013, #1014, #1015)#1125devdianax wants to merge 154 commits into
devdianax wants to merge 154 commits into
Conversation
…Akanimoh12#836 Akanimoh12#822 - DB: EventLog model - add EventLog model to Prisma schema with migration Akanimoh12#829 - DB: Create Prisma seed script with dev users, tips, and leaderboard snapshot Akanimoh12#834 - Auth: POST /auth/verify - ed25519 signature verification, challenge validation, JWT issuance Akanimoh12#836 - Auth: POST /auth/refresh - refresh token rotation with revocation detection Also fixes: - Add missing User relation fields (notifications, leaderboardSnapshots, streak) - Fix failed test due to wrong API path prefix
…ssues-822-834-836-829 feat: implement issues Akanimoh12#822, Akanimoh12#829, Akanimoh12#834, Akanimoh12#836
Add a requestId middleware that honours an inbound x-request-id header or generates a UUID v4, exposes it on req.id (so pino-http logs the same id), and echoes it back on the x-request-id response header. Include the id in error responses via the global error handler. Closes Akanimoh12#797 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…a model docs Closes Akanimoh12#827 - DB: Add database indexes review pass Closes Akanimoh12#831 - DB: Document the data model (ER overview) Closes Akanimoh12#833 - Auth: POST /auth/challenge Closes Akanimoh12#841 - Auth: Rate limit auth endpoints
Auth: challenge endpoint + rate limiting; DB: indexes + data model docs
Add a pre-aggregated daily analytics table (one row per UTC day) holding totalTips, totalVolume, newUsers and activeUsers, with a unique date so a scheduled job can upsert per-day rows for dashboard trend reads.
Add an append-only AuditLog (actor, action, target, metadata, createdAt) so privileged/admin operations are recorded for accountability, indexed by actor, action and createdAt for querying.
Add a nullable deletedAt to the user-facing resource models (User, ApiKey, Notification, Goal, Subscription) so records can be logically deleted instead of hard-deleted; a non-null value marks the row as deleted. Immutable/on-chain and append-only models (Tip, Refund, EventLog, AuditLog, AnalyticsDaily) and infra tables are intentionally excluded.
Ensure every model carries createdAt @default(now()) and updatedAt @updatedat. Adds the missing timestamps to Tip, IndexerCursor, EventLog, Refund, Notification, XAccount, LeaderboardSnapshot, AuthChallenge, RefreshToken, AnalyticsDaily and AuditLog. The migration backfills new NOT NULL updatedAt columns with CURRENT_TIMESTAMP then drops the default so the database matches the schema (bare @updatedat) with no drift.
…kanimoh12#842, Akanimoh12#849) - Replace authChallenge.delete with deleteMany + count check so the nonce can only be consumed once atomically (count=0 means already used → 401) - Replace user findUnique + create with prisma.user.upsert to eliminate the TOCTOU race on concurrent first logins for the same Stellar address
…ce paths (Akanimoh12#842, Akanimoh12#849) - Swap authChallenge.delete mock for deleteMany returning { count } - Swap user.findUnique + user.create mocks for user.upsert - Add test: nonce already consumed returns 401 - Add test: existing user returned on subsequent login - Add tests: challenge address mismatch, token expiry
Validates the Authorization: Bearer <token> header, verifies the JWT with config.auth.jwtSecret, attaches req.user: AuthUser, and calls next(UnauthorizedError) on any failure (missing header, wrong scheme, invalid or expired token).
Covers: missing Authorization header, non-Bearer scheme, invalid JWT, expired JWT, and valid JWT attaches req.user and calls next.
…ow (Akanimoh12#832) Documents the nonce-sign-verify design decision, token design, sequence diagram, single-use nonce enforcement, upsert user on first login, and trade-offs vs SEP-10 / SIWE.
…alytics-audit-timestamps feat: Add AnalyticsDaily, AuditLog, soft-delete & timestamps to DB models
…+ requireAuth - GET /profiles/by-address/:address — lookup by Stellar address - PATCH /profiles/reactivate — undo soft-delete - POST /profiles/image — base64 image upload, IPFS pin, store CID - POST /tips/prepare — build unsigned Soroban tip tx via RPC - requireAuth middleware (JWT verification) - profileImageCid on User model + migration - OpenAPI docs with security scheme - Fix pre-existing test mock hoisting issues
…sues Profiles & Tips: by-address lookup, reactivate, image upload, prepare tx
Auth: Nonce single-use enforcement, account creation on first login, requireAuth middleware, and challenge-response ADR
…mpose (Akanimoh12#795 Akanimoh12#791) Closes Akanimoh12#795 Closes Akanimoh12#791 Closes Akanimoh12#887 Closes Akanimoh12#888
…-887-888 test(backend): add Supertest helper and wire app service to docker-compose
Add tip read APIs and the off-chain indexer poll loop. Tips module: - GET /tips/:id returns a single tip (404 when missing) - GET /profiles/:username/tips lists tips received by a profile - GET /users/me/tips/sent lists tips sent by the authenticated user - Cursor pagination (limit/cursor) and Zod-validated inputs; BigInt amounts serialized to decimal strings Indexer (src/indexer): - Poll getEvents from the stored cursor ledger forward at the configured interval, resuming via the IndexerCursor table - Idempotent projections: EventLog deduped by (txHash, topic, ledger) and Tip upserted on the unique txHash, so replays produce no duplicates - Wired into server bootstrap with graceful shutdown Closes Akanimoh12#872 Closes Akanimoh12#873 Closes Akanimoh12#874 Closes Akanimoh12#893
…n-module feat: implement X (Twitter) integration module
prisma/schema.prisma had duplicate User fields and a duplicate TipStatus enum from an unresolved merge, src/app.ts had two concatenated import blocks with a dangling `}` and duplicate route registrations, and AppError.ts declared ServiceUnavailableError twice. These broke `prisma generate` and `tsc` outright. Also fixes an unrelated x.test.ts type error (unsafe globalThis cast) that was blocking `npm run typecheck`.
Adds the NotificationPreference model (per-user tipReceived/goalReached toggles, defaulting to all-enabled when no row exists) with GET/PATCH /notifications/preferences, a GET /notifications/unread-count endpoint, and a createNotification helper that persists a notification, checks the caller's preferences, and broadcasts it over the realtime gateway. The helper is shared infrastructure for the tip and goal notification triggers. Closes Akanimoh12#966, Akanimoh12#967
recordTip now notifies the receiving creator via createNotification once a tip is newly inserted (never on the existing-tip or P2002 dedupe paths, so replays don't double-notify). Skips self-tips and recipients without an off-chain User row, and never lets a notification failure block the tip response. Closes Akanimoh12#963
projectGoalReached now calls createNotification once the goal transitions into COMPLETED. The transition is detected by comparing against the row's prior status rather than event replay state, so re-processing the same ledger never creates a duplicate notification. Closes Akanimoh12#964
… and debounced recomputation ## Summary Implements the credit scoring system for Stellar Tipz with the following improvements: ### Issue Akanimoh12#912: Credit Score Formula Module (Pure Functions) - Create `credit.formula.ts` with pure, deterministic scoring functions - All formula functions are side-effect free and fully testable - Implement component calculations: tip volume, X metrics, account age, streak bonus - Pure formula is separated from database/cache operations ### Issue Akanimoh12#916: Credit Score Breakdown / Factors - Create `credit.factors.ts` to expose contributing factors and weights - Provide transparent breakdown of how each factor contributes to the score - API-ready methods to format factor information for responses - Clear documentation of weights, divisors, and caps ### Issue Akanimoh12#921: Credit Score Weights via Config - Create `credit.config.ts` to load weights from environment variables - Support configurable weights, divisors, and caps via env vars - All configuration is validated and has sensible defaults - Easy to adjust scoring algorithm without code changes ### Issue Akanimoh12#918: Credit Score Recompute on New Tip - Implement debounced recomputation mechanism in `credit.service.ts` - Add `scheduleRecomputeCreditScore()` for debounced tip-triggered updates - Multiple rapid tips result in single recomputation (5s debounce) - Prevents excessive database writes while maintaining fresh scores ### Technical Details - Formula is pure and configuration-driven for maximum flexibility - Updated environment configuration with new credit score parameters - Comprehensive unit tests for all formula functions and edge cases - Updated existing tests to verify debouncing behavior - Follows module conventions established in the codebase ### Testing - Added `credit.formula.test.ts` with 30+ tests covering: - Individual component calculations (tip, X metrics, age) - Weight application and capping logic - Full formula with all components - Tier assignment and edge cases - Configuration flexibility - Updated `credit.test.ts` to test service-level debouncing - All pure functions are deterministic and testable Closes Akanimoh12#912 Closes Akanimoh12#916 Closes Akanimoh12#918 Closes Akanimoh12#921
…8-921-credit-scoring-improvements feat(credit): pure formula functions, configurable weights, and debounced recomputation
feat:Notifications: preferences, unread count, and tip/goal triggers
…altime event Closes Akanimoh12#941, Akanimoh12#951 - withdrawals: compute a 2% (configurable via WITHDRAWAL_FEE_BPS) fee on prepareWithdrawal, send the net amount on-chain, and return fee/netAmount in the response. - realtime: add a typed `balance.updated` Socket.IO event, auth-enforced via the existing user:<id> room, and emit it after a tip is confirmed. - fix pre-existing merge-artifact duplication (app.ts imports/mounts, AppError.ts ServiceUnavailableError, prisma schema.prisma User model and TipStatus enum) that broke the build for every test on this branch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…heartbeat config + docs - Add a shared typed contract for Socket.IO events and payloads (SocketData, connected/error events) so client and server agree on shapes. - Reuse the REST module's verifyAccessToken for the socket auth handshake instead of duplicating JWT verification. - Throttle new connections per IP and client events per socket with a small in-memory sliding-window limiter; emit a typed RATE_LIMITED error. - Configure explicit pingInterval/pingTimeout for heartbeat detection and document heartbeat + client reconnection behavior in docs/REALTIME.md. - Wire initRealtime(httpServer) into server.ts — it was implemented but never actually started. - Remove a duplicate, unwired realtime.* implementation left over from a prior merge and consolidate on the one already used by tips.controller.ts. - Fix duplicate import/enum/model-field blocks in app.ts, schema.prisma, and AppError.ts (merge artifacts) that broke `tsc`/`prisma generate` repo-wide. The notifications listing endpoint (GET /notifications, paginated) was already fully implemented and tested on this branch; no changes needed. Closes Akanimoh12#954 Closes Akanimoh12#955 Closes Akanimoh12#956 Closes Akanimoh12#959
…ifications-954-955-956-959 feat(realtime): typed event contract, rate limiting, heartbeat + notifications listing
…ime-balance-941-951
…and-realtime-balance-941-951 Withdrawal fee calculation (2%) + realtime balance.updated event
…cast tests - Akanimoh12#965: add a `subscription_charged` notification type, gated by a new per-user `subscriptionCharged` preference (default enabled), and fire it from the indexer's sub_exec projection when a charge is genuinely new (event-log gated, so replays never duplicate it). - Akanimoh12#957: fix gateway.test.ts, which was signing tokens with a stale `sub` claim and asserting a stale error message — both leftovers from a prior auth/error-shape change that left the auth-handshake tests silently broken (one timing out, one failing). Add missing room-broadcast coverage for tip.created and notification.created, which had no tests. - Akanimoh12#961 (POST /notifications/read-all) and Akanimoh12#962 (notification creation service) were already fully implemented and tested on this branch by prior work; verified against the Definition of Done and left as-is. Closes Akanimoh12#961, Akanimoh12#962, Akanimoh12#965, Akanimoh12#957
…thdrawal submit endpoint Implements four backend issues on test-implement-drips: - Realtime: emit leaderboard.updated to a public `leaderboard` room whenever a confirmed tip changes a creator's rank (Akanimoh12#952). - Realtime: attach the Socket.IO Redis adapter (gated by REALTIME_REDIS_ADAPTER_ENABLED) so rooms are shared across horizontally scaled instances (Akanimoh12#948). - Realtime: strengthen per-creator room test coverage with targeted-delivery assertions; the room join/auth implementation already existed (Akanimoh12#949). - Withdrawals: add POST /withdrawals/submit to broadcast a wallet-signed withdrawal transaction and record it as a PENDING withdrawal, idempotent by txHash (Akanimoh12#940). Also fixes a pre-existing bug in tips.test.ts (undefined mockCreateNotification reference / mockUserFindUnique typo) that was crashing the entire test file before any test could run. Closes Akanimoh12#952 Closes Akanimoh12#948 Closes Akanimoh12#949 Closes Akanimoh12#940 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ions-and-realtime feat(backend): notify on subscription charge + fix realtime room-broadcast tests
…oard-redis-withdrawal-submit-940-948-949-952 Implements four backend realtime/withdrawals issues on `test-implement-drips`.
…ate-limit-backoff feat(x): handle X API rate limits with retry/backoff and circuit breaker Akanimoh12#975
- Akanimoh12#945: Add OpenAPI docs for withdrawals and balances endpoints - Akanimoh12#944: Add edge-case tests for withdrawals module (pagination, empty state, zero balance) - Akanimoh12#958: Notifications module skeleton already complete (service, controller, routes, schema, types, tests) - Akanimoh12#960: Add POST /notifications/:id/read route as alias for PATCH Closes Akanimoh12#944, Akanimoh12#945, Akanimoh12#958, Akanimoh12#960
…ifications-944-945-958-960 feat: withdrawals API docs, enhanced tests, notifications POST /:id/read
- Closes Akanimoh12#1014: Search module skeleton with routes, controller, service, schema, types, and tests - Closes Akanimoh12#1015: GET /search/creators endpoint for searching creators by name/username - Closes Akanimoh12#1012: Analytics module with comprehensive Vitest tests - Closes Akanimoh12#1013: Analytics docs with OpenAPI documentation Modules follow existing conventions: - Zod validation, AppError subclasses, Prisma singleton, pino logger - OpenAPI docs via mergeOpenApiPaths - Vitest tests with mocked Prisma client
|
@devdianax 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! 🚀 |
This was referenced Jul 26, 2026
Author
|
Closing — reopening against test-implement-drips instead of main. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the Search and Analytics backend modules following existing module conventions.
Closes #1012
Closes #1013
Closes #1014
Closes #1015
Search module (
backend/src/modules/search/)Analytics module (
backend/src/modules/analytics/)/analytics/dailyand/analytics/summary(Analytics: Analytics docs #1013)Conventions followed
AppErrorsubclasses for HTTP errors (via global error handler)console.log)mergeOpenApiPathssrc/app.ts