Blueforce-Linux/web
emirhanmamak-blueforce b6a14ca8e0 refine(operators): wave code back in the fleet row; drop a dead helper
The fleet row named the wave by its short name only ("PILOT-2") while the
detail view and the filter carry the full label ("P2 — PILOT-2"). An operator
matching a row against a work order speaks in codes, so the row carries the
full label now -- still named once, which the row test asserts.

Also takes the console agent's leftover: a dead state_style helper removed and
the unit convention on ALL_TOKENS documented (lengths are unitless; Tk padding
is already pixels, the parity test strips the CSS unit).
2026-09-16 22:47:12 +03:00
..
data feat(web): add the central fleet panel the operator asked for first 2026-09-16 22:39:50 +03:00
static feat(web): add the central fleet panel the operator asked for first 2026-09-16 22:39:50 +03:00
templates refine(operators): wave code back in the fleet row; drop a dead helper 2026-09-16 22:47:12 +03:00
__init__.py feat(web): add the central fleet panel the operator asked for first 2026-09-16 22:39:50 +03:00
fleet_data.py feat(web): add the central fleet panel the operator asked for first 2026-09-16 22:39:50 +03:00
panel.py feat(web): add the central fleet panel the operator asked for first 2026-09-16 22:39:50 +03:00
README.md feat(web): add the central fleet panel the operator asked for first 2026-09-16 22:39:50 +03:00
render.py feat(web): add the central fleet panel the operator asked for first 2026-09-16 22:39:50 +03:00
server.py feat(web): add the central fleet panel the operator asked for first 2026-09-16 22:39:50 +03:00

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

# 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)

{
  "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-<no> / bf-<no> (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; KAYITLIyı 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

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.