zebra-day 0.0.32__py3-none-any.whl → 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zebra_day/__init__.py +35 -0
- zebra_day/bin/fetch_zebra_config.py +15 -0
- zebra_day/bin/generate_coord_grid_zpl.py +50 -0
- zebra_day/bin/print_zpl_from_file.py +21 -0
- zebra_day/bin/probe_new_label_dimensions.py +75 -0
- zebra_day/bin/scan_for_networed_zebra_printers_curl.sh +2 -1
- zebra_day/bin/zserve.py +701 -259
- zebra_day/cli/__init__.py +240 -0
- zebra_day/cli/cognito.py +121 -0
- zebra_day/cli/gui.py +255 -0
- zebra_day/cli/printer.py +168 -0
- zebra_day/cli/template.py +176 -0
- zebra_day/cmd_mgr.py +35 -0
- zebra_day/etc/label_styles/blank.zpl +0 -0
- zebra_day/etc/label_styles/cornersStripOf4Squares_1inX1in.zpl +55 -0
- zebra_day/etc/label_styles/corners_1inX2in.zpl +28 -0
- zebra_day/etc/label_styles/corners_20cmX30cm.zpl +6 -0
- zebra_day/etc/label_styles/corners_smallTube.zpl +7 -0
- zebra_day/etc/label_styles/corners_unspecifiedDimensions.zpl +15 -0
- zebra_day/etc/label_styles/plate_1inX0.25inHD.zpl +9 -0
- zebra_day/etc/label_styles/smallTubeWdotHD_prod.zpl +8 -0
- zebra_day/etc/label_styles/smallTubeWdot_corners.zpl +7 -0
- zebra_day/etc/label_styles/smallTubeWdot_prod.zpl +8 -0
- zebra_day/etc/label_styles/smallTubeWdot_prodAlt1.zpl +6 -0
- zebra_day/etc/label_styles/smallTubeWdot_prodAlt1b.zpl +3 -0
- zebra_day/etc/label_styles/smallTubeWdot_prodV2.zpl +8 -0
- zebra_day/etc/label_styles/smallTubeWdot_reagent.zpl +29 -0
- zebra_day/etc/label_styles/stripOf4Squares_1inX1in.zpl +32 -0
- zebra_day/etc/label_styles/test_800dX800dCoordinateArray.zpl +1 -0
- zebra_day/etc/label_styles/tmps/tmp_zpl_templates.here +0 -0
- zebra_day/etc/label_styles/tube_20mmX30mmA.zpl +7 -0
- zebra_day/etc/label_styles/tube_2inX0.5in.zpl +15 -0
- zebra_day/etc/label_styles/tube_2inX0.5inHD.zpl +15 -0
- zebra_day/etc/label_styles/tube_2inX1inHD.zpl +22 -0
- zebra_day/etc/label_styles/tube_2inX1inHDv3.zpl +21 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.022846_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.033657_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.039597_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.047295_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.055804_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:50:25.061337_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.073326_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.081950_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.088251_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.096501_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.104767_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.110364_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.118239_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.125950_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.349866_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.361085_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.558323_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:24.565756_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/{2023-10-25_02:19:04.139607_printer_config.json → 2026-02-01_01:51:29.739070_printer_config.json} +4 -3
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.753796_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.760201_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.768747_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.775312_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.782533_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.789287_printer_config.json +1 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.794230_printer_config.json +3 -0
- zebra_day/etc/old_printer_config/2026-02-01_01:51:29.800021_printer_config.json +5 -0
- zebra_day/etc/printer_config.json +3 -54
- zebra_day/etc/printer_config.template.json +3 -2
- zebra_day/etc/tmp_printers0.json +5 -0
- zebra_day/etc/tmp_printers374.json +5 -0
- zebra_day/etc/tmp_printers383.json +5 -0
- zebra_day/etc/tmp_printers450.json +5 -0
- zebra_day/etc/tmp_printers504.json +5 -0
- zebra_day/etc/tmp_printers608.json +5 -0
- zebra_day/etc/tmp_printers657.json +5 -0
- zebra_day/etc/tmp_printers838.json +5 -0
- zebra_day/etc/tmp_printers839.json +5 -0
- zebra_day/etc/tmp_printers933.json +5 -0
- zebra_day/etc/tmp_printers957.json +5 -0
- zebra_day/exceptions.py +88 -0
- zebra_day/files/hold +0 -0
- zebra_day/files/test_png_17696.png +0 -0
- zebra_day/files/test_png_23477.png +0 -0
- zebra_day/files/test_png_28157.png +0 -0
- zebra_day/files/test_png_35832.png +0 -0
- zebra_day/files/test_png_36400.png +0 -0
- zebra_day/files/test_png_40816.png +0 -0
- zebra_day/files/test_png_49564.png +0 -0
- zebra_day/files/test_png_53848.png +0 -0
- zebra_day/files/test_png_62542.png +0 -0
- zebra_day/files/test_png_91597.png +0 -0
- zebra_day/files/test_png_93633.png +0 -0
- zebra_day/files/tmpbjo3k7q1.png +0 -0
- zebra_day/files/tmpigtr4pwy.png +0 -0
- zebra_day/files/zpl_label_tube_2inX1in_2026-02-01_01:51:24.370964.png +0 -0
- zebra_day/logging_config.py +74 -0
- zebra_day/logs/.hold +0 -0
- zebra_day/logs/print_requests.log +2 -0
- zebra_day/paths.py +143 -0
- zebra_day/print_mgr.py +489 -103
- zebra_day/static/datschund.css +63 -43
- zebra_day/static/datschund.png +0 -0
- zebra_day/static/daylily.png +0 -0
- zebra_day/static/favicon.svg +20 -0
- zebra_day/static/general.css +99 -0
- zebra_day/static/js/zebra_modern.js +172 -0
- zebra_day/static/lsmc.css +354 -0
- zebra_day/static/moon.jpeg +0 -0
- zebra_day/static/oakland.css +0 -32
- zebra_day/static/popday_daylily.css +1 -1
- zebra_day/static/style.css +39 -0
- zebra_day/static/zebra_modern.css +771 -0
- zebra_day/templates/base.html +36 -0
- zebra_day/templates/bpr.html +72 -0
- zebra_day/templates/build_new_config.html +36 -0
- zebra_day/templates/build_print_request.html +32 -0
- zebra_day/templates/chg_ui_style.html +19 -0
- zebra_day/templates/edit_template.html +128 -0
- zebra_day/templates/edit_zpl.html +37 -0
- zebra_day/templates/index.html +82 -0
- zebra_day/templates/legacy/base.html +37 -0
- zebra_day/templates/legacy/bpr.html +72 -0
- zebra_day/templates/legacy/build_new_config.html +36 -0
- zebra_day/templates/legacy/build_print_request.html +32 -0
- zebra_day/templates/legacy/chg_ui_style.html +19 -0
- zebra_day/templates/legacy/edit_template.html +128 -0
- zebra_day/templates/legacy/edit_zpl.html +37 -0
- zebra_day/templates/legacy/index.html +82 -0
- zebra_day/templates/legacy/list_prior_configs.html +24 -0
- zebra_day/templates/legacy/print_result.html +30 -0
- zebra_day/templates/legacy/printer_details.html +25 -0
- zebra_day/templates/legacy/printer_status.html +70 -0
- zebra_day/templates/legacy/save_result.html +17 -0
- zebra_day/templates/legacy/send_print_request.html +34 -0
- zebra_day/templates/legacy/simple_print.html +94 -0
- zebra_day/templates/legacy/view_pstation_json.html +29 -0
- zebra_day/templates/list_prior_configs.html +24 -0
- zebra_day/templates/modern/base.html +98 -0
- zebra_day/templates/modern/config.html +141 -0
- zebra_day/templates/modern/dashboard.html +160 -0
- zebra_day/templates/modern/print_request.html +141 -0
- zebra_day/templates/modern/print_result.html +88 -0
- zebra_day/templates/modern/printer_detail.html +117 -0
- zebra_day/templates/modern/printers.html +133 -0
- zebra_day/templates/modern/save_result.html +46 -0
- zebra_day/templates/modern/template_editor.html +172 -0
- zebra_day/templates/modern/templates.html +122 -0
- zebra_day/templates/print_result.html +30 -0
- zebra_day/templates/printer_details.html +25 -0
- zebra_day/templates/printer_status.html +70 -0
- zebra_day/templates/save_result.html +17 -0
- zebra_day/templates/send_print_request.html +34 -0
- zebra_day/templates/simple_print.html +94 -0
- zebra_day/templates/view_pstation_json.html +29 -0
- zebra_day/web/__init__.py +9 -0
- zebra_day/web/app.py +171 -0
- zebra_day/web/auth.py +172 -0
- zebra_day/web/middleware.py +159 -0
- zebra_day/web/routers/__init__.py +2 -0
- zebra_day/web/routers/api.py +163 -0
- zebra_day/web/routers/ui.py +1051 -0
- zebra_day/zpl_renderer.py +273 -0
- zebra_day-1.0.2.dist-info/METADATA +786 -0
- zebra_day-1.0.2.dist-info/RECORD +179 -0
- {zebra_day-0.0.32.dist-info → zebra_day-1.0.2.dist-info}/WHEEL +1 -1
- zebra_day-1.0.2.dist-info/entry_points.txt +4 -0
- zebra_day/etc/.blind +0 -1
- zebra_day/etc/current_style.txt +0 -1
- zebra_day/etc/label_styles/test_2inX1in.zpl +0 -22
- zebra_day/etc/label_styles/tmps/labware_2inX1in.na.2023-10-24_12:49:21.045127.zpl +0 -21
- zebra_day/etc/label_styles/tmps/labware_2inX1in.naggg.2023-10-24_16:02:04.704814.8888.2023-10-24_16:02:16.443911.zpl +0 -21
- zebra_day/etc/label_styles/tmps/labware_2inX1in.naggg.2023-10-24_16:02:04.704814.zpl +0 -21
- zebra_day/etc/label_styles/tmps/test_2inX1in.na.2023-10-24_12:45:37.002774.zpl +0 -22
- zebra_day/etc/old_printer_config/2023-10-24_16:06:06.931764_printer_config.json +0 -67
- zebra_day/etc/printer_config.json~ +0 -67
- zebra_day/files/tmp_2olihg4.png +0 -0
- zebra_day/files/tmpveojoyvn.png +0 -0
- zebra_day/files/zpl_label_labware_2inX1in_2023-10-25_02:30:08.093631.png +0 -0
- zebra_day/files/zpl_label_test_2inX1in_2023-10-24_15:54:29.343124.png +0 -0
- zebra_day/files/zpl_label_test_2inX1in_2023-10-24_16:01:45.670132.png +0 -0
- zebra_day/static/beyonce.css +0 -227
- zebra_day/static/datschund_on_moon.css +0 -164
- zebra_day/static/medicalsci.css +0 -144
- zebra_day/static/moar_zebra.css +0 -133
- zebra_day/static/popday.css +0 -140
- zebra_day/static/popday_dark.css +0 -140
- zebra_day/static/popday_dog.css +0 -140
- zebra_day-0.0.32.dist-info/METADATA +0 -14
- zebra_day-0.0.32.dist-info/RECORD +0 -53
- zebra_day-0.0.32.dist-info/entry_points.txt +0 -2
- /zebra_day/{etc/label_styles/blank_0inX0in.zpl → bin/__init__.py} +0 -0
- /zebra_day/etc/label_styles/{labware_2inX1in.zpl → generic_2inX1in.zpl} +0 -0
- {zebra_day-0.0.32.dist-info → zebra_day-1.0.2.dist-info/licenses}/LICENSE +0 -0
- {zebra_day-0.0.32.dist-info → zebra_day-1.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""zebra_day CLI - Zebra Printer Fleet Management CLI using Typer."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from zebra_day import paths as xdg
|
|
13
|
+
from zebra_day.cli.gui import gui_app
|
|
14
|
+
from zebra_day.cli.printer import printer_app
|
|
15
|
+
from zebra_day.cli.template import template_app
|
|
16
|
+
from zebra_day.cli.cognito import cognito_app
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
app = typer.Typer(
|
|
21
|
+
name="zday",
|
|
22
|
+
help="zebra_day - Zebra Printer Fleet Management CLI",
|
|
23
|
+
add_completion=True,
|
|
24
|
+
no_args_is_help=True,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Register subcommand groups
|
|
28
|
+
app.add_typer(gui_app, name="gui", help="Web UI server management")
|
|
29
|
+
app.add_typer(printer_app, name="printer", help="Printer fleet management")
|
|
30
|
+
app.add_typer(template_app, name="template", help="ZPL template management")
|
|
31
|
+
app.add_typer(cognito_app, name="cognito", help="Cognito authentication management")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_version() -> str:
|
|
35
|
+
"""Get zebra_day version."""
|
|
36
|
+
try:
|
|
37
|
+
from importlib.metadata import version
|
|
38
|
+
return version("zebra_day")
|
|
39
|
+
except Exception:
|
|
40
|
+
return "dev"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@app.command("version")
|
|
44
|
+
def version():
|
|
45
|
+
"""Show zebra_day version."""
|
|
46
|
+
console.print(f"zebra_day [cyan]{_get_version()}[/cyan]")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.command("info")
|
|
50
|
+
def info():
|
|
51
|
+
"""Show zebra_day configuration and status."""
|
|
52
|
+
table = Table(title="zebra_day Info")
|
|
53
|
+
table.add_column("Property", style="cyan")
|
|
54
|
+
table.add_column("Value")
|
|
55
|
+
|
|
56
|
+
# Version
|
|
57
|
+
table.add_row("Version", _get_version())
|
|
58
|
+
table.add_row("Python", sys.version.split()[0])
|
|
59
|
+
|
|
60
|
+
# XDG Paths
|
|
61
|
+
table.add_row("Config Dir", str(xdg.get_config_dir()))
|
|
62
|
+
table.add_row("Data Dir", str(xdg.get_data_dir()))
|
|
63
|
+
table.add_row("Logs Dir", str(xdg.get_logs_dir()))
|
|
64
|
+
table.add_row("Cache Dir", str(xdg.get_cache_dir()))
|
|
65
|
+
|
|
66
|
+
# Printer config
|
|
67
|
+
printer_cfg = xdg.get_printer_config_path()
|
|
68
|
+
if printer_cfg.exists():
|
|
69
|
+
table.add_row("Printer Config", f"[green]{printer_cfg}[/green]")
|
|
70
|
+
else:
|
|
71
|
+
table.add_row("Printer Config", f"[yellow]not found[/yellow] [dim]({printer_cfg})[/dim]")
|
|
72
|
+
|
|
73
|
+
# Check if GUI server is running
|
|
74
|
+
pid_file = xdg.get_state_dir() / "gui.pid"
|
|
75
|
+
if pid_file.exists():
|
|
76
|
+
try:
|
|
77
|
+
pid = int(pid_file.read_text().strip())
|
|
78
|
+
os.kill(pid, 0)
|
|
79
|
+
table.add_row("GUI Server", f"[green]Running[/green] (PID {pid})")
|
|
80
|
+
except (ValueError, ProcessLookupError, PermissionError):
|
|
81
|
+
table.add_row("GUI Server", "[dim]Stopped[/dim]")
|
|
82
|
+
else:
|
|
83
|
+
table.add_row("GUI Server", "[dim]Stopped[/dim]")
|
|
84
|
+
|
|
85
|
+
console.print(table)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@app.command("status")
|
|
89
|
+
def status(
|
|
90
|
+
json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
|
|
91
|
+
):
|
|
92
|
+
"""Show printer fleet status, network connectivity, and service health."""
|
|
93
|
+
import json as json_mod
|
|
94
|
+
|
|
95
|
+
status_data = {
|
|
96
|
+
"gui_server": {"running": False, "pid": None, "url": None},
|
|
97
|
+
"printers": {"configured": 0, "labs": []},
|
|
98
|
+
"config": {"exists": False, "path": None},
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# Check GUI server
|
|
102
|
+
pid_file = xdg.get_state_dir() / "gui.pid"
|
|
103
|
+
if pid_file.exists():
|
|
104
|
+
try:
|
|
105
|
+
pid = int(pid_file.read_text().strip())
|
|
106
|
+
os.kill(pid, 0)
|
|
107
|
+
status_data["gui_server"]["running"] = True
|
|
108
|
+
status_data["gui_server"]["pid"] = pid
|
|
109
|
+
status_data["gui_server"]["url"] = "http://0.0.0.0:8118"
|
|
110
|
+
except (ValueError, ProcessLookupError, PermissionError):
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
# Check printer config
|
|
114
|
+
printer_cfg = xdg.get_printer_config_path()
|
|
115
|
+
status_data["config"]["path"] = str(printer_cfg)
|
|
116
|
+
if printer_cfg.exists():
|
|
117
|
+
status_data["config"]["exists"] = True
|
|
118
|
+
try:
|
|
119
|
+
import zebra_day.print_mgr as zdpm
|
|
120
|
+
zp = zdpm.zpl()
|
|
121
|
+
if hasattr(zp, "printers") and "labs" in zp.printers:
|
|
122
|
+
labs = list(zp.printers["labs"].keys())
|
|
123
|
+
status_data["printers"]["labs"] = labs
|
|
124
|
+
total_printers = sum(
|
|
125
|
+
len([k for k in zp.printers["labs"][lab].keys()])
|
|
126
|
+
for lab in labs
|
|
127
|
+
)
|
|
128
|
+
status_data["printers"]["configured"] = total_printers
|
|
129
|
+
except Exception:
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
if json_output:
|
|
133
|
+
console.print(json_mod.dumps(status_data, indent=2))
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
# Human-readable output
|
|
137
|
+
console.print("\n[bold]Service Status[/bold]")
|
|
138
|
+
if status_data["gui_server"]["running"]:
|
|
139
|
+
console.print(f" [green]●[/green] GUI Server: [green]Running[/green] (PID {status_data['gui_server']['pid']})")
|
|
140
|
+
console.print(f" URL: [cyan]{status_data['gui_server']['url']}[/cyan]")
|
|
141
|
+
else:
|
|
142
|
+
console.print(" [dim]○[/dim] GUI Server: [dim]Not running[/dim]")
|
|
143
|
+
|
|
144
|
+
console.print("\n[bold]Printer Fleet[/bold]")
|
|
145
|
+
if status_data["config"]["exists"]:
|
|
146
|
+
console.print(f" [green]●[/green] Config: [green]Loaded[/green]")
|
|
147
|
+
console.print(f" Printers: {status_data['printers']['configured']}")
|
|
148
|
+
console.print(f" Labs: {', '.join(status_data['printers']['labs']) or 'none'}")
|
|
149
|
+
else:
|
|
150
|
+
console.print(" [yellow]○[/yellow] Config: [yellow]Not found[/yellow]")
|
|
151
|
+
console.print(f" Run [cyan]zday bootstrap[/cyan] to initialize")
|
|
152
|
+
console.print()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@app.command("bootstrap")
|
|
156
|
+
def bootstrap(
|
|
157
|
+
ip_stub: Optional[str] = typer.Option(None, "--ip-stub", "-i", help="IP stub for printer scan (e.g., 192.168.1)"),
|
|
158
|
+
skip_scan: bool = typer.Option(False, "--skip-scan", "-s", help="Skip printer network scan"),
|
|
159
|
+
json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
|
|
160
|
+
):
|
|
161
|
+
"""Initialize configuration, scan for printers, and setup first-time environment.
|
|
162
|
+
|
|
163
|
+
This is the recommended first-time setup command. It will:
|
|
164
|
+
1. Create XDG configuration directories
|
|
165
|
+
2. Initialize printer configuration
|
|
166
|
+
3. Scan the network for Zebra printers (unless --skip-scan)
|
|
167
|
+
"""
|
|
168
|
+
import json as json_mod
|
|
169
|
+
import time
|
|
170
|
+
import socket
|
|
171
|
+
|
|
172
|
+
result = {
|
|
173
|
+
"config_dir": str(xdg.get_config_dir()),
|
|
174
|
+
"data_dir": str(xdg.get_data_dir()),
|
|
175
|
+
"printers_found": 0,
|
|
176
|
+
"labs": [],
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if not json_output:
|
|
180
|
+
console.print("\n[bold cyan]zebra_day Bootstrap[/bold cyan]\n")
|
|
181
|
+
console.print("[green]✓[/green] Config directory: " + result["config_dir"])
|
|
182
|
+
console.print("[green]✓[/green] Data directory: " + result["data_dir"])
|
|
183
|
+
|
|
184
|
+
if skip_scan:
|
|
185
|
+
if not json_output:
|
|
186
|
+
console.print("[dim]Skipping printer scan (--skip-scan)[/dim]")
|
|
187
|
+
else:
|
|
188
|
+
# Determine IP stub
|
|
189
|
+
if not ip_stub:
|
|
190
|
+
try:
|
|
191
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
192
|
+
s.connect(("8.8.8.8", 80))
|
|
193
|
+
local_ip = s.getsockname()[0]
|
|
194
|
+
s.close()
|
|
195
|
+
ip_stub = ".".join(local_ip.split(".")[:-1])
|
|
196
|
+
except Exception:
|
|
197
|
+
ip_stub = "192.168.1"
|
|
198
|
+
|
|
199
|
+
if not json_output:
|
|
200
|
+
console.print(f"\n[cyan]→[/cyan] Scanning network for Zebra printers ({ip_stub}.*)...")
|
|
201
|
+
console.print("[dim] This may take a few minutes...[/dim]")
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
import zebra_day.print_mgr as zdpm
|
|
205
|
+
zp = zdpm.zpl()
|
|
206
|
+
zp.probe_zebra_printers_add_to_printers_json(ip_stub=ip_stub)
|
|
207
|
+
|
|
208
|
+
if hasattr(zp, "printers") and "labs" in zp.printers:
|
|
209
|
+
for lab in zp.printers["labs"]:
|
|
210
|
+
printers_in_lab = len([k for k in zp.printers["labs"][lab].keys()])
|
|
211
|
+
result["printers_found"] += printers_in_lab
|
|
212
|
+
result["labs"].append(lab)
|
|
213
|
+
|
|
214
|
+
if not json_output:
|
|
215
|
+
console.print(f"[green]✓[/green] Scan complete: {result['printers_found']} printer(s) found")
|
|
216
|
+
if result["labs"]:
|
|
217
|
+
console.print(f" Labs: {', '.join(result['labs'])}")
|
|
218
|
+
except Exception as e:
|
|
219
|
+
if not json_output:
|
|
220
|
+
console.print(f"[yellow]⚠[/yellow] Scan error: {e}")
|
|
221
|
+
|
|
222
|
+
if json_output:
|
|
223
|
+
console.print(json_mod.dumps(result, indent=2))
|
|
224
|
+
else:
|
|
225
|
+
console.print("\n[bold green]✓ Bootstrap complete![/bold green]")
|
|
226
|
+
console.print("\n[bold]Next steps:[/bold]")
|
|
227
|
+
console.print(" zday gui start Start the web UI")
|
|
228
|
+
console.print(" zday printer list Show configured printers")
|
|
229
|
+
console.print(" zday info Show configuration details")
|
|
230
|
+
console.print()
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def main():
|
|
234
|
+
"""Main CLI entry point."""
|
|
235
|
+
app()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
if __name__ == "__main__":
|
|
239
|
+
raise SystemExit(main())
|
|
240
|
+
|
zebra_day/cli/cognito.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Cognito authentication management commands for zebra_day CLI.
|
|
2
|
+
|
|
3
|
+
This module delegates to daylily-cognito if available, otherwise provides
|
|
4
|
+
basic status and info commands.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _is_cognito_available() -> bool:
|
|
18
|
+
"""Check if daylily-cognito is installed."""
|
|
19
|
+
try:
|
|
20
|
+
from daylily_cognito.cli import cognito_app as _ # noqa: F401
|
|
21
|
+
return True
|
|
22
|
+
except ImportError:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_cognito_app() -> typer.Typer:
|
|
27
|
+
"""Get the Cognito CLI app, either from daylily-cognito or a fallback."""
|
|
28
|
+
if _is_cognito_available():
|
|
29
|
+
# Import and return the full cognito CLI from daylily-cognito
|
|
30
|
+
from daylily_cognito.cli import cognito_app
|
|
31
|
+
return cognito_app
|
|
32
|
+
else:
|
|
33
|
+
# Return a minimal fallback app
|
|
34
|
+
return _create_fallback_app()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _create_fallback_app() -> typer.Typer:
|
|
38
|
+
"""Create a fallback cognito app with basic commands."""
|
|
39
|
+
app = typer.Typer(help="Cognito authentication management (limited - daylily-cognito not installed)")
|
|
40
|
+
|
|
41
|
+
@app.command("status")
|
|
42
|
+
def status():
|
|
43
|
+
"""Show current Cognito authentication configuration."""
|
|
44
|
+
table = Table(title="Cognito Configuration")
|
|
45
|
+
table.add_column("Variable", style="cyan")
|
|
46
|
+
table.add_column("Value")
|
|
47
|
+
table.add_column("Status")
|
|
48
|
+
|
|
49
|
+
pool_id = os.environ.get("COGNITO_USER_POOL_ID")
|
|
50
|
+
client_id = os.environ.get("COGNITO_APP_CLIENT_ID")
|
|
51
|
+
region = os.environ.get("COGNITO_REGION", os.environ.get("AWS_DEFAULT_REGION"))
|
|
52
|
+
|
|
53
|
+
if pool_id:
|
|
54
|
+
# Truncate for display
|
|
55
|
+
display = pool_id[:15] + "..." if len(pool_id) > 15 else pool_id
|
|
56
|
+
table.add_row("COGNITO_USER_POOL_ID", display, "[green]Set[/green]")
|
|
57
|
+
else:
|
|
58
|
+
table.add_row("COGNITO_USER_POOL_ID", "-", "[yellow]Not set[/yellow]")
|
|
59
|
+
|
|
60
|
+
if client_id:
|
|
61
|
+
display = client_id[:15] + "..." if len(client_id) > 15 else client_id
|
|
62
|
+
table.add_row("COGNITO_APP_CLIENT_ID", display, "[green]Set[/green]")
|
|
63
|
+
else:
|
|
64
|
+
table.add_row("COGNITO_APP_CLIENT_ID", "-", "[yellow]Not set[/yellow]")
|
|
65
|
+
|
|
66
|
+
if region:
|
|
67
|
+
table.add_row("COGNITO_REGION", region, "[green]Set[/green]")
|
|
68
|
+
else:
|
|
69
|
+
table.add_row("COGNITO_REGION", "-", "[yellow]Not set[/yellow]")
|
|
70
|
+
|
|
71
|
+
console.print(table)
|
|
72
|
+
|
|
73
|
+
# Summary
|
|
74
|
+
if pool_id and client_id:
|
|
75
|
+
console.print("\n[green]✓[/green] Cognito is configured")
|
|
76
|
+
console.print(" Start server with: [cyan]zday gui start --auth cognito[/cyan]")
|
|
77
|
+
else:
|
|
78
|
+
console.print("\n[yellow]⚠[/yellow] Cognito is not fully configured")
|
|
79
|
+
console.print(" Set environment variables or install daylily-cognito for full management")
|
|
80
|
+
|
|
81
|
+
@app.command("info")
|
|
82
|
+
def info():
|
|
83
|
+
"""Display information about Cognito setup requirements."""
|
|
84
|
+
console.print("\n[bold]Cognito Authentication Setup[/bold]\n")
|
|
85
|
+
console.print("To enable Cognito authentication for zebra_day:\n")
|
|
86
|
+
|
|
87
|
+
console.print("[bold]1. Install auth dependencies:[/bold]")
|
|
88
|
+
console.print(" [cyan]pip install -e \".[auth]\"[/cyan]\n")
|
|
89
|
+
|
|
90
|
+
console.print("[bold]2. Set environment variables:[/bold]")
|
|
91
|
+
console.print(" [cyan]export COGNITO_USER_POOL_ID=your-pool-id[/cyan]")
|
|
92
|
+
console.print(" [cyan]export COGNITO_APP_CLIENT_ID=your-client-id[/cyan]")
|
|
93
|
+
console.print(" [cyan]export COGNITO_REGION=us-west-2[/cyan] # optional\n")
|
|
94
|
+
|
|
95
|
+
console.print("[bold]3. Start server with authentication:[/bold]")
|
|
96
|
+
console.print(" [cyan]zday gui start --auth cognito[/cyan]\n")
|
|
97
|
+
|
|
98
|
+
if not _is_cognito_available():
|
|
99
|
+
console.print("[dim]For full Cognito management (create, teardown), install daylily-cognito:[/dim]")
|
|
100
|
+
console.print("[dim] pip install daylily-cognito[/dim]")
|
|
101
|
+
|
|
102
|
+
@app.command("create")
|
|
103
|
+
def create():
|
|
104
|
+
"""Create/configure a Cognito user pool (requires daylily-cognito)."""
|
|
105
|
+
console.print("[yellow]⚠[/yellow] This command requires daylily-cognito")
|
|
106
|
+
console.print(" Install with: [cyan]pip install -e \".[auth]\"[/cyan]")
|
|
107
|
+
raise typer.Exit(1)
|
|
108
|
+
|
|
109
|
+
@app.command("teardown")
|
|
110
|
+
def teardown():
|
|
111
|
+
"""Remove Cognito configuration (requires daylily-cognito)."""
|
|
112
|
+
console.print("[yellow]⚠[/yellow] This command requires daylily-cognito")
|
|
113
|
+
console.print(" Install with: [cyan]pip install -e \".[auth]\"[/cyan]")
|
|
114
|
+
raise typer.Exit(1)
|
|
115
|
+
|
|
116
|
+
return app
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# Export the cognito app - either the full version from daylily-cognito or the fallback
|
|
120
|
+
cognito_app = _get_cognito_app()
|
|
121
|
+
|
zebra_day/cli/gui.py
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""GUI server management commands for zebra_day CLI."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import signal
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional, Literal
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
from zebra_day import paths as xdg
|
|
16
|
+
|
|
17
|
+
gui_app = typer.Typer(help="Web UI server management commands")
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
# PID and log file locations
|
|
21
|
+
STATE_DIR = xdg.get_state_dir()
|
|
22
|
+
LOG_DIR = xdg.get_logs_dir()
|
|
23
|
+
PID_FILE = STATE_DIR / "gui.pid"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _ensure_dirs():
|
|
27
|
+
"""Ensure state and log directories exist."""
|
|
28
|
+
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
|
29
|
+
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _get_log_file() -> Path:
|
|
33
|
+
"""Get timestamped log file path."""
|
|
34
|
+
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
35
|
+
return LOG_DIR / f"gui_{ts}.log"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _get_latest_log() -> Optional[Path]:
|
|
39
|
+
"""Get the most recent log file."""
|
|
40
|
+
logs = sorted(LOG_DIR.glob("gui_*.log"), reverse=True)
|
|
41
|
+
return logs[0] if logs else None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _get_pid() -> Optional[int]:
|
|
45
|
+
"""Get the running server PID if exists."""
|
|
46
|
+
if PID_FILE.exists():
|
|
47
|
+
try:
|
|
48
|
+
pid = int(PID_FILE.read_text().strip())
|
|
49
|
+
os.kill(pid, 0)
|
|
50
|
+
return pid
|
|
51
|
+
except (ValueError, ProcessLookupError, PermissionError):
|
|
52
|
+
PID_FILE.unlink(missing_ok=True)
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _check_auth_dependencies() -> bool:
|
|
57
|
+
"""Check if auth dependencies are available."""
|
|
58
|
+
try:
|
|
59
|
+
import jose # noqa: F401
|
|
60
|
+
return True
|
|
61
|
+
except ImportError:
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@gui_app.command("start")
|
|
66
|
+
def start(
|
|
67
|
+
port: int = typer.Option(8118, "--port", "-p", help="Port to run the server on"),
|
|
68
|
+
host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
|
|
69
|
+
auth: str = typer.Option("none", "--auth", "-a", help="Authentication mode: none or cognito"),
|
|
70
|
+
reload: bool = typer.Option(False, "--reload", "-r", help="Enable auto-reload (foreground)"),
|
|
71
|
+
background: bool = typer.Option(True, "--background/--foreground", "-b/-f", help="Run in background"),
|
|
72
|
+
):
|
|
73
|
+
"""Start the zebra_day web UI server."""
|
|
74
|
+
_ensure_dirs()
|
|
75
|
+
|
|
76
|
+
# Validate auth option
|
|
77
|
+
if auth not in ("none", "cognito"):
|
|
78
|
+
console.print(f"[red]✗[/red] Invalid auth mode: {auth}. Use 'none' or 'cognito'.")
|
|
79
|
+
raise typer.Exit(1)
|
|
80
|
+
|
|
81
|
+
# Check if already running
|
|
82
|
+
pid = _get_pid()
|
|
83
|
+
if pid:
|
|
84
|
+
console.print(f"[yellow]⚠[/yellow] Server already running (PID {pid})")
|
|
85
|
+
console.print(f" URL: [cyan]http://{host}:{port}[/cyan]")
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
# Check auth dependencies if cognito mode
|
|
89
|
+
if auth == "cognito":
|
|
90
|
+
if not _check_auth_dependencies():
|
|
91
|
+
console.print("[red]✗[/red] Authentication requested but python-jose is not installed")
|
|
92
|
+
console.print(" Install with: [cyan]pip install -e \".[auth]\"[/cyan]")
|
|
93
|
+
raise typer.Exit(1)
|
|
94
|
+
|
|
95
|
+
# Check required env vars
|
|
96
|
+
missing = []
|
|
97
|
+
if not os.environ.get("COGNITO_USER_POOL_ID"):
|
|
98
|
+
missing.append("COGNITO_USER_POOL_ID")
|
|
99
|
+
if not os.environ.get("COGNITO_APP_CLIENT_ID"):
|
|
100
|
+
missing.append("COGNITO_APP_CLIENT_ID")
|
|
101
|
+
if missing:
|
|
102
|
+
console.print("[red]✗[/red] Cognito auth enabled but environment variables missing:")
|
|
103
|
+
for var in missing:
|
|
104
|
+
console.print(f" • {var}")
|
|
105
|
+
raise typer.Exit(1)
|
|
106
|
+
console.print("[green]✓[/green] Cognito authentication enabled")
|
|
107
|
+
|
|
108
|
+
# Build command
|
|
109
|
+
cmd = [
|
|
110
|
+
sys.executable,
|
|
111
|
+
"-c",
|
|
112
|
+
f"from zebra_day.web.app import run_server; run_server(host='{host}', port={port}, reload={reload}, auth='{auth}')",
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
# Set up environment
|
|
116
|
+
env = os.environ.copy()
|
|
117
|
+
env["PYTHONUNBUFFERED"] = "1"
|
|
118
|
+
env["ZEBRA_DAY_AUTH_MODE"] = auth
|
|
119
|
+
|
|
120
|
+
if reload:
|
|
121
|
+
background = False
|
|
122
|
+
console.print("[dim]Auto-reload enabled (foreground mode)[/dim]")
|
|
123
|
+
|
|
124
|
+
if background:
|
|
125
|
+
log_file = _get_log_file()
|
|
126
|
+
log_f = open(log_file, "w", buffering=1)
|
|
127
|
+
|
|
128
|
+
proc = subprocess.Popen(
|
|
129
|
+
cmd,
|
|
130
|
+
stdout=log_f,
|
|
131
|
+
stderr=subprocess.STDOUT,
|
|
132
|
+
start_new_session=True,
|
|
133
|
+
cwd=Path.cwd(),
|
|
134
|
+
env=env,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
time.sleep(2)
|
|
138
|
+
if proc.poll() is not None:
|
|
139
|
+
log_f.close()
|
|
140
|
+
console.print("[red]✗[/red] Server failed to start. Check logs:")
|
|
141
|
+
console.print(f" [dim]{log_file}[/dim]")
|
|
142
|
+
if log_file.exists():
|
|
143
|
+
content = log_file.read_text().strip()
|
|
144
|
+
if content:
|
|
145
|
+
console.print("\n[dim]--- Last error ---[/dim]")
|
|
146
|
+
for line in content.split("\n")[-10:]:
|
|
147
|
+
console.print(f" {line}")
|
|
148
|
+
raise typer.Exit(1)
|
|
149
|
+
|
|
150
|
+
PID_FILE.write_text(str(proc.pid))
|
|
151
|
+
console.print(f"[green]✓[/green] Server started (PID {proc.pid})")
|
|
152
|
+
console.print(f" URL: [cyan]http://{host}:{port}[/cyan]")
|
|
153
|
+
console.print(f" Logs: [dim]{log_file}[/dim]")
|
|
154
|
+
else:
|
|
155
|
+
console.print(f"[green]✓[/green] Starting server on [cyan]http://{host}:{port}[/cyan]")
|
|
156
|
+
console.print(" Press Ctrl+C to stop\n")
|
|
157
|
+
try:
|
|
158
|
+
result = subprocess.run(cmd, cwd=Path.cwd(), env=env)
|
|
159
|
+
if result.returncode != 0:
|
|
160
|
+
raise typer.Exit(result.returncode)
|
|
161
|
+
except KeyboardInterrupt:
|
|
162
|
+
console.print("\n[yellow]⚠[/yellow] Server stopped")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@gui_app.command("stop")
|
|
166
|
+
def stop():
|
|
167
|
+
"""Stop the zebra_day web UI server."""
|
|
168
|
+
pid = _get_pid()
|
|
169
|
+
if not pid:
|
|
170
|
+
console.print("[yellow]⚠[/yellow] No server running")
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
os.kill(pid, signal.SIGTERM)
|
|
175
|
+
for _ in range(10):
|
|
176
|
+
time.sleep(0.5)
|
|
177
|
+
try:
|
|
178
|
+
os.kill(pid, 0)
|
|
179
|
+
except ProcessLookupError:
|
|
180
|
+
break
|
|
181
|
+
else:
|
|
182
|
+
os.kill(pid, signal.SIGKILL)
|
|
183
|
+
|
|
184
|
+
PID_FILE.unlink(missing_ok=True)
|
|
185
|
+
console.print(f"[green]✓[/green] Server stopped (was PID {pid})")
|
|
186
|
+
except ProcessLookupError:
|
|
187
|
+
PID_FILE.unlink(missing_ok=True)
|
|
188
|
+
console.print("[yellow]⚠[/yellow] Server was not running")
|
|
189
|
+
except PermissionError:
|
|
190
|
+
console.print(f"[red]✗[/red] Permission denied stopping PID {pid}")
|
|
191
|
+
raise typer.Exit(1)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@gui_app.command("status")
|
|
195
|
+
def status():
|
|
196
|
+
"""Check the status of the zebra_day web UI server."""
|
|
197
|
+
pid = _get_pid()
|
|
198
|
+
if pid:
|
|
199
|
+
log_file = _get_latest_log()
|
|
200
|
+
console.print(f"[green]●[/green] Server is [green]running[/green] (PID {pid})")
|
|
201
|
+
console.print(f" URL: [cyan]http://0.0.0.0:8118[/cyan]")
|
|
202
|
+
if log_file:
|
|
203
|
+
console.print(f" Logs: [dim]{log_file}[/dim]")
|
|
204
|
+
else:
|
|
205
|
+
console.print("[dim]○[/dim] Server is [dim]not running[/dim]")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@gui_app.command("logs")
|
|
209
|
+
def logs(
|
|
210
|
+
lines: int = typer.Option(50, "--tail", "-n", help="Number of lines to show"),
|
|
211
|
+
follow: bool = typer.Option(False, "--follow", "-f", help="Follow log output"),
|
|
212
|
+
all_logs: bool = typer.Option(False, "--all", "-a", help="List all log files"),
|
|
213
|
+
):
|
|
214
|
+
"""View zebra_day web UI server logs."""
|
|
215
|
+
if all_logs:
|
|
216
|
+
log_files = sorted(LOG_DIR.glob("gui_*.log"), reverse=True)
|
|
217
|
+
if not log_files:
|
|
218
|
+
console.print("[yellow]⚠[/yellow] No log files found.")
|
|
219
|
+
return
|
|
220
|
+
console.print(f"[bold]Server log files ({len(log_files)}):[/bold]")
|
|
221
|
+
for lf in log_files[:20]:
|
|
222
|
+
size = lf.stat().st_size
|
|
223
|
+
console.print(f" {lf.name} [dim]({size:,} bytes)[/dim]")
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
log_file = _get_latest_log()
|
|
227
|
+
if not log_file:
|
|
228
|
+
console.print("[yellow]⚠[/yellow] No log file found. Start the server first.")
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
if follow:
|
|
232
|
+
console.print(f"[dim]Following {log_file.name} (Ctrl+C to stop)[/dim]\n")
|
|
233
|
+
try:
|
|
234
|
+
subprocess.run(["tail", "-f", "-n", str(lines), str(log_file)])
|
|
235
|
+
except KeyboardInterrupt:
|
|
236
|
+
console.print("\n")
|
|
237
|
+
else:
|
|
238
|
+
console.print(f"[dim]Showing last {lines} lines of {log_file.name}[/dim]\n")
|
|
239
|
+
try:
|
|
240
|
+
subprocess.run(["tail", "-n", str(lines), str(log_file)])
|
|
241
|
+
except Exception as e:
|
|
242
|
+
console.print(f"[red]✗[/red] Error reading log: {e}")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@gui_app.command("restart")
|
|
246
|
+
def restart(
|
|
247
|
+
port: int = typer.Option(8118, "--port", "-p", help="Port to run the server on"),
|
|
248
|
+
host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
|
|
249
|
+
auth: str = typer.Option("none", "--auth", "-a", help="Authentication mode: none or cognito"),
|
|
250
|
+
):
|
|
251
|
+
"""Restart the zebra_day web UI server."""
|
|
252
|
+
stop()
|
|
253
|
+
time.sleep(1)
|
|
254
|
+
start(port=port, host=host, auth=auth, reload=False, background=True)
|
|
255
|
+
|