Files
scheianu 36de1623c2 Add HMAC-SHA256 API authentication to board drivers and edit UI
- Both olimex_esp32_c6_evb and olimex_esp32_c6_evb_pn532 drivers now
  sign every API request with X-Request-Time / X-Request-Sig headers
  using HMAC-SHA256(api_secret, METHOD:path:unix_timestamp)
- Board model gains api_secret column (nullable, default None)
- boards.py edit route saves api_secret from form
- edit.html adds API Secret input with cryptographic Generate button
- If api_secret is empty/None, headers are omitted (backward compat)
2026-03-15 12:33:45 +02:00

113 lines
4.7 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Edit {{ board.name }} Location Management{% endblock %}
{% block content %}
<nav aria-label="breadcrumb" class="mb-3">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('boards.list_boards') }}">Boards</a></li>
<li class="breadcrumb-item"><a href="{{ url_for('boards.board_detail', board_id=board.id) }}">{{ board.name }}</a></li>
<li class="breadcrumb-item active">Edit</li>
</ol>
</nav>
<div class="card border-0 rounded-4" style="max-width:700px">
<div class="card-header bg-transparent fw-semibold pt-3">
<i class="bi bi-pencil me-1"></i> Edit Board — {{ board.name }}
</div>
<div class="card-body">
<form method="POST">
<!-- Connection -->
<h6 class="text-secondary mb-3 text-uppercase small fw-semibold">Connection</h6>
<div class="mb-3">
<label class="form-label">Local Name</label>
<input type="text" name="name" class="form-control" value="{{ board.name }}" required />
</div>
<div class="mb-3">
<label class="form-label">Board Type</label>
<select name="board_type" class="form-select">
{% for value, label in board_types %}
<option value="{{ value }}" {% if board.board_type == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="row g-3 mb-4">
<div class="col-8">
<label class="form-label">IP / Hostname</label>
<input type="text" name="host" class="form-control font-monospace" value="{{ board.host }}" required />
</div>
<div class="col-4">
<label class="form-label">Port</label>
<input type="number" name="port" class="form-control" value="{{ board.port }}" />
</div>
</div>
<div class="row g-3 mb-4">
<div class="col-6">
<label class="form-label">Number of Relays</label>
<input type="number" name="num_relays" class="form-control" value="{{ board.num_relays }}" min="1" max="32" />
</div>
<div class="col-6">
<label class="form-label">Number of Inputs</label>
<input type="number" name="num_inputs" class="form-control" value="{{ board.num_inputs }}" min="0" max="32" />
</div>
</div>
<!-- Labels -->
<h6 class="text-secondary mb-3 text-uppercase small fw-semibold">Relay Labels</h6>
<div class="row g-2 mb-4">
{% for n in range(1, board.num_relays + 1) %}
<div class="col-6">
<label class="form-label small">Relay {{ n }}</label>
<input type="text" name="relay_{{ n }}_label" class="form-control form-control-sm"
placeholder="Relay {{ n }}"
value="{{ board.labels.get('relay_' ~ n, '') }}" />
</div>
{% endfor %}
</div>
<h6 class="text-secondary mb-3 text-uppercase small fw-semibold">Input Labels</h6>
<div class="row g-2 mb-4">
{% for n in range(1, board.num_inputs + 1) %}
<div class="col-6">
<label class="form-label small">Input {{ n }}</label>
<input type="text" name="input_{{ n }}_label" class="form-control form-control-sm"
placeholder="Input {{ n }}"
value="{{ board.labels.get('input_' ~ n, '') }}" />
</div>
{% endfor %}
</div>
<!-- API Security -->
<h6 class="text-secondary mb-3 text-uppercase small fw-semibold">API Security</h6>
<div class="mb-4">
<label class="form-label">API Secret <span class="text-secondary small">(HMAC-SHA256 shared secret)</span></label>
<div class="input-group">
<input type="text" name="api_secret" id="api-secret-input" class="form-control font-monospace"
placeholder="Leave empty to disable API authentication"
value="{{ board.api_secret or '' }}">
<button class="btn btn-outline-secondary" type="button" onclick="genSecret()">
<i class="bi bi-shuffle me-1"></i>Generate
</button>
</div>
<div class="form-text">Must match <code>API_SECRET</code> in the board's <code>secrets.h</code>.</div>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary"><i class="bi bi-check-lg me-1"></i> Save</button>
<a href="{{ url_for('boards.board_detail', board_id=board.id) }}" class="btn btn-outline-secondary">Cancel</a>
</div>
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function genSecret() {
const buf = new Uint8Array(32);
crypto.getRandomValues(buf);
document.getElementById('api-secret-input').value =
Array.from(buf).map(b => b.toString(16).padStart(2, '0')).join('');
}
</script>
{% endblock %}