21e3d61871
New /scan page lists all active devices on the subnet (ARP scan): - 静态绑定: matches a host declaration - 动态分配: matches a dhcpd.leases entry - 未知 / 外部: nothing matches (manually configured or from other DHCP server) Click '转为静态绑定' to convert a discovered device to a static host binding. Also includes: - arp-scan wrapper in dhcp_ops.py (get_local_network, arp_scan, classify_devices) - /scan route with optional auto-refresh every 30s - README updated with arp-scan install step + new feature bullet
110 lines
3.9 KiB
HTML
110 lines
3.9 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}网络扫描{% endblock %}
|
|
{% block content %}
|
|
<div class="page-head">
|
|
<h1>网络扫描</h1>
|
|
<span class="muted small">用 arp-scan 列出网段内活跃设备</span>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<form method="post" class="form-inline">
|
|
<label>接口
|
|
<select name="iface">
|
|
{% for i in ifaces %}
|
|
<option value="{{ i }}" {% if i==iface %}selected{% endif %}>{{ i }}</option>
|
|
{% endfor %}
|
|
{% if not ifaces %}
|
|
<option value="{{ iface }}">{{ iface }}</option>
|
|
{% endif %}
|
|
</select>
|
|
</label>
|
|
<label>网段
|
|
<input type="text" name="network" value="{{ network or '' }}" placeholder="10.168.1.0/24">
|
|
</label>
|
|
<button type="submit" class="btn btn-primary">扫描</button>
|
|
<label class="checkbox-row" style="margin-left:12px">
|
|
<input type="checkbox" id="autoRefresh" checked>
|
|
自动刷新(30 秒)
|
|
</label>
|
|
</form>
|
|
{% if did_scan %}
|
|
<p class="muted small" style="margin-top:8px">{{ scan_msg }}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if did_scan %}
|
|
<div class="card">
|
|
<h2>扫描结果({{ devices|length }} 个设备)</h2>
|
|
{% if devices %}
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th>IP</th>
|
|
<th>MAC</th>
|
|
<th>来源</th>
|
|
<th>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for d in devices %}
|
|
<tr>
|
|
<td><code>{{ d.ip }}</code></td>
|
|
<td><code>{{ d.mac }}</code></td>
|
|
<td>
|
|
{% if d.source == 'static' %}
|
|
<span class="badge badge-ok">静态绑定</span>
|
|
{% elif d.source == 'dynamic' %}
|
|
<span class="badge badge-warn">动态分配</span>
|
|
{% else %}
|
|
<span class="badge">未知 / 外部</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="actions">
|
|
{% if d.source != 'static' and is_admin %}
|
|
<form method="post" action="{{ url_for('host_new') }}" style="display:inline">
|
|
<input type="hidden" name="mac" value="{{ d.mac }}">
|
|
<input type="hidden" name="ip" value="{{ d.ip }}">
|
|
<input type="hidden" name="name" value="dev-{{ d.ip|replace('.', '-') }}">
|
|
<button type="submit" class="btn btn-sm">转为静态绑定</button>
|
|
</form>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
<p class="muted small">
|
|
说明:「静态绑定」= 在 Web 静态绑定(host 声明)里;
|
|
「动态分配」= 在 dhcpd.leases 里被分配过;
|
|
「未知/外部」= 不在上述两类(可能手动配的或别的 DHCP 服务器给的)。
|
|
</p>
|
|
{% else %}
|
|
<p class="muted">未发现活跃设备(网段内无应答)</p>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
<div class="card empty">
|
|
<p>点击「扫描」开始 ARP 扫描。</p>
|
|
<p class="muted small">提示:扫描时会向网段每个 IP 发 ARP 请求,3-5 秒完成。</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<script>
|
|
let timer = null;
|
|
function setupAutoRefresh() {
|
|
const cb = document.getElementById('autoRefresh');
|
|
if (!cb) return;
|
|
if (timer) clearTimeout(timer);
|
|
if (!cb.checked) return;
|
|
// 只在已经扫过一次的情况下自动刷新
|
|
if (document.querySelector('table.data-table')) {
|
|
timer = setTimeout(() => {
|
|
const f = document.querySelector('form.form-inline');
|
|
if (f) f.submit();
|
|
}, 30000);
|
|
}
|
|
}
|
|
document.addEventListener('DOMContentLoaded', setupAutoRefresh);
|
|
document.getElementById('autoRefresh')?.addEventListener('change', setupAutoRefresh);
|
|
</script>
|
|
{% endblock %} |