131 lines
3.5 KiB
Markdown
131 lines
3.5 KiB
Markdown
# Order Persistence - Server-Side Storage
|
|
|
|
## Overview
|
|
|
|
Service order (drag-and-drop positioning) is now persisted server-side using SQLite database, with localStorage as a fallback/cache.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Browser (drag-drop) → localStorage (immediate)
|
|
→ /api/order (async save to server)
|
|
↓
|
|
order-service (Flask)
|
|
↓
|
|
SQLite Database
|
|
(/data/services-order.db)
|
|
```
|
|
|
|
## Components
|
|
|
|
### Backend Service: `order-service.py`
|
|
- **Port:** 8082 (internal)
|
|
- **Database:** SQLite at `/data/services-order.db`
|
|
- **Volume:** `order-data` (persistent across container restarts)
|
|
|
|
### API Endpoints
|
|
|
|
#### GET `/api/order?user_id=default`
|
|
- Retrieves saved service order for a user
|
|
- Returns: JSON object mapping group names to ordered service IDs
|
|
- Example response:
|
|
```json
|
|
{
|
|
"Management": ["portainer", "uptime-kuma", "scrutiny"],
|
|
"Media": ["jellyfin", "jellyseer", "transmission"]
|
|
}
|
|
```
|
|
|
|
#### POST `/api/order`
|
|
- Saves service order for a user
|
|
- Request body:
|
|
```json
|
|
{
|
|
"user_id": "default",
|
|
"order": {
|
|
"Management": ["portainer", "uptime-kuma"],
|
|
...
|
|
}
|
|
}
|
|
```
|
|
- Response: `{"success": true, "message": "Order saved"}`
|
|
|
|
#### DELETE `/api/order?user_id=default`
|
|
- Resets order to default (deletes saved order)
|
|
- Response: `{"success": true, "message": "Order deleted"}`
|
|
|
|
## Database Schema
|
|
|
|
```sql
|
|
CREATE TABLE service_order (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id TEXT NOT NULL DEFAULT 'default',
|
|
order_data TEXT NOT NULL,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
## Behavior
|
|
|
|
### On Page Load
|
|
1. JavaScript calls `/api/order?user_id=default`
|
|
2. If server has data, use it and cache in localStorage
|
|
3. If server returns empty, fall back to localStorage
|
|
4. If both empty, use default XML order
|
|
|
|
### On Drag-Drop
|
|
1. Save to localStorage immediately (instant response)
|
|
2. Async POST to `/api/order` in background
|
|
3. If server save fails, localStorage still works as fallback
|
|
|
|
### Benefits
|
|
- **Cross-device sync:** Order syncs across browsers/devices
|
|
- **Persistent:** Survives container restarts, browser clearing
|
|
- **Fallback:** localStorage works if server is down
|
|
- **Fast:** Instant UI update, async server save
|
|
|
|
## Data Location
|
|
|
|
- **Server:** Docker volume `services-homepage_order-data`
|
|
- Inspect: `docker volume inspect services-homepage_order-data`
|
|
- Location: `/var/lib/docker/volumes/services-homepage_order-data/_data/`
|
|
- **Client:** Browser localStorage key `services-order`
|
|
|
|
## Backup/Restore
|
|
|
|
### Backup Server Data
|
|
```bash
|
|
# Copy database from volume
|
|
docker run --rm -v services-homepage_order-data:/data -v $(pwd):/backup \
|
|
alpine cp /data/services-order.db /backup/order-backup.db
|
|
```
|
|
|
|
### Restore Server Data
|
|
```bash
|
|
# Copy database to volume
|
|
docker run --rm -v services-homepage_order-data:/data -v $(pwd):/backup \
|
|
alpine cp /backup/order-backup.db /data/services-order.db
|
|
|
|
# Restart service to load new data
|
|
docker restart services-homepage-order-service
|
|
```
|
|
|
|
### Reset All Orders
|
|
```bash
|
|
# Delete all saved orders
|
|
curl -X DELETE "http://192.168.2.180:8088/api/order?user_id=default"
|
|
|
|
# Or restart with fresh database
|
|
docker compose down
|
|
docker volume rm services-homepage_order-data
|
|
docker compose up -d
|
|
```
|
|
|
|
## Future Enhancements
|
|
|
|
- Multi-user support (authentication)
|
|
- Order history/versioning
|
|
- Export/import order via UI
|
|
- Sync status indicator in UI
|
|
- Conflict resolution for concurrent edits
|