Gateway-neutral SMS inbox scaffold with:
pnpmmonorepo- Express API server (
apps/server) - SQLite canonical schema + JSON
meta - Provider normalization stubs (
mimsms,twilio,smpp) - SSE (
sms.new) canonical event stream - Vite + React 19 inbox UI (
apps/web) - Tailwind CSS v4 setup
docker pull ghcr.io/mrmeaow/devsms:latestpodman pull ghcr.io/mrmeaow/devsms:latestdocker run -d -p 4000:4000 -p 5153:5153 ghcr.io/mrmeaow/devsms:latestapps/server: canonical message API + DB + SSEapps/web: SSR React inbox client
Migration: apps/server/migrations/001_canonical_sms.sql
Core table: sms_messages
- provider-neutral fields (
sender,recipient,body,status,direction) - provider tracking (
provider,provider_message_id) - flexible metadata (
encoding,parts,cost,currency) - campaign fields (
campaign_id,transaction_type) - compliance fields (
retention_policy) - raw provider payload (
metaJSON)
pnpm install
pnpm dev:server
pnpm dev:webServer API: http://localhost:4000
Web UI: http://localhost:5153
SSR web server mode:
pnpm --filter @devsms/web dev:ssrSSR mode URL: http://localhost:4173
Health:
curl http://localhost:4000/healthList inbox rows:
curl "http://localhost:4000/api/sms?limit=100"
curl "http://localhost:4000/api/sms?provider=mimsms"SSE stream:
curl -N http://localhost:4000/api/eventsDelivery simulation (queued -> delivered):
curl -X POST http://localhost:4000/api/sms/simulate-deliveryMiMSMS:
curl -X POST http://localhost:4000/api/sms/send/mimsms \
-H "content-type: application/json" \
-d '{
"SenderName": "GovOTP",
"MobileNumber": "8801712345678",
"Message": "OTP 1234",
"CampaignId": "campaign-1",
"TransactionType": "T"
}'Twilio:
curl -X POST http://localhost:4000/api/sms/send/twilio \
-H "content-type: application/json" \
-d '{
"From": "+12065550000",
"To": "+12065550123",
"Body": "Hello from Twilio stub"
}'SMPP:
curl -X POST http://localhost:4000/api/sms/send/smpp \
-H "content-type: application/json" \
-d '{
"source_addr": "8801700000000",
"destination_addr": "8801712345678",
"short_message": "SMPP test",
"data_coding": 0
}'Canonical lifecycle:
queued -> sent -> deliveredqueued -> failedqueued -> expired
Provider mapping implemented in apps/server/src/status-map.js.
Build and run locally:
docker build -t devsms:local .
docker run --rm -p 4000:4000 -p 5153:5153 devsms:local- API:
http://localhost:4000 - Web (SSR):
http://localhost:5153
GitHub Actions workflow: .github/workflows/ci-image.yml
- Runs build checks on pull requests.
- On push to
mainand tags (v*), builds Docker image and publishes to GHCR:ghcr.io/<owner>/<repo>:<branch>ghcr.io/<owner>/<repo>:sha-...ghcr.io/<owner>/<repo>:latest(default branch only)
apiVersion: apps/v1
kind: Deployment
metadata:
name: devsms
spec:
replicas: 1
selector:
matchLabels:
app: devsms
template:
metadata:
labels:
app: devsms
spec:
containers:
- name: devsms
image: ghcr.io/mrmeaow/devsms:latest
ports:
- containerPort: 4000
- containerPort: 5153Images are built by GitHub Actions using Docker Buildx with layer caching.
Immutable images are available via SHA tags for reproducible deployments.
You're welcome to contribute! Fork it, work on it, make your PR, wait for my review.
Must follow convensional git commits!
Made with ❤️ by mrmeaow
