lanscape 1.3.8a1__py3-none-any.whl → 2.4.0a2__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.

Potentially problematic release.


This version of lanscape might be problematic. Click here for more details.

Files changed (58) hide show
  1. lanscape/__init__.py +8 -4
  2. lanscape/{libraries → core}/app_scope.py +21 -3
  3. lanscape/core/decorators.py +231 -0
  4. lanscape/{libraries → core}/device_alive.py +83 -16
  5. lanscape/{libraries → core}/ip_parser.py +2 -26
  6. lanscape/{libraries → core}/net_tools.py +209 -66
  7. lanscape/{libraries → core}/runtime_args.py +6 -0
  8. lanscape/{libraries → core}/scan_config.py +103 -5
  9. lanscape/core/service_scan.py +222 -0
  10. lanscape/{libraries → core}/subnet_scan.py +30 -14
  11. lanscape/{libraries → core}/version_manager.py +15 -17
  12. lanscape/resources/ports/test_port_list_scan.json +4 -0
  13. lanscape/resources/services/definitions.jsonc +576 -400
  14. lanscape/ui/app.py +17 -5
  15. lanscape/ui/blueprints/__init__.py +1 -1
  16. lanscape/ui/blueprints/api/port.py +15 -1
  17. lanscape/ui/blueprints/api/scan.py +1 -1
  18. lanscape/ui/blueprints/api/tools.py +4 -4
  19. lanscape/ui/blueprints/web/routes.py +29 -2
  20. lanscape/ui/main.py +46 -19
  21. lanscape/ui/shutdown_handler.py +2 -2
  22. lanscape/ui/static/css/style.css +186 -20
  23. lanscape/ui/static/js/core.js +14 -0
  24. lanscape/ui/static/js/main.js +30 -2
  25. lanscape/ui/static/js/quietReload.js +3 -0
  26. lanscape/ui/static/js/scan-config.js +56 -6
  27. lanscape/ui/templates/base.html +6 -8
  28. lanscape/ui/templates/core/head.html +1 -1
  29. lanscape/ui/templates/info.html +20 -5
  30. lanscape/ui/templates/main.html +33 -36
  31. lanscape/ui/templates/scan/config.html +214 -176
  32. lanscape/ui/templates/scan/device-detail.html +111 -0
  33. lanscape/ui/templates/scan/ip-table-row.html +17 -83
  34. lanscape/ui/templates/scan/ip-table.html +5 -5
  35. lanscape/ui/ws/__init__.py +31 -0
  36. lanscape/ui/ws/delta.py +170 -0
  37. lanscape/ui/ws/handlers/__init__.py +20 -0
  38. lanscape/ui/ws/handlers/base.py +145 -0
  39. lanscape/ui/ws/handlers/port.py +184 -0
  40. lanscape/ui/ws/handlers/scan.py +352 -0
  41. lanscape/ui/ws/handlers/tools.py +145 -0
  42. lanscape/ui/ws/protocol.py +86 -0
  43. lanscape/ui/ws/server.py +375 -0
  44. {lanscape-1.3.8a1.dist-info → lanscape-2.4.0a2.dist-info}/METADATA +18 -3
  45. lanscape-2.4.0a2.dist-info/RECORD +85 -0
  46. {lanscape-1.3.8a1.dist-info → lanscape-2.4.0a2.dist-info}/WHEEL +1 -1
  47. lanscape-2.4.0a2.dist-info/entry_points.txt +2 -0
  48. lanscape/libraries/decorators.py +0 -170
  49. lanscape/libraries/service_scan.py +0 -50
  50. lanscape/libraries/web_browser.py +0 -210
  51. lanscape-1.3.8a1.dist-info/RECORD +0 -74
  52. /lanscape/{libraries → core}/__init__.py +0 -0
  53. /lanscape/{libraries → core}/errors.py +0 -0
  54. /lanscape/{libraries → core}/logger.py +0 -0
  55. /lanscape/{libraries → core}/mac_lookup.py +0 -0
  56. /lanscape/{libraries → core}/port_manager.py +0 -0
  57. {lanscape-1.3.8a1.dist-info → lanscape-2.4.0a2.dist-info}/licenses/LICENSE +0 -0
  58. {lanscape-1.3.8a1.dist-info → lanscape-2.4.0a2.dist-info}/top_level.txt +0 -0
@@ -8,216 +8,254 @@
8
8
  <div class="modal-body">
9
9
  <div class="d-flex gap-2 mb-3">
10
10
  <div class="config-option p-3 flex-fill flex-wrap" id="config-fast" data-config="fast" onclick="setScanConfig('fast')">
11
- <h5 class="mb-1 d-flex align-items-center justify-content-center">
12
- <span class="material-symbols-outlined me-1">bolt</span>Fast
13
- </h5>
11
+ <div class="d-flex flex-column align-items-center justify-content-center">
12
+ <i class="fa-solid fa-bolt mb-2" style="font-size: 1.5em;"></i>
13
+ <h5 class="mb-1">Fast</h5>
14
+ </div>
14
15
  <p class="mb-0">Fast scan, Low accuracy</p>
15
16
  </div>
16
17
  <div class="config-option p-3 flex-fill" id="config-balanced" data-config="balanced" onclick="setScanConfig('balanced')">
17
- <h5 class="mb-1 d-flex align-items-center justify-content-center">
18
- <span class="material-symbols-outlined me-1">balance</span>Balanced
19
- </h5>
18
+ <div class="d-flex flex-column align-items-center justify-content-center">
19
+ <i class="fa-solid fa-scale-balanced mb-2" style="font-size: 1.5em;"></i>
20
+ <h5 class="mb-1">Balanced</h5>
21
+ </div>
20
22
  <p class="mb-0">Balanced scan, Moderate accuracy</p>
21
23
  </div>
22
24
  <div class="config-option p-3 flex-fill" id="config-accurate" data-config="accurate" onclick="setScanConfig('accurate')">
23
- <h5 class="mb-1 d-flex align-items-center justify-content-center">
24
- <span class="material-symbols-outlined me-1">target</span>Accurate
25
- </h5>
25
+ <div class="d-flex flex-column align-items-center justify-content-center">
26
+ <i class="fa-solid fa-bullseye mb-2" style="font-size: 1.5em;"></i>
27
+ <h5 class="mb-1">Accurate</h5>
28
+ </div>
26
29
  <p class="mb-0">Thorough scan, High accuracy</p>
27
30
  </div>
28
31
  </div>
29
32
 
30
- <hr>
31
- <div class="form-group mt-2">
32
- <h6>Device Detection</h6>
33
- <div class="row mt-2">
34
- <div class="col">
35
- <label class="mb-1">Lookup Type</label>
36
- <div id="lookup-type-group" class="d-flex flex-wrap gap-3">
37
- <div class="form-check">
38
- <input class="form-check-input lookup-type-input" type="checkbox" id="lookup_icmp" value="ICMP">
39
- <label class="form-check-label" for="lookup_icmp">ICMP</label>
40
- </div>
41
- <div class="form-check">
42
- <!-- ARP lookup does not always work. Depends on host system -->
43
- <input
44
- class="form-check-input lookup-type-input"
45
- type="checkbox"
46
- id="lookup_arp_lookup"
47
- value="ARP_LOOKUP"
48
- {{ '' if is_arp_supported else 'disabled' }}
49
- />
50
- <label class="form-check-label" for="lookup_arp_lookup">
51
- ARP Lookup
52
- </label>
53
- {% if not is_arp_supported %}
54
- <span
55
- class="material-symbols-outlined arp-help-ico"
56
- data-bs-toggle="tooltip"
57
- data-bs-placement="top"
58
- title="ARP lookup is not supported on this system, click for more info"
59
- onclick="window.open('https://github.com/mdennis281/LANscape/blob/main/support/arp-issues.md', '_blank')"
60
- >
61
- help
62
- </span>
63
- {% endif %}
64
- </div>
65
- <div class="form-check">
66
- <input class="form-check-input lookup-type-input" type="checkbox" id="lookup_icmp_then_arp" value="ICMP_THEN_ARP">
67
- <label class="form-check-label" for="lookup_icmp_then_arp">ICMP then ARP Cache</label>
68
- </div>
69
- <div class="form-check">
70
- <input class="form-check-input lookup-type-input" type="checkbox" id="lookup_poke_then_arp" value="POKE_THEN_ARP">
71
- <label class="form-check-label" for="lookup_poke_then_arp">Poke then ARP Cache</label>
33
+ <div class="config-sections">
34
+ <div class="form-group mt-2 config-section">
35
+ <h6>Device Detection</h6>
36
+ <div class="row mt-2">
37
+ <div class="col">
38
+ <label class="mb-1">Lookup Type</label>
39
+ <div id="lookup-type-group" class="d-flex flex-wrap gap-3">
40
+ <div class="form-check">
41
+ <input class="form-check-input lookup-type-input" type="checkbox" id="lookup_icmp" value="ICMP">
42
+ <label class="form-check-label" for="lookup_icmp">ICMP</label>
43
+ </div>
44
+ <div class="form-check">
45
+ <!-- ARP lookup does not always work. Depends on host system -->
46
+ <input
47
+ class="form-check-input lookup-type-input"
48
+ type="checkbox"
49
+ id="lookup_arp_lookup"
50
+ value="ARP_LOOKUP"
51
+ {{ '' if is_arp_supported else 'disabled' }}
52
+ />
53
+ <label class="form-check-label" for="lookup_arp_lookup">
54
+ ARP Lookup
55
+ </label>
56
+ {% if not is_arp_supported %}
57
+ <i
58
+ class="fa-solid fa-circle-question arp-help-ico"
59
+ data-bs-toggle="tooltip"
60
+ data-bs-placement="top"
61
+ title="ARP lookup is not supported on this system, click for more info"
62
+ onclick="window.open('https://github.com/mdennis281/LANscape/blob/main/docs/arp-issues.md', '_blank')"
63
+ ></i>
64
+ {% endif %}
65
+ </div>
66
+ <div class="form-check">
67
+ <input class="form-check-input lookup-type-input" type="checkbox" id="lookup_icmp_then_arp" value="ICMP_THEN_ARP">
68
+ <label class="form-check-label" for="lookup_icmp_then_arp">ICMP then ARP Cache</label>
69
+ </div>
70
+ <div class="form-check">
71
+ <input class="form-check-input lookup-type-input" type="checkbox" id="lookup_poke_then_arp" value="POKE_THEN_ARP">
72
+ <label class="form-check-label" for="lookup_poke_then_arp">Poke then ARP Cache</label>
73
+ </div>
72
74
  </div>
73
75
  </div>
74
-
75
- </div>
76
- <div class="col">
77
- <label for="t_cnt_isalive">Device Lookup Threads</label>
78
- <input type="number" id="t_cnt_isalive" class="form-control">
76
+ <div class="col">
77
+ <label for="t_cnt_isalive">Device Lookup Threads</label>
78
+ <input type="number" id="t_cnt_isalive" class="form-control">
79
+ </div>
79
80
  </div>
80
81
  </div>
81
- </div>
82
82
 
83
- <hr>
84
- <div id="section-ping" class="form-group mt-2">
85
- <h6>Device Detection <span class=text-color>/</span> Ping Settings</h6>
86
- <div class="row">
87
- <div class="col-3">
88
- <label for="ping_attempts" class="form-label">Ping Attempts</label>
89
- <input type="number" id="ping_attempts" class="form-control">
90
- </div>
91
- <div class="col-1 descriptor">X</div>
92
- <div class="col-3">
93
- <label for="ping_ping_count" class="form-label">Ping Count</label>
94
- <input type="number" id="ping_ping_count" class="form-control">
95
- </div>
96
- <div class="col-1 descriptor">=</div>
97
- <div class="col-3">
98
- <label for="total-ping-attempts">Max pings per device</label>
99
- <input type="number" id="total-ping-attempts" class="form-control" readonly>
100
- </div>
101
- </div>
102
- <div class="row mt-2">
103
- <div class="col">
104
- <label for="ping_timeout" class="form-label">Timeout Per Ping (sec)</label>
105
- <input type="number" step="0.1" id="ping_timeout" class="form-control">
83
+ <div id="section-ping" class="form-group mt-2 config-section">
84
+ <h6>Device Detection <span class=text-color>/</span> Ping Settings</h6>
85
+ <div class="row">
86
+ <div class="col-3">
87
+ <label for="ping_attempts" class="form-label">Ping Attempts</label>
88
+ <input type="number" id="ping_attempts" class="form-control">
89
+ </div>
90
+ <div class="col-1 descriptor">X</div>
91
+ <div class="col-3">
92
+ <label for="ping_ping_count" class="form-label">Ping Count</label>
93
+ <input type="number" id="ping_ping_count" class="form-control">
94
+ </div>
95
+ <div class="col-1 descriptor">=</div>
96
+ <div class="col-3">
97
+ <label for="total-ping-attempts" id="tpa-label">Max pings per device</label>
98
+ <input type="number" id="total-ping-attempts" class="form-control" readonly>
99
+ </div>
106
100
  </div>
107
- <div class="col">
108
- <label for="ping_retry_delay" class="form-label">Delay Between Attempts (sec)</label>
109
- <input type="number" step="0.1" id="ping_retry_delay" class="form-control">
101
+ <div class="row mt-2">
102
+ <div class="col">
103
+ <label for="ping_timeout" class="form-label">Timeout Per Ping (sec)</label>
104
+ <input type="number" step="0.1" id="ping_timeout" class="form-control">
105
+ </div>
106
+ <div class="col">
107
+ <label for="ping_retry_delay" class="form-label">Delay Between Attempts (sec)</label>
108
+ <input type="number" step="0.1" id="ping_retry_delay" class="form-control">
109
+ </div>
110
110
  </div>
111
111
  </div>
112
- </div>
113
112
 
114
- <hr>
115
- <div id="section-arp" class="form-group mt-2">
116
- <h6>Device Detection <span class=text-color>/</span> ARP Settings</h6>
117
- <div class="row">
118
- <div class="col">
119
- <label for="arp_attempts" class="form-label">Attempts</label>
120
- <input type="number" id="arp_attempts" class="form-control">
121
- </div>
122
- <div class="col">
123
- <label for="arp_timeout" class="form-label">Timeout</label>
124
- <input type="number" step="0.1" id="arp_timeout" class="form-control">
113
+ <div id="section-arp" class="form-group mt-2 config-section">
114
+ <h6>Device Detection <span class=text-color>/</span> ARP Settings</h6>
115
+ <div class="row">
116
+ <div class="col">
117
+ <label for="arp_attempts" class="form-label">Attempts</label>
118
+ <input type="number" id="arp_attempts" class="form-control">
119
+ </div>
120
+ <div class="col">
121
+ <label for="arp_timeout" class="form-label">Timeout</label>
122
+ <input type="number" step="0.1" id="arp_timeout" class="form-control">
123
+ </div>
125
124
  </div>
126
125
  </div>
127
- </div>
128
126
 
129
- <hr>
130
- <div id="section-arp-cache" class="form-group mt-2 div-hide">
131
- <h6>Device Detection <span class=text-color>/</span> ARP Cache Settings</h6>
132
- <div class="row">
133
- <div class="col">
134
- <label for="arp_cache_attempts" class="form-label">Attempts</label>
135
- <input type="number" id="arp_cache_attempts" class="form-control">
136
- </div>
137
- <div class="col">
138
- <label for="arp_cache_wait_before" class="form-label">Wait Before (sec)</label>
139
- <input type="number" step="0.1" id="arp_cache_wait_before" class="form-control">
127
+ <div id="section-arp-cache" class="form-group mt-2 div-hide config-section">
128
+ <h6>Device Detection <span class=text-color>/</span> ARP Cache Settings</h6>
129
+ <div class="row">
130
+ <div class="col">
131
+ <label for="arp_cache_attempts" class="form-label">Attempts</label>
132
+ <input type="number" id="arp_cache_attempts" class="form-control">
133
+ </div>
134
+ <div class="col">
135
+ <label for="arp_cache_wait_before" class="form-label">Wait Before (sec)</label>
136
+ <input type="number" step="0.1" id="arp_cache_wait_before" class="form-control">
137
+ </div>
140
138
  </div>
141
139
  </div>
142
- </div>
143
140
 
144
- <hr>
145
- <div id="section-poke" class="form-group mt-2 div-hide">
146
- <h6>Device Detection <span class=text-color>/</span> Poke Settings</h6>
147
- <div class="row">
148
- <div class="col">
149
- <label for="poke_attempts" class="form-label">Attempts</label>
150
- <input type="number" id="poke_attempts" class="form-control">
151
- </div>
152
- <div class="col">
153
- <label for="poke_timeout" class="form-label">Timeout (sec)</label>
154
- <input type="number" step="0.1" id="poke_timeout" class="form-control">
141
+ <div id="section-poke" class="form-group mt-2 div-hide config-section">
142
+ <h6>Device Detection <span class=text-color>/</span> Poke Settings</h6>
143
+ <div class="row">
144
+ <div class="col">
145
+ <label for="poke_attempts" class="form-label">Attempts</label>
146
+ <input type="number" id="poke_attempts" class="form-control">
147
+ </div>
148
+ <div class="col">
149
+ <label for="poke_timeout" class="form-label">Timeout (sec)</label>
150
+ <input type="number" step="0.1" id="poke_timeout" class="form-control">
151
+ </div>
155
152
  </div>
156
153
  </div>
157
- </div>
158
154
 
159
- <hr>
160
-
161
- <div class="form-group mt-2">
162
- <h6>Port Testing</h6>
163
- <div class="row">
164
- <div class="col-3">
165
- <label for="t_cnt_port_scan">Device Scan Threads</label>
166
- <input type="number" id="t_cnt_port_scan" class="form-control">
167
- </div>
168
- <div class="col-1 descriptor">X</div>
169
- <div class="col-3">
170
- <label for="t_cnt_port_test">Port Test Threads</label>
171
- <input type="number" id="t_cnt_port_test" class="form-control">
172
- </div>
173
- <div class="col-1 descriptor">=</div>
174
- <div class="col-3">
175
- <label for="total-port-tests">Total concurrent tests</label>
176
- <input type="number" id="total-port-tests" class="form-control" readonly>
155
+ <div class="form-group mt-2 config-section">
156
+ <h6>Port Testing</h6>
157
+ <div class="row">
158
+ <div class="col-3">
159
+ <label for="t_cnt_port_scan">Device Scan Threads</label>
160
+ <input type="number" id="t_cnt_port_scan" class="form-control">
161
+ </div>
162
+ <div class="col-1 descriptor">X</div>
163
+ <div class="col-3">
164
+ <label for="t_cnt_port_test">Port Test Threads</label>
165
+ <input type="number" id="t_cnt_port_test" class="form-control">
166
+ </div>
167
+ <div class="col-1 descriptor">=</div>
168
+ <div class="col-3">
169
+ <label for="total-port-tests">Total concurrent tests</label>
170
+ <input type="number" id="total-port-tests" class="form-control" readonly>
171
+ </div>
177
172
  </div>
178
- </div>
179
- <div class="row mt-2">
180
- <div class="col">
181
- <label for="port_list">Port List</label>
182
- <div class="port-list-wrapper">
183
- <select id="port_list" class="port-list">
184
- <option value="both">ARP + Ping</option>
185
- <option value="arp">ARP Only</option>
186
- <option value="ping">Ping Only</option>
187
- </select>
173
+ <div class="row mt-2">
174
+ <div class="col">
175
+ <label for="port_list">Port List</label>
176
+ <div class="port-list-wrapper">
177
+ <select id="port_list" class="port-list">
178
+ <option value="both">ARP + Ping</option>
179
+ <option value="arp">ARP Only</option>
180
+ <option value="ping">Ping Only</option>
181
+ </select>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ <div class="row mt-2">
186
+ <div class="col">
187
+ <label for="port_scan_timeout" class="form-label">Port Timeout (sec)</label>
188
+ <input type="number" step="0.1" id="port_scan_timeout" class="form-control">
189
+ </div>
190
+ <div class="col">
191
+ <label for="port_scan_retries" class="form-label">Retries</label>
192
+ <input type="number" id="port_scan_retries" class="form-control">
193
+ </div>
194
+ <div class="col">
195
+ <label for="port_scan_retry_delay" class="form-label">Retry Delay (sec)</label>
196
+ <input type="number" step="0.1" id="port_scan_retry_delay" class="form-control">
188
197
  </div>
189
198
  </div>
190
199
  </div>
191
- <hr>
192
200
 
193
- <div class="form-group mt-2">
194
- <h6>Stages</h6>
195
- <div class="form-check"
196
- data-bs-toggle="tooltip"
197
- data-bs-placement="right"
198
- title="Find Devices using ARP or Ping (always on)"
199
- >
200
- <input class="form-check-input" disabled type="checkbox" id="task_find_devices" checked>
201
- <label class="form-check-label" for="task_find_devices">Find Devices</label>
202
- </div>
203
- <div class="form-check"
204
- data-bs-toggle="tooltip"
205
- data-bs-placement="right"
206
- title="Find open ports on devices"
207
- >
208
- <input class="form-check-input" type="checkbox" id="task_scan_ports">
209
- <label class="form-check-label" for="task_scan_ports">Scan Ports</label>
210
- </div>
211
- <div class="form-check"
212
- data-bs-toggle="tooltip"
213
- data-bs-placement="right"
214
- title="Determine services running on open ports (no supporting UI yet)"
215
- >
216
- <input class="form-check-input" type="checkbox" id="task_scan_port_services">
217
- <label class="form-check-label" for="task_scan_port_services">Scan Port Services</label>
201
+ <div id="section-service-scan" class="form-group mt-2 config-section div-hide">
202
+ <h6>Service Scanning</h6>
203
+ <div class="row">
204
+ <div class="col-12">
205
+ <label for="service_lookup_type" class="form-label">Strategy</label>
206
+ <div class="service-strategy-wrapper">
207
+ <select id="service_lookup_type" class="service-strategy">
208
+ <option value="LAZY">Lazy (few probes)</option>
209
+ <option value="BASIC">Basic (common probes)</option>
210
+ <option value="AGGRESSIVE">Aggressive (all probes)</option>
211
+ </select>
212
+ </div>
213
+ </div>
214
+ </div>
215
+ <div class="row mt-2">
216
+ <div class="col-12 col-md-6">
217
+ <label for="service_timeout" class="form-label">Timeout (sec)</label>
218
+ <input type="number" step="0.1" id="service_timeout" class="form-control">
219
+ </div>
220
+ <div class="col-12 col-md-6 mt-2 mt-md-0">
221
+ <label for="service_max_concurrent_probes" class="form-label">Max Concurrent Probes</label>
222
+ <input type="number" id="service_max_concurrent_probes" class="form-control">
223
+ </div>
224
+ </div>
225
+ <div class="row mt-1">
226
+ <div class="col">
227
+ <small class="text-secondary">Attempts service identification on open ports using selected strategy.</small>
228
+ </div>
229
+ </div>
218
230
  </div>
219
- </div>
220
231
 
232
+ <div class="form-group mt-2 config-section">
233
+ <h6>Stages</h6>
234
+ <div class="form-check"
235
+ data-bs-toggle="tooltip"
236
+ data-bs-placement="right"
237
+ title="Find Devices using ARP or Ping (always on)"
238
+ >
239
+ <input class="form-check-input" disabled type="checkbox" id="task_find_devices" checked>
240
+ <label class="form-check-label" for="task_find_devices">Find Devices</label>
241
+ </div>
242
+ <div class="form-check"
243
+ data-bs-toggle="tooltip"
244
+ data-bs-placement="right"
245
+ title="Find open ports on devices"
246
+ >
247
+ <input class="form-check-input" type="checkbox" id="task_scan_ports">
248
+ <label class="form-check-label" for="task_scan_ports">Scan Ports</label>
249
+ </div>
250
+ <div class="form-check"
251
+ data-bs-toggle="tooltip"
252
+ data-bs-placement="right"
253
+ title="Determine services running on open ports (no supporting UI yet)"
254
+ >
255
+ <input class="form-check-input" type="checkbox" id="task_scan_port_services">
256
+ <label class="form-check-label" for="task_scan_port_services">Scan Port Services</label>
257
+ </div>
258
+ </div>
221
259
  </div>
222
260
 
223
261
  </div>
@@ -226,4 +264,4 @@
226
264
  </div>
227
265
  </div>
228
266
  </div>
229
- </div>
267
+ </div>
@@ -0,0 +1,111 @@
1
+ <div class="modal fade" id="device-modal" tabindex="-1" aria-hidden="true">
2
+ <div class="modal-dialog">
3
+ <div class="modal-content">
4
+ <div class="modal-header border-secondary">
5
+ {% set ip = device.ip|default('Unknown IP') %}
6
+ {% set hostname = device.hostname|default('') %}
7
+ {% set manufacturer = device.manufacturer|default('') %}
8
+ {% set mac = device.mac_addr|default('') %}
9
+ {% set stage = device.stage|default('unknown') %}
10
+ {% set ports = device.ports|default([]) %}
11
+ {% set services = device.services|default({}) %}
12
+ {% set errors = device.caught_errors|default([]) %}
13
+ {% set stage_lower = (stage|string|lower) %}
14
+ {% set stage_badge_class = 'badge-success' if stage_lower == 'complete' else 'badge-warning' if stage_lower in ['running', 'in_progress'] else 'badge-secondary' %}
15
+ <h5 class="modal-title" id="device-modalLabel">
16
+ Device Details
17
+ <span class="text-secondary small ms-2">IP: {{ ip }}</span>
18
+ </h5>
19
+ <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
20
+ </div>
21
+
22
+ <div class="modal-body">
23
+ <div class="config-sections">
24
+ <div class="form-group mt-2 config-section">
25
+ <h6>Overview</h6>
26
+ <div class="kv-grid mt-1">
27
+ <div class="kv-label">IP Address</div>
28
+ <div class="kv-value">{{ ip }}</div>
29
+
30
+ <div class="kv-label">Hostname</div>
31
+ <div class="kv-value">{{ hostname|trim if hostname|default('')|trim else 'Unknown Hostname' }}</div>
32
+
33
+ <div class="kv-label">MAC Address</div>
34
+ <div class="kv-value">{{ mac|trim if mac|default('')|trim else 'Unknown MAC' }}</div>
35
+
36
+ <div class="kv-label">Manufacturer</div>
37
+ <div class="kv-value">{{ manufacturer|trim if manufacturer|default('')|trim else 'Unknown Manufacturer' }}</div>
38
+
39
+ <div class="kv-label">Stage</div>
40
+ <div class="kv-value">
41
+ <span class="badge {{ stage_badge_class }}">{{ stage|default('unknown')|capitalize }}</span>
42
+ </div>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="form-group mt-2 config-section">
47
+ <h6>Open Ports</h6>
48
+ {% if ports and ports|length > 0 %}
49
+ <div class="chip-group mt-1">
50
+ {% for p in ports %}
51
+ <span class="chip" title="Port {{ p }}">
52
+ <i class="fa-solid fa-network-wired"></i>{{ p }}
53
+ </span>
54
+ {% endfor %}
55
+ </div>
56
+ {% else %}
57
+ <div class="text-secondary">No open ports detected.</div>
58
+ {% endif %}
59
+ </div>
60
+
61
+ <div class="form-group mt-2 config-section">
62
+ <h6>Services</h6>
63
+ {% if services and services|length > 0 %}
64
+ <div class="service-list mt-1">
65
+ {% for svc, svc_ports in services|dictsort %}
66
+ <div class="service-row">
67
+ <div class="service-name">
68
+ <i class="fa-solid fa-server align-middle me-1"></i>{{ svc|default('Unknown') }}
69
+ </div>
70
+ <div class="service-ports">
71
+ {% set s_ports = svc_ports|default([]) %}
72
+ {% if s_ports and s_ports|length > 0 %}
73
+ {% for sp in s_ports %}
74
+ <span class="chip" title="{{ svc }} on {{ ip }}:{{ sp }}">{{ sp }}</span>
75
+ {% endfor %}
76
+ {% else %}
77
+ <span class="text-secondary">No ports</span>
78
+ {% endif %}
79
+ </div>
80
+ </div>
81
+ {% endfor %}
82
+ </div>
83
+ {% else %}
84
+ <div class="text-secondary">No service information available.</div>
85
+ {% endif %}
86
+ </div>
87
+
88
+ <div class="form-group mt-2 config-section">
89
+ <h6>Errors</h6>
90
+ {% if errors and errors|length > 0 %}
91
+ <ul class="list-unstyled error-list mt-1">
92
+ {% for err in errors %}
93
+ <li class="text-danger">
94
+ <i class="fa-solid fa-circle-exclamation align-middle me-1"></i>
95
+ {{ err }}
96
+ </li>
97
+ {% endfor %}
98
+ </ul>
99
+ {% else %}
100
+ <div class="text-secondary">No errors captured.</div>
101
+ {% endif %}
102
+ </div>
103
+ </div>
104
+ </div>
105
+
106
+ <div class="modal-footer border-secondary">
107
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </div>