feat(api): complete phase 9 mobile contracts
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
---
|
||||
title: Faz 9 — Mobil API ve Instance Discovery Sözleşmesi
|
||||
description: React Native istemcileri için discovery, API v1, metadata, capability, sürümleme ve güvenlik sınırı.
|
||||
status: completed
|
||||
last_updated: 2026-07-17
|
||||
---
|
||||
|
||||
# Faz 9 — Mobil API ve instance discovery
|
||||
|
||||
Bu faz mobil uygulamanın Neta backend iç yapısını bilmeden bir self-hosted instance'ı tanımasını sağlar. Mobil uygulamanın kendisi ve device pairing implementasyonu kapsam dışıdır.
|
||||
|
||||
## 1. Bağlantı akışı
|
||||
|
||||
Kullanıcı mobil uygulamaya instance URL'sini girer:
|
||||
|
||||
```text
|
||||
https://neta.example.com
|
||||
```
|
||||
|
||||
İstemci:
|
||||
|
||||
1. URL'yi yalnızca origin olacak şekilde normalize eder; credential, query ve fragment kabul etmez.
|
||||
2. Production'da HTTPS olmayan remote origin'i reddeder. HTTP yalnızca `localhost`, `127.0.0.1` ve emulator geliştirme adresleri için açık kullanıcı onayıyla kullanılabilir.
|
||||
3. `GET /.well-known/neta` çağrısını yapar.
|
||||
4. `protocol=neta` ve desteklenen `discoveryVersion` değerini doğrular.
|
||||
5. Discovery belgesindeki API URL'lerinin beklenen origin'den çıkmadığını doğrular.
|
||||
6. `GET /api/v1/meta` çağrısını yapar ve `instance.id` değerini discovery `instanceId` değeriyle karşılaştırır.
|
||||
7. Daha önce kaydedilmiş aynı origin farklı bir `instanceId` döndürürse bunu yeni/restore edilmiş instance olarak kullanıcıya açıkça gösterir; mevcut credential'ı sessizce yeniden kullanmaz.
|
||||
8. Minimum client sürümünü ve capability listesini değerlendirir.
|
||||
9. Instance kaydını `origin + instanceId + applicationName + iconUrl` ile yerel güvenli metadata alanına kaydeder.
|
||||
|
||||
Redirect takibi en fazla üç hop olmalı ve HTTPS'ten HTTP'ye downgrade edilmemelidir. Discovery veya meta içindeki URL kullanıcı girdisinden bağımsız güven kaynağı sayılmaz.
|
||||
|
||||
## 2. Endpoint özeti
|
||||
|
||||
| Endpoint | Auth | Cache | Amaç |
|
||||
| --- | --- | --- | --- |
|
||||
| `GET /.well-known/neta` | Public | 60 saniye public | Instance ve API keşfi |
|
||||
| `GET /api/v1/meta` | Public | 60 saniye public | Marka, sürüm ve capability bilgisi |
|
||||
| `GET /api/v1/health` | Public | No-store | DB/migration readiness |
|
||||
| `GET /api/v1/me` | Better Auth session | Private/no-store | Güvenli kullanıcı/session özeti |
|
||||
|
||||
Public endpoint'ler session oluşturmaz ve secret dönmez. `/me` geçersiz, süresi dolmuş veya disabled hesaba ait session için `401 UNAUTHENTICATED` döndürür.
|
||||
|
||||
## 3. Discovery v1
|
||||
|
||||
Örnek:
|
||||
|
||||
```json
|
||||
{
|
||||
"protocol": "neta",
|
||||
"discoveryVersion": 1,
|
||||
"instanceId": "6d26e558-9c93-4cd8-93a1-5c8d5166045a",
|
||||
"applicationName": "Poyraz Studio",
|
||||
"api": {
|
||||
"version": "1",
|
||||
"baseUrl": "https://neta.example.com/api/v1",
|
||||
"metaUrl": "https://neta.example.com/api/v1/meta",
|
||||
"healthUrl": "https://neta.example.com/api/v1/health"
|
||||
},
|
||||
"security": {
|
||||
"httpsRequired": true,
|
||||
"insecureLoopbackAllowed": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`instanceId` ilk discovery isteğinde kriptografik UUID olarak oluşturulur, `instance_settings` tablosunda saklanır ve backup/restore ile korunur. Domain veya marka adı instance kimliği değildir.
|
||||
|
||||
## 4. API v1 envelope
|
||||
|
||||
Başarılı yanıt:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
Hata:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"error": {
|
||||
"code": "UNAUTHENTICATED",
|
||||
"message": "Geçerli bir oturum gerekli.",
|
||||
"details": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`details` opsiyoneldir. `/api/v1` yanıtları `X-Neta-API-Version: 1` header'ı taşır. Mobil istemci kullanıcıya göstereceği metni `message` alanından alabilir; program akışını yalnızca stabil `code` üzerinden kurmalıdır.
|
||||
|
||||
Mevcut hata kodları:
|
||||
|
||||
- `VALIDATION_ERROR`
|
||||
- `UNAUTHENTICATED`
|
||||
- `FORBIDDEN`
|
||||
- `NOT_FOUND`
|
||||
- `CONFLICT`
|
||||
- `INVARIANT_VIOLATION`
|
||||
- `UPSTREAM_ERROR`
|
||||
- `UPSTREAM_TIMEOUT`
|
||||
- `SERVICE_UNAVAILABLE`
|
||||
- `INTERNAL_ERROR`
|
||||
|
||||
## 5. Metadata ve capability
|
||||
|
||||
`/api/v1/meta` şu grupları döndürür:
|
||||
|
||||
- Protokol/discovery/API major sürümü
|
||||
- Neta server package sürümü
|
||||
- Kalıcı instance kimliği ve oluşturulma zamanı
|
||||
- Application/organization adı
|
||||
- Mobil için absolute logo/icon URL'leri ve semantic marka bilgisi
|
||||
- Minimum desteklenen mobil client sürümü
|
||||
- `ios` ve `android` platform listesi
|
||||
- Authentication durumu
|
||||
- Capability listesi
|
||||
- Discovery, API, health ve me linkleri
|
||||
|
||||
Capability kaydı:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "portal.client",
|
||||
"version": 1,
|
||||
"status": "available",
|
||||
"access": "client"
|
||||
}
|
||||
```
|
||||
|
||||
İstemci bilinmeyen capability ID ve alanlarını yok saymalıdır. `status=planned`, endpoint'in kullanılabilir olduğu anlamına gelmez.
|
||||
|
||||
İlk capability seti:
|
||||
|
||||
- `instance.discovery`
|
||||
- `instance.branding`
|
||||
- `auth.better-auth-cookie`
|
||||
- `files.local`
|
||||
- `freelancer.core`
|
||||
- `portal.client`
|
||||
- `ai.assistant`
|
||||
- `auth.device-pairing` — `planned`
|
||||
|
||||
## 6. Minimum mobil sürüm
|
||||
|
||||
Instance sahibi opsiyonel SemVer tabanını environment ile ilan edebilir:
|
||||
|
||||
```env
|
||||
NETA_MINIMUM_MOBILE_VERSION=1.2.0
|
||||
```
|
||||
|
||||
Boşsa metadata `minimumSupportedVersion: null` döndürür ve server sürüm zorlaması yapmaz. İlk mobil release yayınlandığında istemci kendi sürümünü SemVer olarak karşılaştırır:
|
||||
|
||||
- Client sürümü minimumdan düşükse authenticated mutation başlatmaz.
|
||||
- Discovery/meta/health erişimini korur.
|
||||
- Kullanıcıya upgrade gereksinimini gösterir.
|
||||
- Pre-release SemVer yalnızca test kanallarında kullanılmalıdır.
|
||||
|
||||
## 7. API sürümleme politikası
|
||||
|
||||
- Major sürüm URL'dedir: `/api/v1`.
|
||||
- Yeni opsiyonel alan, yeni endpoint ve yeni capability additive değişikliktir; v1 içinde yapılabilir.
|
||||
- Alan silme, alan tipini değiştirme, mevcut enum anlamını değiştirme veya auth modelini kırma yeni major `/api/v2` gerektirir.
|
||||
- ID'ler opaque string kabul edilir; UUID formatına client iş mantığı bağlanmaz.
|
||||
- Timestamp'ler UTC ISO-8601 string'dir.
|
||||
- Para değerleri ilgili resource API'leri yayınlandığında integer minor unit olacaktır.
|
||||
- Liste endpoint'leri yayınlandığında cursor pagination kullanacaktır; offset sözleşmesi varsayılmaz.
|
||||
- İstemci bilinmeyen JSON alanlarını ve enum/capability değerlerini forward-compatible biçimde yok sayar.
|
||||
- Eski major kaldırılmadan önce metadata capability ve release notlarıyla deprecation duyurulur.
|
||||
|
||||
## 8. Authentication sınırı
|
||||
|
||||
Bugünkü `/me`, Better Auth cookie session'ını doğrular; web ve entegrasyon smoke testleri aynı güvenli session adapter'ını kullanır. Session token, password hash veya AI secret yanıt içine girmez.
|
||||
|
||||
React Native için kalıcı bearer/device session üretimi bu fazda uygulanmadı. Mobil istemci `auth.device-pairing.status=planned` gördüğünde pairing UI'ını etkinleştirmemelidir. Gelecek güvenlik ve lifecycle kararı [ADR-0018](adr-0018-device-pairing.md) belgesindedir.
|
||||
|
||||
## 9. Test ve kalite kapısı
|
||||
|
||||
`pnpm phase9:smoke` şunları gerçek Next.js ve Better Auth akışında doğrular:
|
||||
|
||||
- Eşzamanlı discovery isteklerinde tek ve kalıcı instance ID
|
||||
- Discovery/meta ID ve absolute URL tutarlılığı
|
||||
- Public cache ve session oluşturmama davranışı
|
||||
- API version header ve envelope
|
||||
- Readiness health
|
||||
- Minimum client sürümü ve capability modeli
|
||||
- Anonymous `/me` negatif testi
|
||||
- Freelancer `/me`
|
||||
- Client `/me` ve client kimlik bağı
|
||||
- Disabled client session reddi
|
||||
- Branding değişikliğinin absolute mobile metadata'ya yansıması
|
||||
- Pairing route'larının implementasyon tamamlanmadan yayınlanmaması
|
||||
Reference in New Issue
Block a user