Skip to content

Commit f9e0009

Browse files
committed
Initial commit
0 parents  commit f9e0009

26 files changed

Lines changed: 4617 additions & 0 deletions

.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Required
2+
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3+
TWILIO_AUTH_TOKEN=your_auth_token_here
4+
5+
# Optional
6+
TWILIO_PHONE_NUMBER=+15551234567
7+
ADMIN_ENABLED=true
8+
VALIDATE_TWILIO_SIG=true

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
dist/
3+
.wrangler/
4+
.dev.vars
5+
.env
6+
.claude

CLAUDE.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# CLAUDE.md — Avalanche Canada SMS Service
2+
3+
## Project Summary
4+
5+
Serverless SMS service: users text GPS coordinates, receive avalanche forecast summaries from Avalanche Canada. Built with Hono + TypeScript, deployed to Cloudflare Workers or AWS Lambda. See SPECIFICATION.md for full details.
6+
7+
## Commands
8+
9+
- `npm run dev` — start local dev server (Hono)
10+
- `npm test` — run tests (Vitest)
11+
- `npm run typecheck` — run tsc --noEmit
12+
- `npx wrangler deploy` — deploy to Cloudflare Workers
13+
14+
## Architecture
15+
16+
- **Hono** app with routes in `src/routes/` — sms, admin, health
17+
- **Services** in `src/services/` — avalanche-ca API client, Twilio helpers, status checks
18+
- **Libs** in `src/lib/` — coordinate parsing, forecast formatting
19+
- **No database, no caching** — every SMS request fetches fresh from Avalanche Canada API
20+
- **Admin dashboard** shows live health checks of dependent services (Avalanche Canada API, Twilio), not analytics
21+
22+
## Key Design Decisions
23+
24+
- Template-based forecast formatting, not LLM. The template is in `src/lib/format-forecast.ts`.
25+
- SMS replies must be <=160 characters (single SMS segment). Use abbreviated labels (Alp/TL/BT) and short date format.
26+
- Twilio request signature validation is on by default. Set `VALIDATE_TWILIO_SIG=false` for local dev.
27+
- Coordinate parsing accepts: raw decimal degrees, cardinal directions, comma-separated, Apple Maps URLs (`maps.apple.com/?ll=`), Google Maps URLs (`google.com/maps`, `maps.app.goo.gl` shortened links). Shortened Google Maps URLs require following the redirect. Validated to Canadian bounds (lat 40-70, lng -145 to -50).
28+
29+
## Avalanche Canada API
30+
31+
- Forecast by point: `GET https://api.avalanche.ca/forecasts/en/products/point?lat={lat}&long={lng}`
32+
- All regions: `GET https://api.avalanche.ca/forecasts/en/areas`
33+
- The API is public, no auth required.
34+
35+
## Code Style
36+
37+
- TypeScript strict mode
38+
- No classes — use plain functions and objects
39+
- Keep handlers thin: parse input, call service, format output
40+
- All user-facing error messages are friendly, never expose raw errors in SMS replies
41+
- Tests go in `test/` mirroring `src/` structure
42+
43+
## Testing
44+
45+
- Use Vitest
46+
- Unit test coordinate parsing exhaustively (all formats, edge cases, garbage input)
47+
- Unit test forecast formatting with fixture data
48+
- Integration test SMS handler with mocked fetch for Avalanche Canada API
49+
- No need to test Twilio signature validation in unit tests (it's their SDK)
50+
51+
## Environment Variables
52+
53+
- `TWILIO_ACCOUNT_SID` — required
54+
- `TWILIO_AUTH_TOKEN` — required
55+
- `VALIDATE_TWILIO_SIG` — optional, set `false` for dev
56+
- `ADMIN_ENABLED` — optional, set `false` to disable admin routes

README.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Avalanche Canada SMS Service
2+
3+
A serverless application that lets users text their GPS coordinates to a phone number and receive a summary of current avalanche conditions from [Avalanche Canada](https://avalanche.ca) for that location.
4+
5+
## How It Works
6+
7+
1. Text your coordinates (e.g. `51.04, -115.06`) to the service phone number
8+
2. The server identifies which Avalanche Canada forecast region covers that location
9+
3. You receive an SMS reply with the current danger ratings and avalanche problems
10+
11+
Example reply (fits in a single 160-character SMS):
12+
13+
```
14+
Kananaskis
15+
Feb 23
16+
Alp:Considerable TL:Moderate BT:Low
17+
Storm Slabs, Wind Slabs
18+
avalanche.ca
19+
```
20+
21+
## Tech Stack
22+
23+
- **Runtime:** Node.js / TypeScript
24+
- **Framework:** [Hono](https://hono.dev) (runs on Cloudflare Workers or AWS Lambda)
25+
- **SMS:** [Twilio](https://twilio.com)
26+
- **Data Source:** [Avalanche Canada API](https://api.avalanche.ca)
27+
- **Summarization:** Template-based (no LLM)
28+
29+
## Setup
30+
31+
### Prerequisites
32+
33+
- Node.js 20+
34+
- A Twilio account with a phone number
35+
- Cloudflare account (for Workers deployment) or AWS account (for Lambda)
36+
37+
### Install
38+
39+
```bash
40+
npm install
41+
```
42+
43+
### Environment Variables
44+
45+
Copy the example file and fill in your Twilio credentials:
46+
47+
```bash
48+
cp .env.example .env
49+
```
50+
51+
See [.env.example](./.env.example) for all available variables.
52+
53+
### Development
54+
55+
```bash
56+
npm run dev
57+
```
58+
59+
This starts a local Wrangler dev server. Set `VALIDATE_TWILIO_SIG=false` in your `.env` to skip Twilio signature checks during local development.
60+
61+
### Test
62+
63+
```bash
64+
npm test
65+
```
66+
67+
### Deploy to Cloudflare Workers
68+
69+
1. **Authenticate with Cloudflare:**
70+
71+
```bash
72+
npx wrangler login
73+
```
74+
75+
This opens a browser window for OAuth. You only need to do this once.
76+
77+
2. **Set Twilio secrets:**
78+
79+
```bash
80+
npx wrangler secret put TWILIO_ACCOUNT_SID
81+
npx wrangler secret put TWILIO_AUTH_TOKEN
82+
```
83+
84+
You'll be prompted to enter each value. These are stored encrypted and never appear in your code or config.
85+
86+
3. **Deploy:**
87+
88+
```bash
89+
npm run deploy
90+
```
91+
92+
Wrangler will output the deployed URL (e.g. `https://avalanche-canada-sms.<your-subdomain>.workers.dev`).
93+
94+
4. **Configure Twilio webhook:**
95+
96+
In the [Twilio console](https://console.twilio.com/), go to your phone number's configuration and set the incoming message webhook to:
97+
98+
```
99+
https://avalanche-canada-sms.<your-subdomain>.workers.dev/sms/incoming
100+
```
101+
102+
Method: `HTTP POST`
103+
104+
5. **Test end-to-end:** Send an SMS with coordinates to your Twilio number and verify you receive a forecast reply.
105+
106+
**AWS Lambda:** Use your preferred Lambda deployment tool (SST, Serverless Framework, etc.) with the [Hono AWS Lambda adapter](https://hono.dev/docs/getting-started/aws-lambda).
107+
108+
## Admin Dashboard
109+
110+
Access the status dashboard at `/admin` to view live health checks of dependent services:
111+
112+
- **Avalanche Canada API** — verifies the forecast endpoint is responding
113+
- **Twilio** — verifies credentials are valid and the API is reachable
114+
115+
Shows overall status (ok / degraded / down), per-service status with latency, and auto-refreshes every 30 seconds.
116+
117+
## Supported Input Formats
118+
119+
- Raw coordinates: `51.0447 -115.0632` or `51.0447, -115.0632` or `51.0447N 115.0632W`
120+
- iOS location sharing (Apple Maps links)
121+
- Android location sharing (Google Maps links, including shortened `maps.app.goo.gl` URLs)
122+
123+
## Documentation
124+
125+
- [SPECIFICATION.md](./SPECIFICATION.md) — full technical specification
126+
- [CLAUDE.md](./CLAUDE.md) — development guidelines for Claude Code

0 commit comments

Comments
 (0)