rebreak-monorepo/backend/MAGIC_API.md
chahinebrini 77edd67cbe fix(magic): explicit imports + staging defaults + sheet height
- backend/api/magic/register: explicit import of MAGIC_DEVICE_LIMIT
  and createAdGuardClient (Nitro auto-import was missing them
  → ReferenceError → HTTP 500 on /api/magic/register)
- mac-app: default backendBaseUrl falls back to staging.rebreak.org
  (app.rebreak.org serves wrong TLS cert)
- native MagicSheet: fallback download/dmg URLs point to staging
- native settings: Magic sheet capped at detents=[0.85] so AppHeader
  stays visible
- bundles all in-flight Magic feature work (pair create/redeem,
  device endpoints, schema, adguard utils, mac-app, locales)
2026-06-03 08:25:02 +02:00

289 lines
6.2 KiB
Markdown

# RebreakMagic Device-Binding — API Documentation
Backend-Implementation für DNS-basiertes Device-Binding via AdGuard Home DoH.
## Architektur-Überblick
```
RebreakMagic.app (Swift/macOS)
↓ POST /api/magic/register (JWT Auth)
Backend (Nitro)
├─ DB: UserDevice (magicDnsToken, magicEnrolledAt, ...)
├─ AdGuard REST API: Create Persistent Client
└─ Response: { dnsToken, profileUrl }
RebreakMagic.app → GET /api/magic/profile.mobileconfig?token=<dnsToken>
macOS Configuration Profile (.mobileconfig)
↓ DNS-over-HTTPS: https://dns.rebreak.org/dns-query/{dnsToken}
AdGuard Home (Hetzner) — Filtering + Logging per Client-ID
```
## Endpoints
### 1. `POST /api/magic/register`
Registriert Mac als Magic-Client, generiert DNS-Token, provisioniert AdGuard.
**Auth:** `Authorization: Bearer <jwt>`
**Body:**
```json
{
"deviceId": "550e8400-e29b-41d4-a716-446655440000",
"hostname": "Chahines MacBook Pro",
"model": "MacBookPro18,3",
"osVersion": "14.5"
}
```
**Response (Success):**
```json
{
"success": true,
"data": {
"deviceId": "550e8400-e29b-41d4-a716-446655440000",
"dnsToken": "QX7g9kL2mN4pR6tV8wY0zB3cD5fG7hJ9kM2nP4qS6uW8xZ0",
"profileUrl": "/api/magic/profile.mobileconfig?token=QX7g9kL2mN4pR6tV8wY0zB3cD5fG7hJ9kM2nP4qS6uW8xZ0",
"existing": false
}
}
```
**Response (Limit erreicht):**
```json
{
"statusCode": 409,
"message": "Magic-Device-Limit erreicht (max 3)",
"data": {
"code": "limit_reached",
"activeBindings": [
{
"deviceId": "...",
"hostname": "Mac #1",
"model": "MacBookPro18,3",
"osVersion": "14.5",
"magicEnrolledAt": "2026-06-01T10:00:00.000Z",
"releaseRequestedAt": null
}
]
}
}
```
**cURL:**
```bash
curl -X POST https://staging.rebreak.org/api/magic/register \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"deviceId": "550e8400-e29b-41d4-a716-446655440000",
"hostname": "Chahines MacBook Pro",
"model": "MacBookPro18,3",
"osVersion": "14.5"
}'
```
---
### 2. `GET /api/magic/devices`
Listet alle aktiven Magic-Bindings des Users.
**Auth:** `Authorization: Bearer <jwt>`
**Response:**
```json
{
"success": true,
"data": [
{
"deviceId": "550e8400-e29b-41d4-a716-446655440000",
"hostname": "Chahines MacBook Pro",
"model": "MacBookPro18,3",
"osVersion": "14.5",
"magicEnrolledAt": "2026-06-01T10:00:00.000Z",
"releaseRequestedAt": null,
"releaseAvailableAt": null
}
]
}
```
**cURL:**
```bash
curl https://staging.rebreak.org/api/magic/devices \
-H "Authorization: Bearer $JWT_TOKEN"
```
---
### 3. `POST /api/magic/devices/:deviceId/request-release`
Startet 24h Cooldown für Device-Freigabe.
**Auth:** `Authorization: Bearer <jwt>`
**Response:**
```json
{
"success": true,
"data": {
"releaseRequestedAt": "2026-06-01T10:00:00.000Z",
"releaseAvailableAt": "2026-06-02T10:00:00.000Z"
}
}
```
**cURL:**
```bash
curl -X POST https://staging.rebreak.org/api/magic/devices/550e8400-e29b-41d4-a716-446655440000/request-release \
-H "Authorization: Bearer $JWT_TOKEN"
```
---
### 4. `POST /api/magic/devices/:deviceId/cancel-release`
Zieht Release-Request zurück.
**Auth:** `Authorization: Bearer <jwt>`
**Response:**
```json
{
"success": true,
"data": { "ok": true }
}
```
**cURL:**
```bash
curl -X POST https://staging.rebreak.org/api/magic/devices/550e8400-e29b-41d4-a716-446655440000/cancel-release \
-H "Authorization: Bearer $JWT_TOKEN"
```
---
### 5. `GET /api/magic/profile.mobileconfig?token=<dnsToken>`
Generiert personalisiertes macOS Configuration Profile.
**Auth:** KEINE (Token in Query-Parameter)
**Response-Headers:**
- `Content-Type: application/x-apple-aspen-config`
- `Content-Disposition: attachment; filename="RebreakMagic-<deviceId>.mobileconfig"`
**Response-Body:** XML-Plist (mobileconfig)
**cURL:**
```bash
curl "https://staging.rebreak.org/api/magic/profile.mobileconfig?token=QX7g9kL2mN4pR6tV8wY0zB3cD5fG7hJ9kM2nP4qS6uW8xZ0" \
-o RebreakMagic.mobileconfig
```
---
## DB-Schema
**UserDevice Model (Prisma Schema):**
```prisma
model UserDevice {
// ... existing fields ...
// RebreakMagic DNS-Device-Binding
magicDnsToken String? @unique @map("magic_dns_token")
magicEnrolledAt DateTime? @map("magic_enrolled_at")
magicRevokedAt DateTime? @map("magic_revoked_at")
magicHostname String? @map("magic_hostname")
}
```
**Migration:**
```bash
# User führt aus (NICHT auto-deployen):
pnpm prisma migrate dev --name magic_binding_fields
```
---
## AdGuard-Integration
**API-Endpoint:** `https://dns.rebreak.org/control/clients/add`
**Auth:** Basic Auth (`ADGUARD_USER`, `ADGUARD_PASSWORD`)
**Payload:**
```json
{
"name": "magic_<deviceId>",
"ids": ["<dnsToken>"],
"use_global_settings": false,
"filtering_enabled": true,
"parental_enabled": false,
"safebrowsing_enabled": true,
"blocked_services": []
}
```
**DoH-URL-Format (embedded in mobileconfig):**
```
https://dns.rebreak.org/dns-query/<dnsToken>
```
---
## Cron-Worker
**Funktion:** `processMagicReleases()` in `server/utils/magicCron.ts`
**Logic:**
1. Findet alle UserDevice mit `releaseRequestedAt < NOW() - 24h` AND `magicRevokedAt IS NULL`
2. Für jedes Device:
- DELETE AdGuard Client (`/control/clients/delete`)
- Setze `magicRevokedAt = NOW()`
3. Return `{ processed, errors }`
**Deployment:** TODO — Nitro Scheduled Task oder externer Cron-Trigger
---
## ENV-Variablen
Siehe [ENV_VARS.md](../ENV_VARS.md#rebreakmagic-dns-over-https-neu-2026-06-01):
- `ADGUARD_BASE_URL` — Default: `https://dns.rebreak.org`
- `ADGUARD_USER` — Admin-User für AdGuard Home REST API
- `ADGUARD_PASSWORD` — Admin-Password
---
## TODOs (Phase 2)
- [ ] Profile-Signierung via Apple Developer Certificate (`/usr/bin/security cms -S`)
- [ ] Cron-Registration für `processMagicReleases()` (Nitro scheduled task oder externer Cron)
- [ ] Plan-basierte Limits (jetzt hardcoded `MAGIC_DEVICE_LIMIT = 3`)
- [ ] AdGuard Blocked-Services konfigurieren (Gambling-Filter via AdGuard-Blocklisten)
- [ ] Tests (Phase 2: `rebreak-tester`)
- [ ] Frontend-Integration (RN-UI + RebreakMagic.app)