feat: PocketVeto v1.0.0 — initial public release
Self-hosted US Congress monitoring platform with AI policy briefs, bill/member/topic follows, ntfy + RSS + email notifications, alignment scoring, collections, and draft-letter generator. Authored by: Jack Levy
This commit is contained in:
158
TROUBLESHOOTING.md
Normal file
158
TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# Troubleshooting
|
||||
|
||||
Common issues encountered during development and deployment of PocketVeto.
|
||||
|
||||
---
|
||||
|
||||
## 502 Bad Gateway after rebuilding a container
|
||||
|
||||
**Symptom**
|
||||
|
||||
All API calls return 502. nginx error log shows:
|
||||
|
||||
```
|
||||
connect() failed (111: Connection refused) while connecting to upstream,
|
||||
upstream: "http://172.18.0.X:8000/api/..."
|
||||
```
|
||||
|
||||
The IP in the error is the *old* IP of the container before the rebuild.
|
||||
|
||||
**Root cause**
|
||||
|
||||
When nginx uses `upstream` blocks, it resolves hostnames once at process startup and caches the result for the lifetime of the process. Rebuilding a container (e.g. `docker compose build api && docker compose up -d api`) assigns it a new Docker network IP. nginx still holds the old IP and all connections are refused.
|
||||
|
||||
**Immediate fix**
|
||||
|
||||
```bash
|
||||
docker compose restart nginx
|
||||
```
|
||||
|
||||
This forces nginx to re-resolve all upstream hostnames from Docker's internal DNS (`127.0.0.11`).
|
||||
|
||||
**Permanent fix (already applied)**
|
||||
|
||||
Replace `upstream` blocks with `set $variable` in `proxy_pass`. nginx only activates the `resolver` directive when a variable is used — making it re-resolve on each request cycle (every `valid=N` seconds).
|
||||
|
||||
```nginx
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
# BAD — resolves once at startup, caches forever
|
||||
upstream api {
|
||||
server api:8000;
|
||||
}
|
||||
location /api/ {
|
||||
proxy_pass http://api;
|
||||
}
|
||||
|
||||
# GOOD — re-resolves via resolver every 10 s
|
||||
location /api/ {
|
||||
set $api http://api:8000;
|
||||
proxy_pass $api;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Wrong service name for docker compose exec
|
||||
|
||||
The API service is named `api` in `docker-compose.yml`, not `backend`.
|
||||
|
||||
```bash
|
||||
# Wrong
|
||||
docker compose exec backend alembic upgrade head
|
||||
|
||||
# Correct
|
||||
docker compose exec api alembic upgrade head
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Alembic migration not applied after rebuild
|
||||
|
||||
If a new migration file was added after the last image build, the API container won't have it baked in. The container runs `alembic upgrade head` at startup from the built image.
|
||||
|
||||
**Fix**: rebuild the API image so the new migration file is included, then restart:
|
||||
|
||||
```bash
|
||||
docker compose build api && docker compose up -d api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Wrong postgres user
|
||||
|
||||
The database superuser is `congress` (set via `POSTGRES_USER` in `.env` / `docker-compose.yml`), not the default `postgres`.
|
||||
|
||||
```bash
|
||||
# Wrong
|
||||
docker compose exec postgres psql -U postgres pocketveto
|
||||
|
||||
# Correct
|
||||
docker compose exec postgres psql -U congress pocketveto
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend changes not showing after editing source files
|
||||
|
||||
The frontend runs as a production Next.js build (`NODE_ENV=production`) — there is no hot reload. Code changes require a full image rebuild:
|
||||
|
||||
```bash
|
||||
docker compose build frontend && docker compose up -d frontend
|
||||
```
|
||||
|
||||
Static assets are cache-busted automatically by Next.js (content-hashed filenames), so a hard refresh in the browser is not required after the new container starts.
|
||||
|
||||
---
|
||||
|
||||
## Celery tasks not reflecting code changes
|
||||
|
||||
Celery worker and beat processes also run from the built image. After changing any worker code:
|
||||
|
||||
```bash
|
||||
docker compose build worker beat && docker compose up -d worker beat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checking logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker compose logs -f
|
||||
|
||||
# Single service (last 50 lines)
|
||||
docker compose logs --tail=50 api
|
||||
docker compose logs --tail=50 nginx
|
||||
docker compose logs --tail=50 worker
|
||||
|
||||
# Follow in real time
|
||||
docker compose logs -f api worker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Inspecting the database
|
||||
|
||||
```bash
|
||||
docker compose exec postgres psql -U congress pocketveto
|
||||
```
|
||||
|
||||
Useful queries:
|
||||
|
||||
```sql
|
||||
-- Recent notification events
|
||||
SELECT event_type, bill_id, dispatched_at, created_at
|
||||
FROM notification_events
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 20;
|
||||
|
||||
-- Follow modes per user
|
||||
SELECT u.email, f.follow_type, f.follow_value, f.follow_mode
|
||||
FROM follows f
|
||||
JOIN users u ON u.id = f.user_id
|
||||
ORDER BY u.email, f.follow_type;
|
||||
|
||||
-- Users and their RSS tokens
|
||||
SELECT id, email, rss_token IS NOT NULL AS has_rss_token FROM users;
|
||||
```
|
||||
Reference in New Issue
Block a user