Sendi is a Webex to Twilio SMS gateway written in TypeScript that runs directly on Node 22.18+ via native type-stripping (no bundler/transpiler). Each Webex space represents one external SMS contact; inbound MMS is proxied both ways, with delivery-status cards for outbound sends. Highlights: - Express 5 HTTP surface for Twilio /sms and /callback (signature-validated) - webex-node-bot-framework in websocket transport mode for bot control plane - Prisma 6 + SQLite via better-sqlite3 driver adapter - Zod-validated env with fail-fast startup and pino secret redaction - Pino logging: pretty in dev, structured JSON in prod - 13 vitest unit tests (phone normalization, Twilio signature validation, card action dispatch) - Native --require preload patches Node 22's read-only globalThis.navigator so @webex/internal-media-core (transitive) can load Co-authored-by: Cursor <cursoragent@cursor.com>
110 lines
3.1 KiB
SQL
110 lines
3.1 KiB
SQL
-- CreateTable
|
|
CREATE TABLE "Space" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"roomId" TEXT NOT NULL,
|
|
"smsPhone" TEXT NOT NULL,
|
|
"wbxTmPhone" TEXT NOT NULL,
|
|
"smsName" TEXT NOT NULL,
|
|
"title" TEXT NOT NULL,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" DATETIME NOT NULL
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Contact" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"name" TEXT NOT NULL,
|
|
"phone" TEXT NOT NULL,
|
|
"blocked" BOOLEAN NOT NULL DEFAULT false,
|
|
"blockReason" TEXT,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Message" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"sid" TEXT,
|
|
"roomId" TEXT NOT NULL,
|
|
"from" TEXT NOT NULL,
|
|
"body" TEXT NOT NULL,
|
|
"mediaUrls" TEXT NOT NULL,
|
|
"status" TEXT NOT NULL,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT "Message_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Space" ("roomId") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "BulkJob" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"parentId" TEXT NOT NULL,
|
|
"senderEmail" TEXT NOT NULL,
|
|
"text" TEXT NOT NULL,
|
|
"files" TEXT NOT NULL,
|
|
"status" TEXT NOT NULL DEFAULT 'pending',
|
|
"analytics" JSONB,
|
|
"sentAt" DATETIME,
|
|
"completedAt" DATETIME,
|
|
"spaceId" TEXT,
|
|
CONSTRAINT "BulkJob_spaceId_fkey" FOREIGN KEY ("spaceId") REFERENCES "Space" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Survey" (
|
|
"id" TEXT NOT NULL PRIMARY KEY,
|
|
"roomId" TEXT NOT NULL,
|
|
"question" TEXT NOT NULL,
|
|
"endDate" DATETIME NOT NULL,
|
|
"responses" TEXT NOT NULL,
|
|
"completed" BOOLEAN NOT NULL DEFAULT false,
|
|
CONSTRAINT "Survey_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Space" ("roomId") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "OutboundRouting" (
|
|
"email" TEXT NOT NULL PRIMARY KEY,
|
|
"phoneNumber" TEXT NOT NULL,
|
|
"groups" TEXT NOT NULL
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "InboundRouting" (
|
|
"phoneNumber" TEXT NOT NULL PRIMARY KEY,
|
|
"site" TEXT NOT NULL,
|
|
"people" TEXT NOT NULL,
|
|
"keywords" JSONB,
|
|
"outOfOffice" JSONB
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "BlockNumber" (
|
|
"phoneNumber" TEXT NOT NULL PRIMARY KEY,
|
|
"name" TEXT NOT NULL,
|
|
"reason" TEXT NOT NULL,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "PendingMessage" (
|
|
"roomId" TEXT NOT NULL PRIMARY KEY,
|
|
"text" TEXT NOT NULL,
|
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT "PendingMessage_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Space" ("roomId") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Space_roomId_key" ON "Space"("roomId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Space_smsPhone_wbxTmPhone_idx" ON "Space"("smsPhone", "wbxTmPhone");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Contact_phone_key" ON "Contact"("phone");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Message_sid_key" ON "Message"("sid");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Message_roomId_idx" ON "Message"("roomId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Message_createdAt_idx" ON "Message"("createdAt");
|