summaryrefslogtreecommitdiff
path: root/README.md
blob: cf2eecbaa17499b1261d527af40e03c8dc39c4c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# Prism v2

Financial overview app. FastAPI backend (yfinance + SQLite watchlist) and Next.js frontend (Plotly chart, ticker search, market bar, watchlist).

## Stack

- Backend: Python 3.14+, FastAPI, uvicorn, yfinance, SQLite
- Frontend: Node 20+, Next.js (App Router), TypeScript
- Production: systemd + nginx (TLS via certbot)

## Layout

- `backend/app/` — FastAPI app, services, SQLite watchlist
- `frontend/app/` — Next.js App Router; shared types in `frontend/types/api.ts`
- `systemd/` — service units (`prismv2-backend.service`, `prismv2-frontend.service`)
- `nginx/` — TLS server block (`/api/` → :8001, `/` → :3001)
- `scripts/stack.sh` — local dev start/stop/restart/status
- `design-system/` — brand tokens + Prism UI kit (mirrored to `frontend/public/design-system/`)

## Environment

Copy `.env.example` → `.env`. Current feature set works without keys.

- `NEXT_PUBLIC_API_BASE_URL` — leave empty in production (same-origin `/api/*` via nginx)
- `FMP_API_KEY`, `FINNHUB_API_KEY` — reserved for future enrichment

## Local Development

```bash
# Backend
cd backend && python -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/uvicorn app.main:app --reload --host 127.0.0.1 --port 8001

# Frontend
cd frontend && npm install
NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8001 npm run dev -- --hostname 127.0.0.1 --port 3001
```

Or use `./scripts/stack.sh {start|stop|restart|status}` (logs in `.run/logs/`).

## API

- `GET /health`
- `GET /api/search?q=`
- `GET /api/market/indices`
- `GET /api/tickers/{symbol}/overview`
- `GET /api/tickers/{symbol}/history?period=1m|3m|6m|1y|5y`
- `GET|POST|DELETE /api/watchlist[/{symbol}]` (uppercase, capped at 10)

SQLite lives at `backend/data/prism.db`. Backend seeds a `default` profile on startup.

## Production Deployment

Target topology:

- Bare repo at `/srv/git/prism-v2.git` (push target)
- Working tree at `/var/www/prism-v2/` owned by `www-data` (no `.git/` — populated by post-receive hook via `git --work-tree=... checkout -f`)
- Backend on `127.0.0.1:8001`, frontend on `127.0.0.1:3001`
- nginx terminates TLS and reverse-proxies `/api/` → backend, `/` → frontend

Workflow: `git push prod master` on your laptop → post-receive hook checks out the tree into `/var/www/prism-v2/` and runs `scripts/deploy.sh`, which builds + restarts.

### First-time setup

```bash
# 1. Create bare repo
sudo mkdir -p /srv/git && sudo git init --bare /srv/git/prism-v2.git

# 2. Create working tree dir
sudo install -d -o www-data -g www-data /var/www/prism-v2

# 3. Push from your laptop so the working tree gets populated
#    (add the remote on your laptop: git remote add prod user@host:/srv/git/prism-v2.git)
#    git push prod master

# 4. On the server: install systemd units + nginx site + build + start
cd /var/www/prism-v2
sudo ./scripts/deploy.sh --install

# 5. First-time TLS (Certbot edits the nginx server block in-place)
sudo certbot --nginx -d prism.tylerhoang.xyz
sudo systemctl reload nginx

# 6. Install the post-receive hook so future pushes auto-deploy
sudo cp /var/www/prism-v2/scripts/post-receive.sample /srv/git/prism-v2.git/hooks/post-receive
sudo chmod +x /srv/git/prism-v2.git/hooks/post-receive

# 7. Sudoers entry so the git-push user can run deploy.sh without a password
#    (replace GIT_USER with the account that owns /srv/git or receives the push)
echo 'GIT_USER ALL=(root) NOPASSWD: /var/www/prism-v2/scripts/deploy.sh' | sudo tee /etc/sudoers.d/prism-v2-deploy
sudo chmod 0440 /etc/sudoers.d/prism-v2-deploy
```

### Subsequent deploys

From your laptop:

```bash
git push prod master    # post-receive hook checks out + runs deploy.sh
```

Manual deploy on the server (e.g. retry after a failed build, or to refresh systemd/nginx):

```bash
cd /var/www/prism-v2
sudo ./scripts/deploy.sh              # build + restart + smoke check
sudo ./scripts/deploy.sh --install    # also refresh systemd units / nginx site
```

`deploy.sh` is idempotent: ensures ownership, builds the backend venv and `npm ci && npm run build` as `www-data` (with `HOME` + `NPM_CONFIG_CACHE` set), restarts both services, and curls `/health` and `/` before exiting. It does NOT touch git — the post-receive hook owns checkout.

### Ops

```bash
sudo systemctl status prismv2-backend.service prismv2-frontend.service --no-pager
sudo journalctl -u prismv2-backend.service -f
sudo journalctl -u prismv2-frontend.service -f

curl -i http://127.0.0.1:8001/health
curl -i https://prism.tylerhoang.xyz/api/search?q=AAPL
```

## Verification

```bash
pytest                                    # backend tests (pytest.ini sets pythonpath)
cd frontend && npm run lint && npm run build
```

## Production Rules

- Do not hardcode a localhost API base in frontend browser code. `frontend/lib/api.ts` must default to empty so the browser hits same-origin `/api/*`.
- Never commit `.env`, `node_modules/`, `backend/.venv/`, or `backend/data/prism.db`.
- Watchlist is normalized to uppercase and capped at 10 symbols.