# Blueforce Field OS — Filo Paneli (merkez web paneli) Operatörün ofis tarayıcısından filoyu gördüğü yüzey: **hangi cihaz kuruldu, hangi yayılım dalgasında ve sıradaki tek iş ne**. Cihaz başına operatör arayüzü `sudo bf` (tkinter konsolu, `bfos/`) ile aynı filoyu iki uçtan anlatır; iki yüzey tek durum sözlüğünü paylaşır (`docs/DESIGN.md`). ## Neden yalnız Python standart kütüphanesi Filo çevrimdışı kurulur ve paketler commit'lenmiş havuzdan gelir; `fastapi`, `uvicorn`, `flask`, `jinja2` bu havuzda **yok**. Panel bunları import etse yönettiği cihazlara kurulamaz (bkz. `PRODUCT.md`, "Stack"; karar kullanıcıyla birlikte alındı). Bu yüzden: `http.server` + `string.Template`, harici CDN veya webfont yok, tüm varlıklar yerel. ## Çalıştırma ```bash # 1) Hiçbir şey vermeden: 127.0.0.1:8787, etiketli DEMO VERİ, salt-okunur python3 -m web.server # (python3 web/server.py de aynı şekilde çalışır) # 2) Gerçek envanter dosyasıyla python3 -m web.server --data /var/lib/blueforce/inventory/fleet.json # 3) Sunucuyu başlatmadan veri kaynağını denetle (CI/operatör kontrolü) python3 -m web.server --check --data ./web/data/fleet.json # 4) Tam yetki (bu sürümde yazma uç noktası yoktur; yalnız arayüz farkı) python3 -m web.server --privilege full ``` Seçenekler: `--host` (varsayılan `127.0.0.1`), `--port` (varsayılan `8787`), `--data`, `--privilege {read-only,full}`, `--check`, `--version`. Panel varsayılan olarak **yalnız yerel arayüze** bağlanır. ## Uç noktalar (hepsi okuma) | Yol | Ne döner | |---|---| | `/` (veya `/filo`) | İlk ekran: başlık bandı, durum özeti, cihaz listesi, seçili cihaz ayrıntısı | | `/?q=&durum=&dalga=&hazir_degil=1&cihaz=` | Aynı ekran, sunucu tarafında filtrelenmiş (JavaScript yok) | | `/hakkinda` | Panelin sürümü, veri kaynağı, durum sözlüğü, dalgalar, kanallar ve panel kuralları | | `/static/tokens.css`, `/static/app.css` | Yerel stil dosyaları (allow-list; listeye eklenmeyen dosya 404) | `GET` ve `HEAD` dışındaki yöntemler **405** ile ve Türkçe gerekçeyle reddedilir. Hiçbir uç nokta komut çalıştırmaz, dosya yazmaz veya cihazda değişiklik yapmaz. ## Veri sözleşmesi (`schema_version: 1`) ```json { "schema_version": 1, "generated_at": "2026-09-16T09:30:00+03:00", "devices": [ { "dealer_id": "12010193", "wave": "P1", "state": "READY", "last_seen": "2026-09-16T09:05:00+03:00", "channels": { "ssh_over_wg": {"status": "verified", "checked_at": "2026-09-14T10:00:00+03:00"}, "xrdp": "pending", "rustdesk": "verified", "meshcentral": "verified" } } ] } ``` - `dealer_id`: 8 hane (`^[0-9]{8}$`). Cihaz kimliği ve hostname **türetilir**: `BF-` / `bf-` (`docs/02`). Dosyada `device_id`/`hostname` de varsa yalnız kurala uyduğunda kabul edilir. - `wave`: `LAB`, `P1`, `P2`, `W1`, `W2`, `PROD` (Ansible grup adları da kabul: `lab`, `pilot_1`, `wave_1`, `production`). - `state`: `PROVISIONED_OFFLINE`, `ENROLLED`, `READY`, `FAILED`, `ABSENT` (Türkçe kelimeler de kabul: `KURULDU`, `KAYITLI`, `HAZIR`, `HATA`, `YOK`). - `channels`: dört kanal (`ssh_over_wg`, `xrdp`, `rustdesk`, `meshcentral`), durum `verified` / `pending` / `failed` / `unknown` / `missing`. - Örnek dosya: `web/data/fleet.example.json`. ## Panelin uyguladığı kurallar (fail-closed) 1. **HAZIR kapısı:** `READY` iddiası dört kanal da `verified` olmadan reddedilir; cihaz `HATA` olarak ve gerekçesiyle gösterilir (`docs/27`). Panel bir iddiayı uyarıya yumuşatmaz. 2. **Başarısız kanal:** denetimi başarısız (`failed`) bir kanal varsa cihaz `HATA` sayılır — cihaz bir insan bekliyor demektir. 3. **Yükseltme yok:** panel hiçbir cihazın durumunu ilerletmez; `KAYITLI`yı kendiliğinden `HAZIR` yapmaz. 4. **Geçersiz kayıt görünür:** kimliği kurulamayan kayıt (hatalı bayi no, yinelenen bayi no, bilinmeyen dalga) sessizce düşürülmez; sayfada "Geçersiz kayıtlar" altında nedeniyle listelenir. 5. **Bozuk veri demo veriye dönüşmez:** `--data`/`BF_FLEET_DATA` ile verilen dosya yoksa, JSON değilse veya şema sürümü desteklenmiyorsa panel `VERİ YOK` der; uydurmaz. ## Test ```bash python3 tests/test_web_static.py -v # statik + sözleşme testleri (port açmaz) bash tests/run-all.sh # depo genelindeki tüm testler ``` Testler HTTP yüzeyini `web/panel.py:route()` (saf fonksiyon) üzerinden kapsar; bu yüzden hiçbir test gerçek port bağlamaz, tarayıcı açmaz.