129 lines
5.2 KiB
Markdown
129 lines
5.2 KiB
Markdown
# Messenger
|
|
|
|
Self-hosted messenger for family and friends. Built from scratch: Go backend,
|
|
React PWA frontend, PostgreSQL. Deployed on a home-lab mini PC.
|
|
|
|
> **Status: v1.0.0** — production-ready. Auth, direct chats and groups,
|
|
> real-time delivery, media, reactions, search, Web Push, PWA offline,
|
|
> metrics and hardening are all implemented and tested.
|
|
|
|
## Repository layout
|
|
|
|
```
|
|
server/ Go API
|
|
cmd/api/ entrypoint (wiring, migrations, graceful shutdown)
|
|
cmd/keygen/ generate VAPID keys for Web Push
|
|
cmd/seed/ create demo users and a family group
|
|
internal/api/ HTTP handlers: auth, users, contacts, conversations, messages, media, search, push
|
|
internal/auth/ argon2id hashing, JWT access tokens, refresh tokens
|
|
internal/realtime/ WebSocket hub: events, presence, typing, read receipts
|
|
internal/media/ S3-like storage + image thumbnails
|
|
internal/store/ PostgreSQL access (pgx), embedded migrations
|
|
internal/platform/ config, logging, metrics, rate limiting, version
|
|
web/ React + TypeScript + Vite PWA (offline, Web Push)
|
|
deploy/ docker-compose (api, postgres, victoriametrics, grafana)
|
|
docs/ architecture (mermaid), ADR
|
|
.github/ CI workflows
|
|
```
|
|
|
|
## API
|
|
|
|
All endpoints under `/api/v1`. Auth via httpOnly cookies (`messenger_access`
|
|
= JWT 15 min, `messenger_refresh` = opaque token 30 days, rotated on refresh).
|
|
|
|
| Method | Path | Description |
|
|
|--------|------|-------------|
|
|
| POST | `/api/v1/auth/register` | Create account (sets cookies) |
|
|
| POST | `/api/v1/auth/login` | Sign in (sets cookies) |
|
|
| POST | `/api/v1/auth/refresh` | Rotate refresh token |
|
|
| POST | `/api/v1/auth/logout` | Revoke session, clear cookies |
|
|
| GET | `/api/v1/users/me` | Current user |
|
|
| GET | `/api/v1/users?q=` | Search users by username/name |
|
|
| GET | `/api/v1/contacts` | Contact list |
|
|
| POST | `/api/v1/contacts` | `{user_id}` — add contact |
|
|
| DELETE | `/api/v1/contacts/{user_id}` | Remove contact |
|
|
| GET | `/api/v1/conversations` | List chats + unread counts |
|
|
| POST | `/api/v1/conversations` | `{user_id}` DM or `{type:"group", name, member_ids}` |
|
|
| GET | `/api/v1/conversations/{id}/members` | List participants + roles |
|
|
| POST | `/api/v1/conversations/{id}/members` | `{user_id}` — add to group |
|
|
| DELETE | `/api/v1/conversations/{id}/members/{user_id}` | Remove/leave (owner or self) |
|
|
| GET | `/api/v1/conversations/{id}/messages` | History, keyset `?before=` |
|
|
| POST | `/api/v1/conversations/{id}/messages` | `{body, temp_id, media_id?}` — send |
|
|
| PUT | `/api/v1/conversations/{id}/messages/{msg_id}/reactions` | `{emoji}` — react |
|
|
| DELETE | `/api/v1/conversations/{id}/messages/{msg_id}/reactions/{emoji}` | Remove reaction |
|
|
| POST | `/api/v1/media` | Upload file (multipart `file`) |
|
|
| GET | `/api/v1/media/{id}` | Download file |
|
|
| GET | `/api/v1/media/{id}/thumb` | Image thumbnail |
|
|
| GET | `/api/v1/search/messages?q=` | Search user's messages |
|
|
| POST | `/api/v1/push/subscribe` | Register Web Push subscription |
|
|
| DELETE | `/api/v1/push/subscribe` | Unregister Web Push subscription |
|
|
| GET | `/ws` | WebSocket (auth via cookie) |
|
|
|
|
WebSocket event types: `message.created`, `typing`, `presence`, `read`,
|
|
`reaction.added/removed`, `member.added/removed`, `conversation.added/removed`.
|
|
|
|
## Prerequisites
|
|
|
|
- Go 1.27+, Node.js 22+, PostgreSQL
|
|
- Docker + Docker Compose (for deployment)
|
|
|
|
## Local development
|
|
|
|
```bash
|
|
# create the role once (as postgres superuser)
|
|
sudo -u postgres psql -c "CREATE ROLE messenger LOGIN PASSWORD 'messenger';"
|
|
sudo -u postgres psql -c "CREATE DATABASE messenger OWNER messenger;"
|
|
|
|
# backend (migrations run automatically on start)
|
|
cd server
|
|
go run ./cmd/api # :8080
|
|
|
|
# frontend (separate terminal)
|
|
cd web
|
|
npm install
|
|
npm run dev # :5173, proxies /api and /ws -> :8080
|
|
```
|
|
|
|
Optional: demo data → `cd server && go run ./cmd/seed`
|
|
(users papa/mama/syn/doch, password `password123`, group «Семья»).
|
|
|
|
## Deployment (Docker Compose)
|
|
|
|
```bash
|
|
cp deploy/.env.example deploy/.env # adjust secrets
|
|
cd deploy
|
|
docker compose up -d --build
|
|
```
|
|
|
|
Services: `api` (single static binary serving API + frontend), `postgres`,
|
|
`victoriametrics` (metrics storage), `grafana` (dashboard «Messenger»
|
|
auto-provisioned at http://localhost:3000).
|
|
|
|
### Metrics
|
|
|
|
`GET /metrics` exposes Prometheus metrics: HTTP requests by status/path,
|
|
request latency, WebSocket connections, Go runtime. Scraped by VictoriaMetrics.
|
|
|
|
### Web Push (optional)
|
|
|
|
```bash
|
|
cd server && go run ./cmd/keygen # prints the three VAPID variables
|
|
```
|
|
Put them in `deploy/.env` (`MESSENGER_VAPID_*`) and in `web/.env`
|
|
(`VITE_VAPID_PUBLIC_KEY`). Without keys everything works except notifications.
|
|
|
|
## Architecture & decisions
|
|
|
|
See `docs/architecture.md` (component and sequence diagrams) and
|
|
`docs/adr/` for the rationale behind: monolith, in-memory WS hub, ILIKE
|
|
search, S3-like media storage, E2EE roadmap.
|
|
|
|
## Roadmap
|
|
|
|
- **Phase 0-3** — skeleton → auth/chats → frontend → groups/reactions/media → search/push/PWA ✅
|
|
- **Phase 4** — P2P WebRTC calls via our WebSocket signalling + coturn TURN fallback (planned, major feature)
|
|
- **Phase 5** — metrics, docs, hardening ✅
|
|
|
|
## License
|
|
|
|
Proprietary for now; open-sourcing at a future major version. |