added media fixes, search, push updates, pwa, metrics. preparing release 1.0
This commit is contained in:
@@ -1,84 +1,91 @@
|
||||
# Messenger
|
||||
|
||||
Self-hosted messenger for family and friends. Built from scratch: Go backend,
|
||||
React PWA frontend, PostgreSQL, WebRTC calls. Deployed on a home-lab mini PC.
|
||||
React PWA frontend, PostgreSQL. Deployed on a home-lab mini PC.
|
||||
|
||||
> **Status:** Phase 1 — auth (JWT + refresh in httpOnly cookies, argon2id),
|
||||
> contacts, direct-message chats, real-time delivery over WebSocket
|
||||
> (messages, typing, presence, read receipts). Backend is complete and tested;
|
||||
> the frontend still shows the Phase 0 status screen.
|
||||
> **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)
|
||||
internal/api/ HTTP handlers: auth, users, contacts, conversations, messages
|
||||
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, structured logging
|
||||
web/ React + TypeScript + Vite frontend (PWA-ready)
|
||||
deploy/ docker-compose and operational config
|
||||
docs/ architecture, ADR, OpenAPI
|
||||
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 (Phase 1)
|
||||
## 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}` — create/find DM |
|
||||
| GET | `/api/v1/conversations/{id}/messages` | History, keyset `?before=` |
|
||||
| POST | `/api/v1/conversations/{id}/messages` | `{body, temp_id}` — send |
|
||||
| GET | `/ws` | WebSocket (auth via cookie) |
|
||||
| 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`.
|
||||
WebSocket event types: `message.created`, `typing`, `presence`, `read`,
|
||||
`reaction.added/removed`, `member.added/removed`, `conversation.added/removed`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Go 1.27+
|
||||
- Node.js 22+
|
||||
- Go 1.27+, Node.js 22+, PostgreSQL
|
||||
- Docker + Docker Compose (for deployment)
|
||||
|
||||
## Local development
|
||||
|
||||
Requires a local PostgreSQL (e.g. `sudo -u postgres psql -c "CREATE ROLE messenger LOGIN PASSWORD 'messenger';"` and
|
||||
`sudo -u postgres psql -c "CREATE DATABASE messenger OWNER messenger;"`).
|
||||
|
||||
```bash
|
||||
# Backend (migrations run automatically on start)
|
||||
cd server
|
||||
go run ./cmd/api # listen on :8080
|
||||
# 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;"
|
||||
|
||||
# Frontend (separate terminal)
|
||||
# backend (migrations run automatically on start)
|
||||
cd server
|
||||
go run ./cmd/api # :8080
|
||||
|
||||
# frontend (separate terminal)
|
||||
cd web
|
||||
npm install
|
||||
npm run dev # Vite on :5173, proxies /api -> :8080
|
||||
npm run dev # :5173, proxies /api and /ws -> :8080
|
||||
```
|
||||
|
||||
The frontend calls `/api/v1/ping` on load and renders the server response.
|
||||
|
||||
## Useful endpoints
|
||||
|
||||
| Endpoint | Purpose |
|
||||
|--------------|----------------------------------|
|
||||
| `/healthz` | Liveness probe |
|
||||
| `/readyz` | Readiness probe |
|
||||
| `/api/v1/ping` | Round-trip check (JSON) |
|
||||
Optional: demo data → `cd server && go run ./cmd/seed`
|
||||
(users papa/mama/syn/doch, password `password123`, group «Семья»).
|
||||
|
||||
## Deployment (Docker Compose)
|
||||
|
||||
@@ -88,19 +95,35 @@ cd deploy
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
The API container is a single static binary that also serves the built
|
||||
frontend (`/app/web`). PostgreSQL runs alongside with a healthcheck.
|
||||
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** — skeleton, CI, Docker ✅
|
||||
- **Phase 1** — auth (JWT + refresh in httpOnly cookie, argon2id), contacts, DMs, WebSocket delivery, receipts, typing ✅ (backend)
|
||||
- Phase 1.5 — frontend: auth screens, chat UI, realtime client (PWA)
|
||||
- Phase 2 — groups, reactions, media upload + thumbnails (S3-like storage interface)
|
||||
- Phase 3 — search (pg_trgm), Web Push (VAPID), PWA offline
|
||||
- Phase 4 — P2P WebRTC calls via our WebSocket signalling + coturn TURN fallback
|
||||
- Phase 5 — metrics (Prometheus + VictoriaMetrics + Grafana), docs, hardening
|
||||
- **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 v1.0.
|
||||
Proprietary for now; open-sourcing at a future major version.
|
||||
Reference in New Issue
Block a user