ability to restrict which parts of the webserver are accessible (#372)

* add ability to restrict www access / css changes

-can now use WEB_SERVER_OPTIONS to decide what parts of the web system are accessible. Set it to  `WEB_SERVER_OPTIONS="cam config car jpeg"` to access everything. Remove things you don't want accessible.
-make the top part of the pages cleaner - now the image in config/cam defaults to smaller but is clickable to see it bigger. 
- separate feed.js from config.js and cam.js
-add warning about RTSP fimrware to diagnostics

* add info on WEB_SERVER_OPTIONS

* Update multicam.html

track and display refresh speed.
This commit is contained in:
virmaior 2023-06-29 11:36:53 +09:00 committed by GitHub
parent 83a05d7221
commit 8d7060c4b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 322 additions and 60 deletions

View File

@ -523,6 +523,14 @@ WEB_SERVER_ENABLED="true"
Enables the local webserver, for configuration, car control, or to retreive an image snapshot via a web browser. Available at : `http://<Camera IP>/` Thank you @virmaior!
```
WEB_SERVER_OPTIONS="cam config car jpeg"`
```
This adds access controls limiting which parts of the web server are exposed.
* cam = cam.cgi
* config = config.cgi
* car = car.sh
* jpeg = jpeg.cgi
---
### SYSLOG:

View File

@ -1,6 +1,4 @@
var feed_interval_frequency = 1000;
// https://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066
function isInt(value) {
return !isNaN(value) &&
@ -16,16 +14,8 @@ function enable_submit()
}
window.onload = function()
window.addEventListener("load",function()
{
var feed = document.getElementById("current_feed");
function update_image()
{
feed.src = feed.src.split("&")[0] + "&load=" + new Date().getTime();
}
feed_interval = setInterval(update_image, feed_interval_frequency);
var sels = document.querySelectorAll('.ii_select').forEach(function(item){
var row = item.getAttribute("row");
item.addEventListener('change',function(e){
@ -56,4 +46,4 @@ window.onload = function()
Array.from(values).forEach(function(item){ item.disabled = false; });
}
});
}
});

View File

@ -1,6 +1,7 @@
#!/opt/wz_mini/bin/bash
# This serves a rudimentary webpage based on wz_mini.conf
. /opt/wz_mini/www/cgi-bin/shared.cgi
test_area_access cam
title="$camver on $camfirmware running wz_mini $hackver as $HOSTNAME"
updated=false
@ -156,12 +157,14 @@ echo -ne "<html><head><title>$title</title>"
handle_css config.css
echo '<script type="text/javascript" src="/cam.js" ></script>'
echo '<script type="text/javascript" src="/feed.js" ></script>'
echo -ne "</head>"
echo -ne '<body ip="'$ipaddr'" mac="'$macaddr'" >'
echo -ne "<h1>$title</h1>";
echo -ne "<div>cam.cgi only lists values in your current configuration file. To add other values, edit manually! </div>"
if [ "$updated" = true ];
then
@ -176,6 +179,7 @@ if [ $base_cam_config != $cam_config ]; then
echo '<div><a href="?action=revert&version='$GET_version'">Revert</a> to this version</a></div>'
fi
echo -ne '<form name="update_config" method="POST" enctype="application/x-www-form-urlencoded" >'

View File

@ -2,6 +2,10 @@
set -x
. /opt/wz_mini/www/cgi-bin/shared.cgi
test_area_access car
echo "HTTP/1.1 200"
echo -e "Content-type: text/html\n\n"
echo ""

View File

@ -2,12 +2,14 @@
# This serves a rudimentary webpage based on wz_mini.conf
. /opt/wz_mini/www/cgi-bin/shared.cgi
test_area_access config
title="$camver on $camfirmware running wz_mini $hackver as $HOSTNAME"
updated=false
echo "HTTP/1.1 200"
echo -e "Content-type: text/html\n\n"
echo ""
@ -172,6 +174,8 @@ echo -ne "<html><head><title>$title</title>"
handle_css config.css
echo '<script type="text/javascript" src="/config.js" ></script>'
echo '<script type="text/javascript" src="/feed.js" ></script>'
echo -ne "</head>"

View File

@ -1,5 +1,5 @@
BODY {
--feed_ratio: 2.5;
--feed_ratio: 1;
--display_bar_height: 20px;
--border-color: #ccc;
padding-bottom: var(--display_bar_height);
@ -7,10 +7,9 @@ BODY {
H1 {
background-color:blue;
color:white;
height:calc(108px * (var(--feed_ratio) - 1));
height:calc(108px * (var(--feed_ratio)));
margin-top:0;
padding:5px;
margin-bottom:108px;
width:calc(100% - (192px * var(--feed_ratio)));
}
@ -56,7 +55,7 @@ H1 {
}
.feed {
position:absolute;
position:fixed;
top:10px;
right:0px;
width:calc(192px * var(--feed_ratio));
@ -167,10 +166,17 @@ H1 {
border-radius:5px;
padding:2px;
padding-right:2px !important;
width:40px;
width:125px;
margin-right:15px;
}
.github_link A {
color:white;
font-size:12px;
}
.full_JPEG {
max-height:100%;
max-width:100%;
height:auto;
width:auto;
}

View File

@ -64,6 +64,9 @@ echo "Firmware Version: $camfirmware <br />"
if [ "$camfirmware" = "4.36.10.2163" ]; then
echo "<div>this version is broken. Please downgrade to a working version</div>"
fi
if [ "$camfirmware" = "4.61.0.3"]; then
echo "<div>wz_mini_hacks does not support the official RTSP firmware.</div>"
fi
echo "<pre>"

View File

@ -1,5 +1,6 @@
#!/bin/sh
. /opt/wz_mini/www/cgi-bin/shared.cgi
test_area_access jpeg
#test for jpeg
if [[ $REQUEST_METHOD = 'GET' ]]; then

View File

@ -1,5 +1,7 @@
#!/bin/sh
# This provides shared values for webpages
base_dir=/opt/wz_mini/
base_hack_ini=/opt/wz_mini/wz_mini.conf
hack_ini=$base_hack_ini
@ -37,11 +39,11 @@ echo -ne '</style>';
function version_info
{
echo "<div id='$1'>"
echo '<div class="github_link" ><a target="_new" href="https://github.com/gtxaspec/wz_mini_hacks">GitHub: wz_mini_hack</a></div>';
echo "<div class='ver_DIV' vertype='Model'>$camver</div>"
echo "<div class='ver_DIV' vertype='Firmware'>$camfirmware</div>"
echo "<div class='ver_DIV' vertype='wz_mini'>$hackver</div>"
echo "<div class='ver_DIV' vertype='Hostname'> $HOSTNAME</div>"
echo '<div class="github_link" ><a target="_new" href="https://github.com/gtxaspec/wz_mini_hacks">Project</a></div>';
echo "</div>"
}
@ -111,3 +113,41 @@ urldecode(){
echo -e "$b"
}
stringContain() { [ -z "${2##*$1*}" ] && [ -z "$1" -o -n "$2" ]; }
test_area_access()
{
echo "search: $1"
values=$(cat "$base_hack_ini" | grep "WEB_SERVER_OPTIONS" | cut -f2 -d= )
if [ -z "$values" ]
then
values="cam config car jpeg"
fi
if [[ "$values" =~ "$1" ]]
then
:
else
echo "HTTP/1.1 200"
echo -e "Content-type: text/html\n\n"
echo ""
echo "<html><head><title>Access Denied</title>"
handle_css config.css
echo "</head><body>"
echo "<h1>access denied to $1</h1>"
echo "<div>access allowed for : $values</div>"
echo "you need to enable access using wz_mini.conf WEB_SERVER_OPTIONS "
version_info display_BAR
echo "</body></html>"
exit
fi
}

View File

@ -1,5 +1,4 @@
var feed_interval_frequency = 1000;
var mac_re = /^[0-9a-f]{1,2}([\.:-])(?:[0-9a-f]{1,2}\1){4}[0-9a-f]{1,2}$/mi;
// https://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066
@ -63,15 +62,8 @@ function enable_submit()
}
window.onload = function()
window.addEventListener("load", function()
{
var feed = document.getElementById("current_feed");
function update_image()
{
feed.src = feed.src.split("&")[0] + "&load=" + new Date().getTime();
}
feed_interval = setInterval(update_image, feed_interval_frequency);
compose_rtsp_block('RTSP_HI_RES');
compose_rtsp_block('RTSP_LOW_RES');
@ -103,4 +95,4 @@ window.onload = function()
}
);
}
});

View File

@ -0,0 +1,20 @@
var feed_interval_frequency = 1000;
window.addEventListener("load",function()
{
var feed = document.getElementById("current_feed");
function update_image()
{
feed.src = feed.src.split("&")[0] + "&load=" + new Date().getTime();
}
feed_interval = setInterval(update_image, feed_interval_frequency);
feed.addEventListener('click',
function(e){
e.preventDefault();
e.target.classList.toggle("full_JPEG");
});
}
);

View File

@ -1,10 +1,11 @@
<html>
<head>
<title>Multicam</title>
<title>Multi-Cam</title>
<meta name="viewport" content="width=device-width, height=device-height,initial-scale=1.0">
<style type="text/css">
BODY {
--cc_bar_height: 20px;
--cam_height: 20%;
}
.za_images {
display:flex;
@ -12,30 +13,66 @@ flex-direction:column;
flex-wrap:wrap;
height:calc(100% - var(--cc_bar_height));
width:100%;
background-color:#cff;
background-color:#5a8dd8;
}
.za_images .cam_DIV {
border:2px solid blue;
border-radius:5px;
padding:2px;
margin:1px;
height:calc(21% - 10px);
position:relative;
}
.za_images .cam_DIV:not(.first_CAM)
{
height:calc(var(--cam_height) - 10px);
text-align:center;
width:17%;
}
.cam_DIV::after {
position:absolute;
bottom:0;
right:0;
color:yellow;
font-size:3vh;
font-weight:bold;
content:attr(cam);
z-index:5;
}
.za_images .first_CAM {
flex:0 0 calc(100% - 40px);
width:75%;
order:0;
}
.za_images .first_CAM
{
width:calc(100% - 17% - 16px);
}
.broken_cam {
border:2px solid red;
}
.active_button {
background:green;
color:white;
}
.inactive_button {
background:grey;
color:black;
}
.full_CAM {
position:absolute;
top:0;
left:0;
width:100% !important;
height:100% !important;
}
.cam_DIV {
position:relative;
}
.za_images IMG {
max-height:100%;
max-width:100%;
position:absolute;
top:0;
left:0;
}
.cam_control {
width:calc(100% - 18px);
@ -49,10 +86,12 @@ background-color:#cff;
border:2px solid green;
}
.cam_control BUTTON {
width:calc(40% - 4px);
width:calc(30% - 4px);
}
.cam_control INPUT {
.cam_control DIV {
width:calc(20% - 4px);
display:inline-flex;
text-align:right;
}
@media (orientation: portrait) and (max-device-width: 450px) {
@ -61,29 +100,63 @@ background-color:#cff;
}
.za_images {
flex-direction:row;
flex-wrap:wrap;
margin-top:30px;
padding-top:20px;
}
.za_images .cam_DIV {
.za_images .cam_DIV:not(.first_CAM) {
width:calc(50% - 10px);
}
.za_images .first_CAM {
.za_images .cam_DIV.first_CAM {
width:calc(100% - 3px) !important;
flex:0 0 calc(100% - 5px) !important;
height:auto !important;
max-height:30%;
height:30%;
}
}
</style>
<script type="text/javascript">
const canWakeLock = () => 'wakeLock' in navigator;
function releaseWakeState() {
if(wakelock) wakelock.release();
wakelock = null;
}
let wakelock;
const requestWakeLock = async () => {
try {
const wakeLock = await navigator.wakeLock.request("screen");
} catch (err) {
// The wake lock request fails - usually system-related, such as low battery.
console.log(`${err.name}, ${err.message}`);
}
};
const cam_tool = {
cam_count : 0,
feed_interval_frequency : 500,
spacing:false,
feed_interval : false,
tick: 0,
feeds : false,
divs : false,
buttons: [],
loads: [],
load_sum :0,
load_fails:0,
focus_click:function(me) {
me.classList.toggle('full_CAM');
return false;
},
click : function() {
if (this.classList.contains('first_CAM')) {
return cam_tool.focus_click(this);
}
for (var i = 0; i < cam_tool.divs.length; i++) {
cam_tool.divs[i].classList.remove("first_CAM");
cam_tool.divs[i].style.order = parseInt(cam_tool.divs[i].getAttribute("cam")) + 1;
@ -91,6 +164,47 @@ const cam_tool = {
this.classList.add("first_CAM");
this.style.order = 0;
},
run_mode: function(e) {
e.preventDefault();
e.stopPropagation();
for (var i = 0; i < cam_tool.modes.length; i++) {
cam_tool.modes[i].classList.remove('active_button');
}
e.target.classList.add('active_button');
if (e.target.getAttribute("mode") == 'auto') {
var average = cam_tool.load_sum / cam_tool.loads.length;
var use = parseInt(average * 2 / 10) * 10;
cam_tool.feed_interval_frequency = use;
cam_tool.calc_spacing();
}
if (e.target.getAttribute("mode") == 'manual') {
cam_tool.feed_interval_frequency = document.querySelector("DIV.load_mode[mode='manual'] INPUT").value;
cam_tool.calc_spacing();
}
},
load_cam : function() {
var cam = this.getAttribute('cam');
if (this.classList.contains('cam_img2')) {
var cam_obj = cam_tool.feeds[cam -1];
} else {
var cam_obj = document.querySelector('img.cam_img2[cam="' + cam + '"]');
}
cam_obj.style.zIndex = 1;
cam_obj.setAttribute('current','n');
this.setAttribute('current','y');
this.style.zIndex = 2;
var load_time = new Date().getTime() - this.getAttribute('load_start');
if (load_time < 100000) {
cam_tool.load_sum += load_time;
cam_tool.loads.push(load_time);
var average = cam_tool.load_sum / cam_tool.loads.length;
document.getElementById('load_average').innerHTML = parseInt(average);
}
},
init : function() {
cam_tool.divs = document.getElementsByClassName('cam_DIV');
for (var i = 0; i < cam_tool.divs.length; i++) {
@ -98,41 +212,111 @@ const cam_tool = {
}
cam_tool.feeds = document.getElementsByClassName('cam_img');
for (var i = 0; i < cam_tool.feeds.length; i++) {
cam_tool.feeds[i].addEventListener('load', cam_tool.load_cam);
}
var img2 = document.getElementsByClassName('cam_img2');
for (var i = 0; i < img2.length; i++) {
img2[i].addEventListener('load', cam_tool.load_cam);
}
cam_tool.buttons["start"] = document.querySelector("BUTTON[action='start']");
cam_tool.buttons["pause"] = document.querySelector("BUTTON[action='pause']");
cam_tool.buttons["start"].addEventListener('click',cam_tool.start);
cam_tool.buttons["pause"].addEventListener('click',cam_tool.pause);
cam_tool.modes = document.getElementsByClassName('load_mode');
for (var i = 0; i < cam_tool.modes.length; i++) {
cam_tool.modes[i].addEventListener('click', cam_tool.run_mode);
}
document.querySelector("DIV.load_mode[mode='manual'] INPUT").value = cam_tool.feed_interval_frequency;
cam_tool.divs[0].click();
cam_tool.start();
},
notify:function(cam,message) {
console.log("camera " + cam + " - " + message);
},
update: function() {
if (((cam_tool.tick *10) % cam_tool.feed_interval_frequency) == 0) {
for (var i= 0; i < cam_tool.feeds.length; i++) {
cam_tool.feeds[i].src = cam_tool.feeds[i].src.split("&")[0] + "&load=" + new Date().getTime();
}
for (var i= 0; i < cam_tool.feeds.length; i++) {
if (((cam_tool.tick * 10) % cam_tool.feed_interval_frequency) == (cam_tool.spacing * i )) {
console.log("i" + i + " tick " + cam_tool.tick + " % interval (" + cam_tool.feed_interval_frequency + ") " + ((cam_tool.tick * 10) % cam_tool.feed_interval_frequency));
var cam1 = cam_tool.feeds[i];
if (cam1.getAttribute('current') == "y") {
cam1.setAttribute('current','n');
var cam = document.querySelector('img.cam_img2[cam="' + (i + 1) + '"]');
cam1.classList.remove('broken_cam');
} else if (document.querySelector('img.cam_img2[cam="' + (i + 1) + '"]').getAttribute('current') == "y")
{
var cam = cam_tool.feeds[i];
document.querySelector('img.cam_img2[cam="' + (i + 1) + '"]').setAttribute('current','n');
} else {
cam1.classList.add('broken_cam');
cam_tool.notify(i,'neither is loaded on camera ');
return false;
}
var load_start = new Date().getTime();
cam.setAttribute('load_start',load_start);
cam.src = cam_tool.feeds[i].src.split("&")[0] + "&load=" + load_start;
}
}
cam_tool.tick++;
},
update_freq(event) {
cam_tool.feed_interval_frequency = event.target.value;
calc_spacing:function() {
cam_tool.spacing = Math.floor( cam_tool.feed_interval_frequency / (cam_tool.cam_count *10 ) ) * 10;
console.log('updated frequency to ' + cam_tool.spacing );
},
update_freq(event) {
console.log('set frequency to ' + event.target.value);
cam_tool.feed_interval_frequency = event.target.value;
cam_tool.calc_spacing();
},
set_buttons:function(active)
{
for (let key in cam_tool.buttons) {
cam_tool.buttons[key].classList.remove('active_button');
cam_tool.buttons[key].classList.add('inactive_button');
}
cam_tool.buttons[active].classList.add("active_button");
cam_tool.buttons[active].classList.remove("inactive_button");
},
pause: function() {
cam_tool.set_buttons('pause');
clearInterval(cam_tool.feed_interval);
cam_tool.tick = 0;
releaseWakeState();
},
start : function() {
if (cam_tool.feed_interval) { cam_tool.pause(); }
cam_tool.feed_interval = setInterval(cam_tool.update, 10);
if (cam_tool.feed_interval) { cam_tool.pause(); }
cam_tool.set_buttons('start');
cam_tool.feed_interval = setInterval(cam_tool.update, 10);
requestWakeLock();
},
compose:function(cam_count,url) {
var zam = document.getElementById("za_images");
for (var i = 1; i <= cam_count; i++)
{
var id = document.createElement('DIV');
id.innerHTML = '<img class="cam_img" src="' + url.replace('%d',i) + '">';
id.setAttribute('cam',i);
id.className = "cam_DIV";
if (i == 1) { id.className += " first_CAM"; }
zam.appendChild(id);
this.add_camera(i,url.replace('%d',i) );
}
},
add_camera:function(i,url)
{
cam_tool.cam_count++;
var zam = document.getElementById("za_images");
var id = document.createElement('DIV');
id.innerHTML = '<img class="cam_img" src="' + url + '" current="y" cam="' + i + '" > <img class="cam_img2" cam="' + i + '" src="" current="n" >';
id.setAttribute('cam',i);
id.className = "cam_DIV";
zam.appendChild(id);
cam_tool.calc_spacing();
}
}
@ -141,6 +325,7 @@ const cam_tool = {
window.onload = function()
{
cam_tool.compose(5,'/z%d/cgi-bin/jpeg.cgi?channel=0');
cam_tool.add_camera(6,'/z7/cgi-bin/get_jpeg.cgi?base=0');
cam_tool.init();
};
</script>
@ -148,9 +333,14 @@ window.onload = function()
<body>
<div class="za_images" id="za_images" ></div>
<div class="cam_control" >
<button onclick="cam_tool.start();">Live</button>
<button onclick="cam_tool.pause();">Pause</button>
<input type="number" onclick="cam_tool.update_freq(event);" min="50" value="500" />
<button action="start">Live</button>
<button action="pause">Pause</button>
<div id="load_average">0</div>
<div id="load_modes">
<div class="load_mode" mode="auto" >Auto</div>
<div class="load_mode" mode="manual" >Manual:
<input type="number" onchange="cam_tool.update_freq(event);" min="50" value="5000" />
</div>
</div>
<video width="1" height="1" loop style="position:absolute;top:0;right:0" >