Visigothic Kings

master
Moe 2024-10-06 00:15:22 +00:00
parent 887acf80d3
commit 8dc03de9ea
17 changed files with 3213 additions and 4925 deletions

44
INSTALL/cuda-12.sh Normal file
View File

@ -0,0 +1,44 @@
#!/bin/sh
echo "------------------------------------------"
echo "-- Installing CUDA Toolkit and CUDA DNN --"
echo "------------------------------------------"
# Install CUDA Drivers and Toolkit
echo "Install CUDA Drivers and Toolkit?"
echo "(y)es or (N)o"
read installTheStuffHomie
if [ "$installTheStuffHomie" = "y" ] || [ "$installTheStuffHomie" = "Y" ]; then
if [ -x "$(command -v apt)" ]; then
# CUDA Toolkit
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-ubuntu2204.pin
sudo mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin-600
echo "Downloading CUDA Toolkit..."
wget https://developer.download.nvidia.com/compute/cuda/12.0.0/local_installers/cuda-repo-ubuntu2204-12-0-local_12.0.0-525.60.13-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu2204-12-0-local_12.0.0-525.60.13-1_amd64.deb
sudo cp /var/cuda-repo-ubuntu2204-12-0-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-0
# Driver
echo "Installing nvidia-driver-515-server"
sudo apt install nvidia-driver-515-server -y
# Install CUDA DNN
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/libcudnn8_8.8.1.3-1+cuda12.0_amd64.deb -O cuda-dnn.deb --no-verbose
sudo dpkg -i cuda-dnn.deb
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/libcudnn8-dev_8.8.1.3-1+cuda12.0_amd64.deb -O cuda-dnn-dev.deb --no-verbose
sudo dpkg -i cuda-dnn-dev.deb
echo "-- Cleaning Up --"
# Cleanup
sudo rm cuda.deb
sudo rm cuda-dnn.deb
sudo rm cuda-dnn-dev.deb
fi
echo "------------------------------"
echo "Reboot is required. Do it now?"
echo "------------------------------"
echo "(y)es or (N)o. Default is No."
read rebootTheMachineHomie
if [ "$rebootTheMachineHomie" = "y" ] || [ "$rebootTheMachineHomie" = "Y" ]; then
sudo reboot
fi
fi

View File

@ -89,6 +89,8 @@ require('./libs/ffmpeg.js')(s,config,lang, async () => {
require('./libs/onvifDeviceManager.js')(s,config,lang,app,io) require('./libs/onvifDeviceManager.js')(s,config,lang,app,io)
//alternate logins //alternate logins
require('./libs/auth/logins.js')(s,config,lang,app) require('./libs/auth/logins.js')(s,config,lang,app)
//rally other Shinobi
require('./libs/rally.js')(s,config,lang,app,io)
//on-start actions, daemon(s) starter //on-start actions, daemon(s) starter
await require('./libs/startup.js')(s,config,lang) await require('./libs/startup.js')(s,config,lang)
//p2p, commander //p2p, commander

View File

@ -7904,6 +7904,11 @@ module.exports = function(s,config,lang){
pageOpen: 'onvifDeviceManager', pageOpen: 'onvifDeviceManager',
eval: `!$user.details.sub || $user.details.monitor_create !== 0`, eval: `!$user.details.sub || $user.details.monitor_create !== 0`,
}, },
{
icon: 'ravelry',
label: `${lang['Rally']}`,
pageOpen: 'rally',
},
{ {
icon: 'eyedropper', icon: 'eyedropper',
label: `${lang['FFprobe']}`, label: `${lang['FFprobe']}`,
@ -9221,5 +9226,88 @@ module.exports = function(s,config,lang){
}, },
} }
}, },
"Rally": {
"section": "Rally",
"name": "Rally",
"blocks": {
"Search Settings": {
"id": "rallyConfigure",
"name": lang.Scan,
"blockquote": lang.RallyDescription,
"color": "green",
"section-pre-class": "col-md-4",
isFormGroupGroup: true,
"info": [
{
"name": "host",
"field": lang["Host"],
"placeholder": "http://shinobi_host:8080",
},
{
"name": "groupKey",
"field": lang["Group Key"],
},
{
"name": "apiKey",
"field": lang["API Key"],
"description": lang.rallyApiKeyFieldText,
},
{
"name": "channel",
"field": lang["Channel"],
"description": lang.rallyChannelDescription,
},
{
"fieldType": "btn-group",
"btns": [
{
forForm: true,
"fieldType": "btn",
"class": `btn-success fill mb-3`,
"icon": `search`,
"attribute": `type="submit"`,
"btnContent": `${lang['Scan']}`,
}
]
}
]
},
"Management": {
"id": "rallyManagement",
"noHeader": true,
"color": "blue",
"section-pre-class": "col-md-8",
"info": [
{
"id":"rallyServerInfo",
"fieldType": "div",
"class": "mb-3",
},
{
"fieldType": "btn-group",
"class": "mb-3",
"btns": [
{
"fieldType": "btn",
"class": `btn-success add-all`,
"btnContent": `<i class="fa fa-plus"></i> ${lang['Add All (Rallied)']}`,
},
// {
// "fieldType": "btn",
// "class": `btn-success add-all-direct`,
// "btnContent": `<i class="fa fa-plus"></i> ${lang['Add All (Direct)']}`,
// },
],
},
{
"id":"rallyCameras",
"fieldType": "table",
"attribute": `data-classes="table table-striped"`,
"divContent": ""
}
]
},
}
}
}) })
} }

View File

@ -18,6 +18,12 @@
"accountEditError": "Account Edit Error", "accountEditError": "Account Edit Error",
"Monitor Map": "Monitor Map", "Monitor Map": "Monitor Map",
"Geolocation": "Geolocation", "Geolocation": "Geolocation",
"Configure": "Configure",
"Scan": "Scan",
"Rally": "Rally",
"rallyApiKeyFieldText": "Permissions required : Get Monitors, Edit Monitors, View Streams, View Videos.",
"rallyDescription": "Here you can connect to a separate Shinobi server and add their cameras to this Shinobi server. The connection would be directly from the separate server instead of directly from the cameras.",
"rallyChannelDescription": "Channel to rally from. Default is 0 (Main).",
"Tested on": "Tested on", "Tested on": "Tested on",
"Architecture": "Architecture", "Architecture": "Architecture",
"Operating Systems": "Operating Systems", "Operating Systems": "Operating Systems",
@ -354,6 +360,7 @@
"File Type": "File Type", "File Type": "File Type",
"Filesize": "Filesize", "Filesize": "Filesize",
"Created": "Created", "Created": "Created",
"Status": "Status",
"Size": "Size", "Size": "Size",
"Video Status": "Video Status", "Video Status": "Video Status",
"Custom Auto Load": "Custom Auto Load", "Custom Auto Load": "Custom Auto Load",
@ -770,6 +777,9 @@
"Install": "Install", "Install": "Install",
"Disable": "Disable", "Disable": "Disable",
"Add All": "Add All", "Add All": "Add All",
"Add All (Rallied)": "Add All (Rallied)",
"Add All (Direct)": "Add All (Direct)",
"AddAllRalliedText": "Do you want to rally and add these Monitors from the specified Shinobi server?",
"Name": "Name", "Name": "Name",
"Skip Ping": "Skip Ping", "Skip Ping": "Skip Ping",
"Retry Connection": "Retry Connection <small>Number of times allowed to fail</small>", "Retry Connection": "Retry Connection <small>Number of times allowed to fail</small>",
@ -1322,6 +1332,7 @@
"Male": "Male", "Male": "Male",
"Female": "Female", "Female": "Female",
"Channel": "Channel", "Channel": "Channel",
"Channels Available": "Channels Available",
"Stream Key": "Stream Key", "Stream Key": "Stream Key",
"Server URL": "Server URL", "Server URL": "Server URL",
"Video Bit Rate": "Video Bit Rate", "Video Bit Rate": "Video Bit Rate",

View File

@ -164,6 +164,7 @@ module.exports = function(s,config,lang,app,io){
}) })
} }
var createDropInEventsDirectory = function(){ var createDropInEventsDirectory = function(){
try{
if(!config.dropInEventsDir){ if(!config.dropInEventsDir){
config.dropInEventsDir = s.dir.streams + 'dropInEvents/' config.dropInEventsDir = s.dir.streams + 'dropInEvents/'
} }
@ -172,6 +173,9 @@ module.exports = function(s,config,lang,app,io){
if(!fs.existsSync(s.dir.dropInEvents)){ if(!fs.existsSync(s.dir.dropInEvents)){
fs.mkdirSync(s.dir.dropInEvents) fs.mkdirSync(s.dir.dropInEvents)
} }
}catch(err){
console.error(err)
}
} }
var getDropInEventDir = function(monitorConfig){ var getDropInEventDir = function(monitorConfig){
var ke = monitorConfig.ke var ke = monitorConfig.ke
@ -209,15 +213,17 @@ module.exports = function(s,config,lang,app,io){
createDropInEventDirectory(monitorConfig,function(err,monitorEventDropDir){}) createDropInEventDirectory(monitorConfig,function(err,monitorEventDropDir){})
} }
// FTP Server // FTP Server
if(config.ftpServer === true){
createDropInEventsDirectory() createDropInEventsDirectory()
if(config.ftpServer === true){
try{
const FtpSrv = require('ftp-srv')
console.error('WARNING : FTP Server is enabled.')
if(!config.ftpServerPort)config.ftpServerPort = 21 if(!config.ftpServerPort)config.ftpServerPort = 21
if(!config.ftpServerUrl)config.ftpServerUrl = `ftp://0.0.0.0:${config.ftpServerPort}` if(!config.ftpServerUrl)config.ftpServerUrl = `ftp://0.0.0.0:${config.ftpServerPort}`
if(!config.ftpServerPasvUrl)config.ftpServerPasvUrl = config.ftpServerUrl.replace(/.*:\/\//, '').replace(/:.*/, ''); if(!config.ftpServerPasvUrl)config.ftpServerPasvUrl = config.ftpServerUrl.replace(/.*:\/\//, '').replace(/:.*/, '');
if(!config.ftpServerPasvMinPort)config.ftpServerPasvMinPort = 10050; if(!config.ftpServerPasvMinPort)config.ftpServerPasvMinPort = 10050;
if(!config.ftpServerPasvMaxPort)config.ftpServerPasvMaxPort = 10100; if(!config.ftpServerPasvMaxPort)config.ftpServerPasvMaxPort = 10100;
config.ftpServerUrl = config.ftpServerUrl.replace('{{PORT}}',config.ftpServerPort) config.ftpServerUrl = config.ftpServerUrl.replace('{{PORT}}',config.ftpServerPort)
const FtpSrv = require('ftp-srv')
const ftpServer = new FtpSrv({ const ftpServer = new FtpSrv({
url: config.ftpServerUrl, url: config.ftpServerUrl,
@ -261,6 +267,11 @@ module.exports = function(s,config,lang,app,io){
}).catch(function(err){ }).catch(function(err){
s.systemLog(err) s.systemLog(err)
}) })
}catch(err){
console.error(err.message)
console.error('Could not start FTP Server, please run "npm install ftp-srv" inside the Shinobi folder.')
console.error('The ftp-srv Module is known to have possible vulnerabilities. Due to the nature of the vulnerability you should be unaffected unless the FTP Port is public facing. Use at your own risk.')
}
} }
//add extensions //add extensions
s.onMonitorInit(onMonitorInit) s.onMonitorInit(onMonitorInit)

View File

@ -265,7 +265,7 @@ Run "npm install ffbinaries" to get a different static FFmpeg downloader.`
console.log('ffbinaries : Downloading FFmpeg. Please Wait...'); console.log('ffbinaries : Downloading FFmpeg. Please Wait...');
ffbinaries.downloadBinaries(['ffmpeg', 'ffprobe'], { ffbinaries.downloadBinaries(['ffmpeg', 'ffprobe'], {
destination: ffbinaryDir, destination: ffbinaryDir,
version : '4.2' version : config.ffbinariesVersion || '4.2'
},function () { },function () {
config.ffmpegDir = ffbinaryDir + 'ffmpeg' config.ffmpegDir = ffbinaryDir + 'ffmpeg'
response.msg = 'ffbinaries : FFmpeg Downloaded.' response.msg = 'ffbinaries : FFmpeg Downloaded.'

View File

@ -116,7 +116,6 @@ module.exports = function(s,config,lang,getSnapshot){
detector_mail_timeout = parseFloat(monitorConfig.details.detector_mail_timeout) * 1000 * 60; detector_mail_timeout = parseFloat(monitorConfig.details.detector_mail_timeout) * 1000 * 60;
} }
s.group[d.ke].activeMonitors[d.id].detector_mail = setTimeout(function(){ s.group[d.ke].activeMonitors[d.id].detector_mail = setTimeout(function(){
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_mail);
s.group[d.ke].activeMonitors[d.id].detector_mail = null s.group[d.ke].activeMonitors[d.id].detector_mail = null
},detector_mail_timeout); },detector_mail_timeout);
const sendMail = function(files){ const sendMail = function(files){

View File

@ -20,8 +20,10 @@ module.exports = function(s,config,lang,getSnapshot){
//telegram bot //telegram bot
if(config.telegramBot === true){ if(config.telegramBot === true){
const TelegramBot = require('node-telegram-bot-api');
try{ try{
const TelegramBot = require('node-telegram-bot-api');
console.error('WARNING : Telegram bot is enabled.')
const sendMessage = async function(sendBody,attachments,groupKey){ const sendMessage = async function(sendBody,attachments,groupKey){
var bot = s.group[groupKey].telegramBot var bot = s.group[groupKey].telegramBot
if(!bot){ if(!bot){
@ -363,8 +365,9 @@ module.exports = function(s,config,lang,getSnapshot){
] ]
}) })
}catch(err){ }catch(err){
console.error(err) console.error(err.message)
console.log('Could not start Telegram bot, please run "npm install node-telegram-bot-api" inside the Shinobi folder.') console.error('Could not start Telegram bot, please run "npm install node-telegram-bot-api" inside the Shinobi folder.')
console.error('The Telegram Module is known to have possible vulnerabilities. Use at your own risk.')
} }
} }
} }

View File

@ -370,10 +370,14 @@ module.exports = function(s,config,lang,app,io){
function sendCopyOfAllMonitorConfigs(){ function sendCopyOfAllMonitorConfigs(){
const groupKeys = Object.keys(s.group); const groupKeys = Object.keys(s.group);
for(groupKey of groupKeys){ for(groupKey of groupKeys){
try{
const monitorConfigs = Object.values(s.group[groupKey].rawMonitorConfigurations); const monitorConfigs = Object.values(s.group[groupKey].rawMonitorConfigurations);
for(monitorConfig of monitorConfigs){ for(monitorConfig of monitorConfigs){
onMonitorUpdate(monitorConfig) onMonitorUpdate(monitorConfig)
} }
}catch(err){
s.debugLog(err)
}
} }
} }
/** /**

46
libs/rally.js Normal file
View File

@ -0,0 +1,46 @@
const {
ShinobiAPI,
formatDateTime,
getCameraTemplate,
cleanStringForMonitorId,
} = require('node-shinobi');
module.exports = (s,config,lang,app,io) => {
function getServerInfo(){}
async function getMonitors({ host, groupKey, apiKey }){
const shinobi = new ShinobiAPI(host, apiKey, groupKey);
const monitors = await shinobi.getMonitor();
return monitors
}
/**
* API : Get Monitors
*/
app.post(config.webPaths.apiPrefix+':auth/rally/:ke/getMonitors', function (req,res){
s.auth(req.params, async function(user){
const groupKey = req.params.ke
const asis = s.getPostData(req,'asis') === '1'
const connectionInfo = s.getPostData(req,'connectionInfo',true) || {}
const {
isRestricted,
isRestrictedApiKey,
apiKeyPermissions,
userPermissions,
} = s.checkPermission(user)
if(
isRestrictedApiKey && apiKeyPermissions.get_monitors_disallowed
){
s.closeJsonResponse(res,[]);
return
}
if(!connectionInfo.host || !connectionInfo.groupKey || !connectionInfo.apiKey){
s.closeJsonResponse(res,{ok: false, msg: lang['No Data']});
return
}
const monitors = await getMonitors(connectionInfo) || [];
s.closeJsonResponse(res, monitors);
},res,req);
});
// page structure
config.webBlocksPreloaded.push(`home/rally`);
}

View File

@ -1,6 +1,6 @@
const fs = require('fs'); const fs = require('fs');
const { Readable } = require('stream'); const { Readable } = require('stream');
const B2 = require('backblaze-b2') const B2 = require('shinobi-backblaze-b2')
module.exports = function(s,config,lang){ module.exports = function(s,config,lang){
//Backblaze B2 //Backblaze B2
var serviceProvider = 'b2' var serviceProvider = 'b2'

View File

@ -10,6 +10,7 @@ module.exports = function(s,config,lang){
await s.group[groupKey].whcs.send(requestOptions); await s.group[groupKey].whcs.send(requestOptions);
} catch (err) { } catch (err) {
console.error('AMZ genericRequest',groupKey,requestOptions) console.error('AMZ genericRequest',groupKey,requestOptions)
console.error('AMZ genericRequest ERR',err)
response.ok = false response.ok = false
response.err = err response.err = err
} }

View File

@ -1,6 +1,9 @@
var fs = require('fs'); var fs = require('fs');
var webdav = require("webdav-fs"); module.exports = async function(s,config,lang){
module.exports = function(s,config,lang){ if(config.webDavUpload){
try{
const webdav = await import("webdav-fs");
console.error('WARNING : WebDAV is enabled.')
// WebDAV // WebDAV
var beforeAccountSaveForWebDav = function(d){ var beforeAccountSaveForWebDav = function(d){
//d = save event //d = save event
@ -353,4 +356,17 @@ module.exports = function(s,config,lang){
}, },
] ]
} }
}catch(err){
console.error(err.message)
console.error('Could not start WebDAV Uploader, please run "npm install webdav-fs" inside the Shinobi folder.')
console.error('The webdav-fs Module is known to have possible vulnerabilities. Due to the nature of the vulnerability you should be unaffected unless Shinobi is public facing. Use at your own risk.')
}
}else{
try{
const webdav = await import("webdav-fs");
console.error('!! Looks like you have webdav-fs installed but its not enabled. Due to a vulnerability with it you now need to add "webDavUpload": true to your conf.json to enable it.')
}catch(err){
}
}
} }

6971
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,6 @@
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.226.0", "@aws-sdk/client-s3": "^3.226.0",
"async": "^3.2.2", "async": "^3.2.2",
"backblaze-b2": "^1.7.0",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"bson": "^4.6.1", "bson": "^4.6.1",
"connection-tester": "^0.2.0", "connection-tester": "^0.2.0",
@ -29,7 +28,6 @@
"express-fileupload": "^1.4.0", "express-fileupload": "^1.4.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"fs-extra": "9.0.1", "fs-extra": "9.0.1",
"ftp-srv": "^4.6.2",
"googleapis": "^100.0.0", "googleapis": "^100.0.0",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
"jsonfile": "^3.0.1", "jsonfile": "^3.0.1",
@ -43,8 +41,8 @@
"mysql2": "^3.9.7", "mysql2": "^3.9.7",
"node-abort-controller": "^3.0.1", "node-abort-controller": "^3.0.1",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"node-shinobi": "^1.0.4",
"node-ssh": "^12.0.4", "node-ssh": "^12.0.4",
"node-telegram-bot-api": "^0.65.1",
"nodemailer": "^6.7.1", "nodemailer": "^6.7.1",
"onvif": "^0.7.1", "onvif": "^0.7.1",
"pam-diff": "^1.1.0", "pam-diff": "^1.1.0",
@ -53,7 +51,8 @@
"pixel-change": "^1.1.0", "pixel-change": "^1.1.0",
"pushover-notifications": "^1.2.2", "pushover-notifications": "^1.2.2",
"sat": "^0.7.1", "sat": "^0.7.1",
"shinobi-node-moving-things-tracker": "^0.9.3", "shinobi-backblaze-b2": "^1.7.1",
"shinobi-node-moving-things-tracker": "^0.9.5",
"shinobi-onvif": "0.2.2", "shinobi-onvif": "0.2.2",
"shinobi-sound-detection": "^0.1.13", "shinobi-sound-detection": "^0.1.13",
"shinobi-zwave": "^1.0.11", "shinobi-zwave": "^1.0.11",
@ -61,8 +60,7 @@
"socket.io": "^4.4.1", "socket.io": "^4.4.1",
"socket.io-client": "^4.5.3", "socket.io-client": "^4.5.3",
"tree-kill": "1.2.2", "tree-kill": "1.2.2",
"unzipper": "0.10.11", "unzipper": "0.10.11"
"webdav-fs": "^4.0.1"
}, },
"bin": "camera.js", "bin": "camera.js",
"scripts": { "scripts": {

127
web/assets/js/bs5.rally.js Normal file
View File

@ -0,0 +1,127 @@
$(document).ready(function(e){
//api window
var foundMonitors = {}
var theWindow = $('#tab-rally')
var theWindowForm = $('#rallyForm')
var tableEl = $('#rallyCameras')
function getMonitors(connectionInfo, asis){
return new Promise((resolve) => {
$.post(`${getApiPrefix('rally')}/getMonitors`,{
connectionInfo,
},function(data){
resolve(data)
})
})
}
function saveMonitor(monitor){
return new Promise((resolve) => {
$.post(`${getApiPrefix('configureMonitor')}/${monitor.mid}`,{
data: JSON.stringify(monitor)
},function(data){
resolve(data)
})
})
}
async function saveMonitors(monitors){
for(monitor of monitors){
await saveMonitor(monitor)
}
}
function channelsAvailable(monitor){
const streams = monitor.streams;
return Object.keys(streams).join(', ')
}
function findRallyTypeFromStreamPath(streamPath) {
let chosenType = null;
if(streamPath.endsWith('m3u8')){
chosenType = 'hls'
}else if(streamPath.endsWith('mp4')){
chosenType = 'mp4'
}
return chosenType;
}
function drawTable(monitors){
var html = ''
foundMonitors = {}
for(monitor of monitors){
var monitorKey = `${monitor.ke}${monitor.mid}`
foundMonitors[monitorKey] = monitor;
html += `
<tr monitor="${monitorKey}">
<td title="${lang['Monitor Name']}">${monitor.name}</td>
<td title="${lang['Monitor ID']}">${monitor.mid}</td>
<td title="${lang.Host}">${monitor.host}</td>
<td title="${lang.Status}">${monitorStatusCodes[monitor.code]}</td>
<td title="${lang['Channels Available']}">${channelsAvailable(monitor)}</td>
<td><a class="add-monitor btn btn-sm btn-success" href="#">${lang.Add}</a></td>
</tr>`
}
tableEl.html(html);
}
function convertToRalliedMonitor(monitor, connectionInfo){
const { host, groupKey, apiKey, channel } = connectionInfo;
let [ hostname, port ] = host.split('://')[1].split(':');
const protocol = host.startsWith('https') ? 'https' : 'http';
hostname = hostname.endsWith('/') ? hostname.substring(0, str.length - 1) : hostname;
const streamPath = monitor.streams[parseInt(channel) || 0];
const rallyType = findRallyTypeFromStreamPath(streamPath);
if(hostname && rallyType){
const autoHostUrl = `${protocol}://${hostname}:${port}${streamPath}`;
monitor.protocol = protocol;
monitor.host = hostname;
monitor.port = port;
monitor.path = streamPath;
monitor.type = rallyType;
monitor.details.auto_host = autoHostUrl;
return monitor
}
return null;
}
function convertToRalliedMonitors(monitors, connectionInfo){
const ralliedMonitors = []
monitors.forEach(monitor => {
var converted = convertToRalliedMonitor(monitor, connectionInfo)
if(converted)ralliedMonitors.push(converted)
});
return ralliedMonitors;
}
function getConnectionInfo(){
const connectionInfo = theWindowForm.serializeObject();
return connectionInfo
}
theWindowForm.submit(async function(e){
e.preventDefault();
const connectionInfo = getConnectionInfo()
const monitors = await getMonitors(connectionInfo)
drawTable(monitors)
return false;
})
theWindow.on('click','.add-monitor',function(e){
e.preventDefault();
const connectionInfo = getConnectionInfo()
const el = $(this).parents('[monitor]')
const monitorKey = el.attr('monitor')
const monitor = foundMonitors[monitorKey]
const convertedMonitor = convertToRalliedMonitor(monitor, connectionInfo)
saveMonitor(convertedMonitor)
})
theWindow.on('click','.add-all',function(e){
const connectionInfo = getConnectionInfo()
const monitors = convertToRalliedMonitors(foundMonitors, connectionInfo)
$.confirm.create({
title: lang['Add All (Rallied)'],
body: lang.AddAllRalliedText,
clickOptions: {
class: 'btn-success',
title: `<i class="fa fa-check"></i> ${lang.Save}`,
},
clickCallback: function(){
saveMonitors(monitors)
}
})
})
// addOnTabOpen('rally', function () {
// })
// addOnTabReopen('rally', function () {
// })
})

View File

@ -0,0 +1,11 @@
<main class="container page-tab dark pt-3" id="tab-rally">
<form id="rallyForm" class="row">
<% Object.keys(define['Rally'].blocks).forEach(function(blockKey) { -%>
<%- include('drawBlock', {
theBlock: define['Rally'].blocks[blockKey],
details: $user.details
}) %>
<% }) -%>
</form>
<script src="<%-window.libURL%>assets/js/bs5.rally.js"></script>
</main>