diff --git a/ESP32CAM-ONVIF/data/app.js b/ESP32CAM-ONVIF/data/app.js new file mode 100644 index 0000000..90c9c83 --- /dev/null +++ b/ESP32CAM-ONVIF/data/app.js @@ -0,0 +1,112 @@ +// --- Login Overlay Logic --- +let authHeader = ""; +document.addEventListener("DOMContentLoaded", function() { + if (localStorage.getItem("auth")) { + authHeader = localStorage.getItem("auth"); + document.getElementById("login-overlay").style.display = "none"; + updateStatus(); + } +}); +function login(e) { + e.preventDefault(); + const user = document.getElementById("username").value; + const pass = document.getElementById("password").value; + authHeader = "Basic " + btoa(user + ":" + pass); + fetch("/api/status", {headers: {"Authorization": authHeader}}) + .then(r => { + if (r.status === 401) throw new Error("Unauthorized"); + return r.json(); + }) + .then(() => { + document.getElementById("login-overlay").style.display = "none"; + localStorage.setItem("auth", authHeader); + updateStatus(); + }) + .catch(() => { + document.getElementById("login-error").textContent = "Invalid credentials!"; + authHeader = ""; + localStorage.removeItem("auth"); + }); + return false; +} + +// --- API Helpers --- +function api(url, opts={}) { + opts.headers = opts.headers || {}; + opts.headers["Authorization"] = authHeader; + return fetch(url, opts); +} + +// --- Tab Navigation --- +function showPanel(panel) { + document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active')); + document.querySelectorAll('.panel').forEach(sec => sec.style.display = 'none'); + document.querySelector('.tab-btn[onclick="showPanel(\''+panel+'\')"]').classList.add('active'); + document.getElementById('panel-' + panel).style.display = ''; + if (panel === 'sd') refreshSD(); +} + +// --- SD Card Management --- +function refreshSD() { + api('/api/sd/list').then(r => r.json()).then(files => { + const tbody = document.querySelector('#sd-table tbody'); + tbody.innerHTML = ''; + files.forEach(file => { + const tr = document.createElement('tr'); + tr.innerHTML = `