f0f271e046
* feat(GRO-106): messaging schema + migrations - Add conversations, messages, message_attachments, message_consent_events tables - Add messagingChannelEnum, messageDirectionEnum, messageStatusEnum, messageConsentKindEnum - Extend business_settings with messagingPhoneNumber and telnyxMessagingProfileId columns - Add required indexes and unique constraints with cascade-on-delete FKs - Add migration 0030_messaging.sql * fix(GRO-981): restore journal entries and add DESC to indexes - _journal.json: restore idx 28 (0028_sms_reminders), add idx 29 (0029_db_indexes_constraints), renumber 0030_messaging to idx 30 (was missing 0028 and 0029 entries — they were silently skipped) - schema.ts: add .desc() to conversations.lastMessageAt and messages.createdAt indexes per spec - 0030_messaging.sql: add DESC to both generated index statements * feat(GRO-106): inbound Telnyx webhook + persistence - Add POST /api/webhooks/telnyx/messaging route with HMAC signature verification - Add services/messaging/inbound.ts: findOrCreateConversation, upsertMessage (idempotent on providerMessageId), delivery receipt handling - Register telnyxWebhooksRouter in index.ts (before auth middleware) - Add unit tests for signature validation, find-or-create, idempotent insert, delivery receipt * fix(GRO-982): address all QA blocking failures - #7: Extract validateTelnyxSignature in sms.ts as standalone exported fn, reuse in TelnyxProvider.validateWebhookSignature and telnyx.ts route - #1: Replace uuid v4 import with crypto.randomUUID() (built-in, no dep) - #2: Remove updatedAt from messages update in handleMessageFinalized (no such column exists) - #3: Fix test import path ../../ → ../../../ for telnyx route import - #4: validateTelnyxSignature accepts string | undefined | null to match Hono c.req.header() return type - #5&6: Add null guards for .returning() results in findOrCreateConversation and upsertMessage - #8: Remove dead buildFindOrCreateConversationParams function - #9: Remove unused imports (messageDirectionEnum, messageStatusEnum, resolveBusinessIdByMessagingNumber in test) - #10: Wrap upsertMessage insert in try/catch; unique violation returns {isNew: false} instead of crashing - #11: Add EOF newlines to all modified files * chore: add uuid dependency for messaging services * fix(GRO-982): address 5 test failures in inbound webhook - Fix signature route tests: use /messaging not full mount path - Fix handleMessageReceived mock order: business lookup first - Fix stale mock state: add full mockReset in handleMessageFinalized beforeEach - Fix delivery logic: set delivered for all message.finalized events - Deduplicate test that was accidentally added twice * fix(GRO-982): look up or create client by phone before inserting conversation Fixes FK constraint violation where clientId was set to businessSettings.id or a random UUID. Now looks up clients.phone = clientPhone first; if no match, creates a placeholder client with phone as name and a placeholder email. * fix(GRO-982): address QA round 4 blocking failures - Fix URL in signature tests: use /messaging not full path - Reorder mocks: businessSettings first, then conversations, clients, messages - Add mockDb.mockReset in handleMessageFinalized beforeEach - Remove direction guard: set delivered for any message.finalized * fix(GRO-982): add missing message insert mock in handleMessageReceived test * fix(GRO-982): simplify test mocks to match actual code flow --------- Co-authored-by: groombook-engineer[bot] <269742240+groombook-engineer[bot]@users.noreply.github.com> Co-authored-by: Chris Farhood <chris@farhood.org> Co-authored-by: Paperclip <noreply@paperclip.ing>
44 lines
1.1 KiB
JSON
44 lines
1.1 KiB
JSON
{
|
|
"name": "@groombook/api",
|
|
"version": "0.0.1",
|
|
"private": true,
|
|
"type": "module",
|
|
"scripts": {
|
|
"dev": "tsx watch src/index.ts",
|
|
"build": "tsc",
|
|
"start": "node dist/index.js",
|
|
"lint": "eslint src --ext .ts",
|
|
"typecheck": "tsc --noEmit",
|
|
"test": "vitest run"
|
|
},
|
|
"dependencies": {
|
|
"@aws-sdk/client-s3": "^3.800.0",
|
|
"@aws-sdk/s3-request-presigner": "^3.800.0",
|
|
"@groombook/db": "workspace:*",
|
|
"@groombook/types": "workspace:*",
|
|
"@hono/node-server": "^1.13.7",
|
|
"@hono/zod-validator": "^0.7.6",
|
|
"better-auth": "^1.5.6",
|
|
"hono": "^4.6.17",
|
|
"node-cron": "^3.0.3",
|
|
"nodemailer": "^6.9.16",
|
|
"stripe": "^22.0.0",
|
|
"telnyx": "^1.23.0",
|
|
"uuid": "^11.1.1",
|
|
"zod": "^4.3.6"
|
|
},
|
|
"devDependencies": {
|
|
"@types/node": "^22.10.7",
|
|
"@types/node-cron": "^3.0.11",
|
|
"@types/nodemailer": "^6.4.17",
|
|
"@types/uuid": "^10.0.0",
|
|
"@vitest/coverage-v8": "^3.2.4",
|
|
"eslint": "^9.18.0",
|
|
"tsx": "^4.19.2",
|
|
"typescript": "^5.7.3",
|
|
"typescript-eslint": "^8.20.0",
|
|
"vitest": "^3.2.4"
|
|
},
|
|
"license": "AGPL-3.0-only"
|
|
}
|