98 lines
4.3 KiB
Markdown
98 lines
4.3 KiB
Markdown
|
|
# Architecture — Social Proximity
|
||
|
|
|
||
|
|
## System Overview
|
||
|
|
|
||
|
|
Social Proximity er bygget som et minimal monorepo med tre separate komponenter:
|
||
|
|
|
||
|
|
```
|
||
|
|
┌────────────────────────────────────────────────────────┐
|
||
|
|
│ Mobile App (Flutter) │
|
||
|
|
│ │
|
||
|
|
│ BLE Scanner ──► Match Screen ──► "Say hello" nudge │
|
||
|
|
└───────────────────────┬────────────────────────────────┘
|
||
|
|
│ HTTPS + WebSocket
|
||
|
|
▼
|
||
|
|
┌────────────────────────────────────────────────────────┐
|
||
|
|
│ Backend (FastAPI) │
|
||
|
|
│ │
|
||
|
|
│ Match Engine ──► Session Store (Redis) ──► DB (PG) │
|
||
|
|
└───────────┬────────────────────────────────────────────┘
|
||
|
|
│ Internal API
|
||
|
|
▼
|
||
|
|
┌────────────────────────────────────────────────────────┐
|
||
|
|
│ Admin Dashboard (Next.js) │
|
||
|
|
│ │
|
||
|
|
│ Anonymised stats — pings, sessions, interest counts │
|
||
|
|
└────────────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
## Components
|
||
|
|
|
||
|
|
### Mobile App
|
||
|
|
- **Technology:** Flutter (Dart) — cross-platform Android + iOS
|
||
|
|
- **BLE library:** `flutter_blue_plus`
|
||
|
|
- **Responsibility:** BLE scanning and advertisement, interest profile, consent toggle, nudge display
|
||
|
|
- **Does NOT:** store data persistently, track location, communicate with other users directly
|
||
|
|
|
||
|
|
### Backend
|
||
|
|
- **Technology:** FastAPI (Python) + WebSockets
|
||
|
|
- **Session store:** Redis — ephemeral sessions, no long-term identity storage
|
||
|
|
- **Database:** PostgreSQL — anonymised aggregate stats only
|
||
|
|
- **Responsibility:** receive BLE advertisement tokens, match users, push nudge via WebSocket, aggregate stats
|
||
|
|
|
||
|
|
### Admin Dashboard
|
||
|
|
- **Technology:** Next.js (TypeScript)
|
||
|
|
- **Responsibility:** display anonymised platform stats — no PII, no user-level data
|
||
|
|
- **Access:** Internal only (no public-facing auth in MVP)
|
||
|
|
|
||
|
|
## Data Flow
|
||
|
|
|
||
|
|
### Happy path — two users nudged to talk
|
||
|
|
|
||
|
|
```
|
||
|
|
User A opens app, selects interests, enables "open to talk"
|
||
|
|
└─► App generates ephemeral BLE token
|
||
|
|
└─► App advertises token via BLE
|
||
|
|
└─► App registers token + interests with backend (POST /session)
|
||
|
|
|
||
|
|
User B nearby detects User A's BLE token
|
||
|
|
└─► App sends token to backend (POST /match)
|
||
|
|
└─► Backend checks: does B's interest list overlap with A's?
|
||
|
|
└─► If overlap + both consent: backend sends WebSocket nudge to both
|
||
|
|
"Someone nearby also works with DevOps and enjoys hardstyle"
|
||
|
|
└─► Users see nudge, put phones down, say hello
|
||
|
|
└─► Session expires automatically (Redis TTL: 2 hours)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Privacy properties
|
||
|
|
- BLE tokens are ephemeral — regenerated every session
|
||
|
|
- Backend never stores exact location or movement history
|
||
|
|
- Interest matching happens server-side — apps never see each other's raw profile
|
||
|
|
- After session expiry, all session data is deleted from Redis
|
||
|
|
|
||
|
|
## Infrastructure
|
||
|
|
|
||
|
|
| Environment | Platform |
|
||
|
|
|---|---|
|
||
|
|
| Local dev | Docker Compose (backend + PostgreSQL + Redis) |
|
||
|
|
| Production | Azure Container Apps or Kubernetes (i80.dk) |
|
||
|
|
|
||
|
|
## Repository Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
SigHej/
|
||
|
|
├── README.md
|
||
|
|
├── Docs/ ← Technical documentation (this folder)
|
||
|
|
├── backend/ ← FastAPI application
|
||
|
|
├── app/ ← Flutter mobile app
|
||
|
|
├── admin/ ← Next.js admin dashboard
|
||
|
|
└── docker-compose.yml
|
||
|
|
```
|
||
|
|
|
||
|
|
See individual docs for each component:
|
||
|
|
- [BACKEND.md](./BACKEND.md)
|
||
|
|
- [APP.md](./APP.md)
|
||
|
|
- [ADMIN.md](./ADMIN.md)
|
||
|
|
- [TESTING.md](./TESTING.md)
|
||
|
|
- [DECISIONS.md](./DECISIONS.md)
|