Phase 1: scaffold local backend — models, schemas, routers, printer service, Docker
This commit is contained in:
113
PLANS AND STRATEGIES/04_CLOUD_BACKEND.md
Normal file
113
PLANS AND STRATEGIES/04_CLOUD_BACKEND.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Guide 04 — Cloud Backend (FastAPI on VPS)
|
||||
|
||||
## Overview
|
||||
A lightweight FastAPI backend hosted on your VPS. It does NOT handle restaurant operations — that's the local backend's job. Its sole responsibilities are: site registration, license management, remote lock/unlock, and receiving periodic heartbeats from local backends.
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
cloud_backend/
|
||||
├── main.py
|
||||
├── config.py # VPS settings, master secret key
|
||||
├── database.py # PostgreSQL (or SQLite for simplicity)
|
||||
├── models/
|
||||
│ ├── site.py
|
||||
│ └── admin.py
|
||||
├── schemas/
|
||||
├── routers/
|
||||
│ ├── auth.py # Sysadmin login
|
||||
│ ├── sites.py # Site management
|
||||
│ └── heartbeat.py # Receives check-ins from local backends
|
||||
└── requirements.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Schema
|
||||
|
||||
### `admins` table
|
||||
```
|
||||
id INTEGER PK
|
||||
username TEXT UNIQUE
|
||||
password_hash TEXT
|
||||
role TEXT # 'sysadmin' (you, always)
|
||||
```
|
||||
|
||||
### `sites` table
|
||||
```
|
||||
id INTEGER PK
|
||||
site_id TEXT UNIQUE # UUID, generated on registration
|
||||
name TEXT # Restaurant name
|
||||
owner_name TEXT
|
||||
contact_email TEXT
|
||||
secret_key TEXT # Shared secret for heartbeat auth (hashed)
|
||||
is_active BOOLEAN DEFAULT TRUE
|
||||
is_locked BOOLEAN DEFAULT FALSE
|
||||
lock_reason TEXT NULL
|
||||
license_expires_at DATETIME
|
||||
created_at DATETIME
|
||||
last_seen_at DATETIME NULL # Updated on each heartbeat
|
||||
last_seen_ip TEXT NULL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Auth — `/api/auth`
|
||||
```
|
||||
POST /login
|
||||
Body: { username, password }
|
||||
Returns: JWT token (for sysadmin panel use)
|
||||
```
|
||||
|
||||
### Sites — `/api/sites`
|
||||
All routes require sysadmin JWT.
|
||||
```
|
||||
GET / # List all sites with status
|
||||
POST / # Register new site → returns site_id + secret_key
|
||||
GET /:site_id # Site detail + last heartbeat info
|
||||
PUT /:site_id # Update site info / extend license
|
||||
POST /:site_id/lock # Remotely lock a site
|
||||
Body: { reason: "..." }
|
||||
POST /:site_id/unlock # Remotely unlock a site
|
||||
DELETE /:site_id # Deregister site
|
||||
```
|
||||
|
||||
### Heartbeat — `/api/heartbeat`
|
||||
Called by local backends every 6 hours. No sysadmin auth — uses site's own secret key.
|
||||
```
|
||||
POST /
|
||||
Header: X-Site-ID: <site_id>
|
||||
Header: X-Site-Key: <secret_key>
|
||||
Body: { version: "1.0.0", uptime_seconds: 12345 }
|
||||
Returns: {
|
||||
licensed: true,
|
||||
locked: false,
|
||||
lock_reason: null,
|
||||
expires_at: "2026-12-31T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Health — `/health`
|
||||
```
|
||||
GET /health # Public, no auth. Cloud liveness check.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment Notes
|
||||
- Host on your VPS with nginx reverse proxy + SSL (Let's Encrypt)
|
||||
- Use PostgreSQL for the cloud DB (more robust than SQLite for a server)
|
||||
- Run with: `uvicorn main:app --host 0.0.0.0 --port 8001`
|
||||
- Set up systemd service for auto-restart
|
||||
|
||||
---
|
||||
|
||||
## Security Notes
|
||||
- The `secret_key` per site is generated once at registration and never shown again (like an API key)
|
||||
- Local backends store it in their `config.py` / environment variable
|
||||
- Heartbeat endpoint rate-limited to prevent abuse
|
||||
- All sysadmin routes require JWT with short expiry
|
||||
Reference in New Issue
Block a user