Files
Kareem Horstink 331de4bc9e
Build Android APK / build (push) Failing after 4m21s
Build Server .exe / build (push) Failing after 2m34s
Add settings page, PIN manager, and auto-config
- New settings page at /settings (localhost-only) accessible via tray icon
- PIN manager with 6-digit codes, 5-min TTL, single-use
- API token uses secrets.token_urlsafe(32) for 256-bit entropy
- Config auto-creates config.yaml on first run with detected local IP
- Default PIN and token generated randomly on first startup
- Added pin_ttl setting (default 300s)
- Updated build_exe.py for onefile exe with settings assets bundled
- Added start.bat and start.sh for easy development startup
2026-07-03 08:21:46 +00:00

143 lines
4.5 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PaperDash Settings</title>
<link rel="stylesheet" href="settings.css" />
</head>
<body>
<div class="container">
<h1>PaperDash Settings</h1>
<p class="subtitle">Changes take effect on next restart</p>
<form id="settings-form">
<div class="field">
<label for="dashboard-pin">Dashboard PIN</label>
<input
type="text"
id="dashboard-pin"
maxlength="4"
placeholder="****"
/>
</div>
<div class="field">
<label for="api-token">API Token</label>
<input
type="password"
id="api-token"
placeholder="Bearer token for scripts"
/>
</div>
<div class="field">
<label for="host">Host</label>
<input type="text" id="host" placeholder="Local IP address" />
<p class="hint">Address to bind the server to</p>
</div>
<div class="field">
<label for="port">Port</label>
<input
type="number"
id="port"
placeholder="8921"
min="1"
max="65535"
/>
</div>
<div class="field">
<label for="log-level">Log Level</label>
<select id="log-level">
<option value="DEBUG">DEBUG</option>
<option value="INFO">INFO</option>
<option value="WARNING">WARNING</option>
<option value="ERROR">ERROR</option>
</select>
</div>
<div class="actions">
<button type="button" id="reset-btn">Reset</button>
<button type="submit" id="save-btn">Save</button>
</div>
</form>
<div id="status" class="status"></div>
</div>
<script>
const form = document.getElementById("settings-form");
const statusEl = document.getElementById("status");
async function loadSettings() {
try {
const res = await fetch("/api/v1/settings");
if (!res.ok) throw new Error("Failed to load settings");
const data = await res.json();
document.getElementById("dashboard-pin").value =
data.dashboard_pin || "";
document.getElementById("api-token").value = data.api_token || "";
document.getElementById("host").value = data.host || "";
document.getElementById("port").value = data.port || 8921;
document.getElementById("log-level").value = data.log_level || "INFO";
} catch (err) {
showStatus("Failed to load settings: " + err.message, "error");
}
}
async function saveSettings(e) {
e.preventDefault();
const payload = {
dashboard_pin: document.getElementById("dashboard-pin").value.trim(),
api_token: document.getElementById("api-token").value.trim(),
host: document.getElementById("host").value.trim(),
port: parseInt(document.getElementById("port").value, 10),
log_level: document.getElementById("log-level").value,
};
try {
const res = await fetch("/api/v1/settings", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error("Failed to save settings");
showStatus("Settings saved. Restart the server to apply.", "success");
} catch (err) {
showStatus("Failed to save: " + err.message, "error");
}
}
function resetSettings() {
document.getElementById("dashboard-pin").value = "";
document.getElementById("api-token").value = "";
document.getElementById("host").value = "";
document.getElementById("port").value = 8921;
document.getElementById("log-level").value = "INFO";
hideStatus();
}
function showStatus(msg, type) {
statusEl.textContent = msg;
statusEl.className = "status " + type;
if (type === "success") {
setTimeout(hideStatus, 5000);
}
}
function hideStatus() {
statusEl.className = "status";
statusEl.style.display = "none";
}
form.addEventListener("submit", saveSettings);
document
.getElementById("reset-btn")
.addEventListener("click", resetSettings);
loadSettings();
</script>
</body>
</html>