Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules
dist
.git
.github
.vercel
.env
.env.*
*.log
coverage
tests
docs
README.md
*.md
public
39 changes: 39 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Multi-stage build for the CoTrackPro Voice Center.
# Build tier compiles TypeScript; runtime tier ships only what's needed
# to run the compiled output. Keeps the final image under ~200MB.

# ── Build stage ──────────────────────────────────────────────────────────
FROM node:20-alpine AS build
WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY tsconfig*.json ./
COPY src ./src
COPY api ./api
COPY scripts ./scripts
RUN npm run build

# Prune devDependencies so the runtime stage only copies what it needs.
RUN npm prune --omit=dev

# ── Runtime stage ────────────────────────────────────────────────────────
FROM node:20-alpine AS runtime
WORKDIR /app

# tini is a tiny init that forwards signals correctly — important for
# clean WebSocket shutdowns when Fly redeploys the machine.
RUN apk add --no-cache tini

COPY --from=build /app/package*.json ./
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist

ENV NODE_ENV=production
ENV PORT=8080
EXPOSE 8080

USER node
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/index.js"]
43 changes: 43 additions & 0 deletions fly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# fly.toml — single-host deployment for the CoTrackPro Voice Center.
#
# Both the HTTP webhook (/call/incoming, /health, /records, …) and the
# Twilio Media Stream WebSocket (/call/stream) run on this one Fly
# machine. SERVER_DOMAIN env var should match the public hostname Fly
# assigns (default: <app>.fly.dev).
#
# `auto_stop_machines = false` keeps the machine always-on — voice calls
# can't tolerate the ~30s cold start that scale-to-zero would introduce.

app = "cotrackpro-talk"
primary_region = "ord"

[build]

[env]
NODE_ENV = "production"
PORT = "8080"

[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = false
auto_start_machines = true
min_machines_running = 1
processes = ["app"]

# Health check used by Fly's load balancer to decide whether to route
# traffic. /health is a tiny JSON endpoint; 5s timeout is generous.
[[http_service.checks]]
interval = "30s"
timeout = "5s"
grace_period = "30s"
method = "GET"
path = "/health"

# Single shared-CPU machine. Plenty for a few concurrent calls. Bump
# to performance-1x or higher (memory_mb 1024+) if you start running
# into OOM under load — call sessions hold a few hundred KB each.
[[vm]]
cpu_kind = "shared"
cpus = 1
memory_mb = 512