| 05-package-export |
02 |
ui |
| fastapi |
| pillow |
| htmx |
| alpine.js |
| icon-upload |
| png-validation |
| printer-detail |
|
| phase |
provides |
| 05-01 |
NinjaRMM ZIP and .intunewin package export endpoints |
|
| phase |
provides |
| 04-02 |
script generation endpoints (install/uninstall/detect) |
|
| phase |
provides |
| 03-02 |
printer detail page foundation in pages.py |
|
|
| Icon upload endpoint with PNG format/dimension/size validation |
| SHA256-addressed icon storage under DATA_DIR/icons/ |
| Icon ORM record tracking (one per printer, replace-on-upload) |
| Printer detail page with Intune Commands section (install/uninstall command strings with copy buttons) |
| Printer detail page with Export section (NinjaRMM ZIP and .intunewin download links) |
| Printer detail page with Icon Upload form (HTMX submission) |
|
|
| added |
patterns |
| Pillow>=10.0 (PNG dimension/format validation) |
|
| Read cfg.DATA_DIR at call time (not import time) for monkeypatch compatibility |
| SHA256-addressed icon storage — dedup automatic, filename = sha256 hash |
| Icon replace pattern: delete existing record then create new (unique FK) |
| Alpine.js copy-to-clipboard with copied state and 2-second timeout |
| HTMX icon upload form with #icon-status swap target |
|
|
| created |
modified |
| imptune/api/icons.py |
| tests/test_icon_upload.py |
|
| imptune/config.py |
| imptune/main.py |
| imptune/api/pages.py |
| imptune/templates/printer_detail.html |
| tests/conftest.py |
| tests/test_packages.py |
| requirements.txt |
|
|
| Pillow used for PNG validation — provides format, dimension, and byte-read in one library |
| Icons stored SHA256-addressed (not by printer ID) — enables dedup if same PNG used for multiple printers |
| Read cfg.DATA_DIR dynamically in icons.py endpoint, not at import time — consistent with monkeypatch pattern established in Phase 02 |
| Icon replace via delete-then-create rather than get_or_create — unique FK makes upsert awkward, simpler to delete first |
| Export and command sections conditionally shown only when has_driver is true — avoids confusing 422 before driver is assigned |
|
| Icon upload: read MAX+1 bytes, check len > MAX for oversized detection |
| Printer detail page sections gated on has_driver boolean from view context |
|
|
15min |
2026-04-10 |