chore: complete v1.0 milestone

Archive v1.0 MVP: 7 phases, 13 plans, 27/27 requirements.

- Archive roadmap to milestones/v1.0-ROADMAP.md
- Archive requirements to milestones/v1.0-REQUIREMENTS.md
- Move milestone audit into milestones/
- Create MILESTONES.md with v1.0 entry
- Evolve PROJECT.md: move shipped requirements to Validated,
  update Context with stack/LOC, log Key Decisions with outcomes
- Collapse ROADMAP.md to one-line milestone summary
- Update STATE.md to shipped status
- Back-fill stale requirements-completed frontmatter on
  02-01, 04-01, 05-01, 06-01 SUMMARY.md files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 09:47:37 +02:00
co-authored by Claude Opus 4.6
parent 63c935e860
commit 67a1cd66ec
93 changed files with 717 additions and 357 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -13,6 +13,7 @@ import imptune.config as cfg
from imptune.db.models import Icon, Printer
from imptune.generators.intunewin_builder import build_intunewin
from imptune.generators.script_generator import render_detect, render_install, render_uninstall
from imptune.storage.driver_store import DriverStore
router = APIRouter(prefix="/printers")
@@ -43,8 +44,7 @@ def _get_printer_and_driver(printer_id: int):
def _get_driver_zip_path(driver) -> str:
"""Return the on-disk path of the driver ZIP file."""
return os.path.join(cfg.DRIVERS_DIR, f"{driver.sha256}.zip")
return str(DriverStore(cfg.DRIVERS_DIR).get_path(driver.sha256))
@router.get("/{printer_id}/packages/ninja")
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+10 -6
View File
@@ -1,5 +1,9 @@
"""Peewee ORM models — full schema for phases 1-5."""
from datetime import datetime
from datetime import UTC, datetime
def _utcnow():
return datetime.now(UTC).replace(tzinfo=None)
from peewee import (
BooleanField,
@@ -24,7 +28,7 @@ class Client(BaseModel):
"""Represents a deployment target (AD client / OU)."""
name = CharField(unique=True)
created_at = DateTimeField(default=datetime.utcnow)
created_at = DateTimeField(default=_utcnow)
class Meta:
table_name = "client"
@@ -36,7 +40,7 @@ class Driver(BaseModel):
sha256 = CharField(unique=True, index=True)
original_filename = CharField()
size_bytes = IntegerField()
uploaded_at = DateTimeField(default=datetime.utcnow)
uploaded_at = DateTimeField(default=_utcnow)
driver_desc = CharField(null=True)
inf_filename = CharField(null=True)
architecture = CharField(null=True)
@@ -58,8 +62,8 @@ class Printer(BaseModel):
color_mode = BooleanField(default=True)
paper_size = CharField(default="A4")
collate = BooleanField(default=True)
created_at = DateTimeField(default=datetime.utcnow)
updated_at = DateTimeField(default=datetime.utcnow)
created_at = DateTimeField(default=_utcnow)
updated_at = DateTimeField(default=_utcnow)
class Meta:
table_name = "printer"
@@ -72,7 +76,7 @@ class Icon(BaseModel):
sha256 = CharField()
original_filename = CharField()
size_bytes = IntegerField()
uploaded_at = DateTimeField(default=datetime.utcnow)
uploaded_at = DateTimeField(default=_utcnow)
class Meta:
table_name = "icon"
+5
View File
File diff suppressed because one or more lines are too long
+49 -6
View File
@@ -3,16 +3,42 @@
.layout {
display: flex;
min-height: 100vh;
align-items: stretch;
}
/* Sidebar */
.sidebar {
.layout > * {
box-sizing: border-box;
}
/* Sidebar — override Pico's default nav styling (which is horizontal flex) */
nav.sidebar {
width: 220px;
flex-shrink: 0;
min-width: 220px;
max-width: 220px;
flex: 0 0 220px;
border-right: 1px solid var(--pico-muted-border-color, #e0e0e0);
padding: 1rem 0;
margin: 0;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
overflow-x: hidden;
}
nav.sidebar ul,
nav.sidebar li {
margin: 0;
padding: 0;
list-style: none;
}
nav.sidebar ul {
display: block;
}
nav.sidebar li {
display: block;
}
.sidebar-brand {
@@ -22,22 +48,37 @@
margin-bottom: 0.5rem;
}
/* Override Pico's horizontal nav ul default */
.sidebar nav,
.sidebar-nav {
display: block;
}
.sidebar-nav {
list-style: none;
padding: 0;
margin: 0;
flex-direction: column;
}
.sidebar-nav li {
display: block;
margin: 0;
padding: 0;
width: 100%;
}
.sidebar-nav a {
display: block;
box-sizing: border-box;
width: 100%;
padding: 0.6rem 1rem;
margin: 0;
text-decoration: none;
color: inherit;
font-weight: 400;
line-height: 1.4;
border-left: 3px solid transparent;
}
.sidebar-nav a:hover {
@@ -47,13 +88,15 @@
.sidebar-nav a.active {
font-weight: 600;
background-color: var(--pico-primary-background, rgba(0,0,0,0.08));
border-left: 3px solid var(--pico-primary, #1a73e8);
border-left-color: var(--pico-primary, #1a73e8);
}
/* Main content */
.main-content {
flex: 1;
padding: 1.5rem;
flex: 1 1 auto;
min-width: 0;
padding: 1.5rem 2rem;
overflow-x: hidden;
overflow-y: auto;
}
+1
View File
File diff suppressed because one or more lines are too long
+4
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
+6 -11
View File
@@ -6,8 +6,9 @@ from pathlib import Path
class DriverStore:
"""SHA256 content-addressed file storage for printer driver packages.
Files are stored as DRIVERS_DIR/{sha256} so identical uploads are
deduplicated automatically.
Files are stored as DRIVERS_DIR/{sha256}.zip so identical uploads are
deduplicated automatically. The .zip suffix lets operators identify
stored driver packages by type when browsing the volume directly.
"""
def __init__(self, base_dir: str) -> None:
@@ -15,20 +16,14 @@ class DriverStore:
self._base.mkdir(parents=True, exist_ok=True)
def save(self, data: bytes) -> str:
"""Persist *data* and return its SHA256 hex digest.
If the file already exists the write is skipped (deduplication).
"""
digest = hashlib.sha256(data).hexdigest()
dest = self._base / digest
dest = self.get_path(digest)
if not dest.exists():
dest.write_bytes(data)
return digest
def get_path(self, sha256: str) -> Path:
"""Return the filesystem path for a given SHA256 digest."""
return self._base / sha256
return self._base / f"{sha256}.zip"
def exists(self, sha256: str) -> bool:
"""Return True if the file for *sha256* exists on disk."""
return (self._base / sha256).exists()
return self.get_path(sha256).exists()
+1 -1
View File
@@ -23,7 +23,7 @@
<li><a href="/packages" {% if request.url.path == "/packages" %}class="active"{% endif %}>Packages</a></li>
</ul>
</nav>
<main class="main-content container">
<main class="main-content">
{% block content %}{% endblock %}
</main>
</div>
+1 -1
View File
@@ -42,7 +42,7 @@
const text = document.getElementById('uninstall-cmd').innerText;
navigator.clipboard.writeText(text).then(() => { copiedUninstall = true; setTimeout(() => copiedUninstall = false, 2000) })
.catch(() => { /* fallback: text is visible for manual copy */ })
" x-text="copiedUninstall ? 'Copied!' : 'Uninstall copy'" class="secondary outline"></button>
" x-text="copiedUninstall ? 'Copied!' : 'Copy'" class="secondary outline"></button>
</div>
<h2>Export</h2>