Shinobi/libs/user.js

469 lines
21 KiB
JavaScript

var fs = require('fs');
var events = require('events');
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var async = require("async");
module.exports = function(s,config,lang){
const {
deleteSetOfVideos,
deleteSetOfTimelapseFrames,
deleteSetOfFileBinFiles,
deleteAddStorageVideos,
deleteMainVideos,
deleteTimelapseFrames,
deleteFileBinFiles,
deleteCloudVideos,
deleteCloudTimelapseFrames,
resetAllStorageCounters,
} = require("./user/utils.js")(s,config,lang);
let purgeDiskGroup = () => {}
const runQuery = async.queue(function(groupKey, callback) {
purgeDiskGroup(groupKey,callback)
}, 1);
if(config.cron.deleteOverMax === true){
purgeDiskGroup = (groupKey,callback) => {
if(s.group[groupKey]){
if(s.group[groupKey].sizePurging !== true){
s.group[groupKey].sizePurging = true
s.debugLog(`${groupKey} deleteMainVideos`)
deleteMainVideos(groupKey,() => {
s.debugLog(`${groupKey} deleteTimelapseFrames`)
deleteTimelapseFrames(groupKey,() => {
s.debugLog(`${groupKey} deleteFileBinFiles`)
deleteFileBinFiles(groupKey,() => {
s.debugLog(`${groupKey} deleteAddStorageVideos`)
deleteAddStorageVideos(groupKey,() => {
s.group[groupKey].sizePurging = false
s.sendDiskUsedAmountToClients(groupKey)
callback();
})
})
})
})
}else{
s.sendDiskUsedAmountToClients(groupKey)
}
}
}
}
s.purgeDiskForGroup = (groupKey) => {
return runQuery.push(groupKey,function(){
//...
})
}
s.setDiskUsedForGroup = function(groupKey,bytes,storagePoint){
//`bytes` will be used as the value to add or substract
if(s.group[groupKey] && s.group[groupKey].diskUsedEmitter){
s.group[groupKey].diskUsedEmitter.emit('set',bytes,storagePoint)
}
}
s.setDiskUsedForGroupAddStorage = function(groupKey,data,storagePoint){
if(s.group[groupKey] && s.group[groupKey].diskUsedEmitter){
s.group[groupKey].diskUsedEmitter.emit('setAddStorage',data,storagePoint)
}
}
s.purgeCloudDiskForGroup = function(e,storageType,storagePoint){
if(s.group[e.ke].diskUsedEmitter){
s.group[e.ke].diskUsedEmitter.emit('purgeCloud',storageType,storagePoint)
}
}
s.setCloudDiskUsedForGroup = function(groupKey,usage,storagePoint){
//`usage` will be used as the value to add or substract
if(s.group[groupKey].diskUsedEmitter){
s.group[groupKey].diskUsedEmitter.emit('setCloud',usage,storagePoint)
}
}
s.sendDiskUsedAmountToClients = function(groupKey){
//send the amount used disk space to connected users
if(s.group[groupKey]&&s.group[groupKey].init){
s.tx({
f: 'diskUsed',
size: s.group[groupKey].usedSpace,
usedSpace: s.group[groupKey].usedSpace,
usedSpaceVideos: s.group[groupKey].usedSpaceVideos,
usedSpaceFilebin: s.group[groupKey].usedSpaceFilebin,
usedSpaceTimelapseFrames: s.group[groupKey].usedSpaceTimelapseFrames,
limit: s.group[groupKey].sizeLimit,
addStorage: s.group[groupKey].addStorageUse
},'GRP_'+groupKey);
}
}
//user log
s.userLog = function(e,x){
if(e.id && !e.mid)e.mid = e.id
if(!x||!e.mid){return}
if(
(e.details && e.details.sqllog === '1') ||
e.mid.indexOf('$') > -1
){
s.knexQuery({
action: "insert",
table: "Logs",
insert: {
ke: e.ke,
mid: e.mid,
info: s.s(x),
}
})
}
const logEvent = {
f: 'log',
ke: e.ke,
mid: e.mid,
log: x,
time: s.timeObject()
}
s.tx(logEvent,'GRPLOG_'+e.ke);
s.onUserLogExtensions.forEach(function(extender){
extender(logEvent)
})
}
s.loadGroup = function(e){
s.loadGroupExtensions.forEach(function(extender){
extender(e)
})
if(!s.group[e.ke]){
s.group[e.ke]={}
}
if(!s.group[e.ke].init){
s.group[e.ke].init={}
}
if(!s.group[e.ke].addStorageUse){s.group[e.ke].addStorageUse={}};
if(!s.group[e.ke].fileBin){s.group[e.ke].fileBin={}};
if(!s.group[e.ke].users){s.group[e.ke].users={}}
if(!s.group[e.ke].dashcamUsers){s.group[e.ke].dashcamUsers={}}
if(!s.group[e.ke].sizePurgeQueue){s.group[e.ke].sizePurgeQueue=[]}
if(!s.group[e.ke].addStorageUse){s.group[e.ke].addStorageUse = {}}
if(!e.limit||e.limit===''){e.limit=10000}else{e.limit=parseFloat(e.limit)}
//save global space limit for group key (mb)
s.group[e.ke].sizeLimit = e.limit || s.group[e.ke].sizeLimit || 10000
s.group[e.ke].sizeLimitVideoPercent = parseFloat(s.group[e.ke].init.size_video_percent) || 90
s.group[e.ke].sizeLimitTimelapseFramesPercent = parseFloat(s.group[e.ke].init.size_timelapse_percent) || 5
s.group[e.ke].sizeLimitFileBinPercent = parseFloat(s.group[e.ke].init.size_filebin_percent) || 5
//save global used space as megabyte value
s.group[e.ke].usedSpace = s.group[e.ke].usedSpace || ((e.size || 0) / 1048576)
//emit the changes to connected users
s.sendDiskUsedAmountToClients(e.ke)
}
s.loadGroupApps = function(e){
// e = user
if(!s.group[e.ke].init){
s.group[e.ke].init={};
}
s.knexQuery({
action: "select",
columns: "*",
table: "Users",
where: [
['ke','=',e.ke],
['details','NOT LIKE',`%"sub"%`],
],
limit: 1
},(err,r) => {
if(r && r[0]){
r = r[0];
const details = JSON.parse(r.details);
//load extenders
s.loadGroupAppExtensions.forEach(function(extender){
extender(r,details)
})
//disk Used Emitter
if(!s.group[e.ke].diskUsedEmitter){
s.group[e.ke].diskUsedEmitter = new events.EventEmitter()
s.group[e.ke].diskUsedEmitter.on('setCloud',function(currentChange,storagePoint){
var amount = currentChange.amount
var storageType = currentChange.storageType
var cloudDisk = s.group[e.ke].cloudDiskUse[storageType]
//validate current values
if(!cloudDisk.usedSpace){
cloudDisk.usedSpace = 0
}else{
cloudDisk.usedSpace = parseFloat(cloudDisk.usedSpace)
}
if(cloudDisk.usedSpace < 0 || isNaN(cloudDisk.usedSpace)){
cloudDisk.usedSpace = 0
}
//change global size value
cloudDisk.usedSpace = cloudDisk.usedSpace + amount
switch(storagePoint){
case'timelapeFrames':
cloudDisk.usedSpaceTimelapseFrames += amount
break;
case'fileBin':
cloudDisk.usedSpaceFilebin += amount
break;
default:
cloudDisk.usedSpaceVideos += amount
break;
}
})
if(config.cron.deleteOverMax === true){
s.group[e.ke].diskUsedEmitter.on('purgeCloud',function(storageType,storagePoint){
deleteCloudVideos(e.ke,storageType,storagePoint,function(){
deleteCloudTimelapseFrames(e.ke,storageType,storagePoint,function(){
})
})
})
}
//s.setDiskUsedForGroup
s.group[e.ke].diskUsedEmitter.on('set',function(currentChange,storageType){
//validate current values
if(!s.group[e.ke].usedSpace){
s.group[e.ke].usedSpace=0
}else{
s.group[e.ke].usedSpace=parseFloat(s.group[e.ke].usedSpace)
}
if(s.group[e.ke].usedSpace<0||isNaN(s.group[e.ke].usedSpace)){
s.group[e.ke].usedSpace=0
}
//change global size value
s.group[e.ke].usedSpace += currentChange
s.group[e.ke].usedSpace = s.group[e.ke].usedSpace < 0 ? 0 : s.group[e.ke].usedSpace
switch(storageType){
case'timelapeFrames':
s.group[e.ke].usedSpaceTimelapseFrames += currentChange
s.group[e.ke].usedSpaceTimelapseFrames = s.group[e.ke].usedSpaceTimelapseFrames < 0 ? 0 : s.group[e.ke].usedSpaceTimelapseFrames
break;
case'fileBin':
s.group[e.ke].usedSpaceFilebin += currentChange
s.group[e.ke].usedSpaceFilebin = s.group[e.ke].usedSpaceFilebin < 0 ? 0 : s.group[e.ke].usedSpaceFilebin
break;
default:
s.group[e.ke].usedSpaceVideos += currentChange
s.group[e.ke].usedSpaceVideos = s.group[e.ke].usedSpaceVideos < 0 ? 0 : s.group[e.ke].usedSpaceVideos
break;
}
//remove value just used from queue
s.sendDiskUsedAmountToClients(e.ke)
})
s.group[e.ke].diskUsedEmitter.on('setAddStorage',function(data,storageType){
var currentSize = data.size
var storageIndex = data.storageIndex
//validate current values
if(!storageIndex.usedSpace){
storageIndex.usedSpace = 0
}else{
storageIndex.usedSpace = parseFloat(storageIndex.usedSpace)
}
if(storageIndex.usedSpace < 0 || isNaN(storageIndex.usedSpace)){
storageIndex.usedSpace = 0
}
//change global size value
storageIndex.usedSpace += currentSize
switch(storageType){
case'timelapeFrames':
storageIndex.usedSpaceTimelapseFrames += currentSize
break;
case'fileBin':
storageIndex.usedSpaceFilebin += currentSize
break;
default:
storageIndex.usedSpaceVideos += currentSize
break;
}
//remove value just used from queue
s.sendDiskUsedAmountToClients(e.ke)
})
}
Object.keys(details).forEach(function(v){
s.group[e.ke].init[v] = details[v]
})
}
})
}
function filterMonitorListOrder(groupKey,details){
const loadedMonitors = s.group[groupKey].rawMonitorConfigurations
var monitorListOrder = (details.monitorListOrder && details.monitorListOrder[0] ? details.monitorListOrder[0] : []).filter(monitorId => !!loadedMonitors[monitorId]);
monitorListOrder = [...new Set(monitorListOrder)];
return monitorListOrder
}
s.accountSettingsEdit = function(d,dontRunExtensions){
s.knexQuery({
action: "select",
columns: "details",
table: "Users",
where: [
['ke','=',d.ke],
['uid','=',d.uid],
]
},(err,r) => {
if(r && r[0]){
r = r[0];
const details = JSON.parse(r.details);
if(!details.sub || details.user_change !== "0"){
if(d.cnid){
if(details.get_server_log === '1'){
s.clientSocketConnection[d.cnid].join('GRPLOG_'+d.ke)
}else{
s.clientSocketConnection[d.cnid].leave('GRPLOG_'+d.ke)
}
}
///unchangeable from client side, so reset them in case they did.
var form = d.form
var formDetails = s.parseJSON(form.details,{})
if(!dontRunExtensions){
s.beforeAccountSaveExtensions.forEach(function(extender){
extender({
form: form,
formDetails: formDetails,
d: details
})
})
}
//admin permissions
formDetails.permissions = details.permissions
formDetails.max_camera = details.max_camera
formDetails.edit_size = details.edit_size
formDetails.edit_days = details.edit_days
formDetails.use_admin = details.use_admin
formDetails.use_ldap = details.use_ldap
formDetails.landing_page = details.landing_page
//check
if(details.edit_days == "0"){
formDetails.days = details.days;
}
if(details.edit_size == "0"){
formDetails.size = details.size;
formDetails.addStorage = details.addStorage;
}
if(details.sub){
formDetails.sub = details.sub;
if(details.monitors){formDetails.monitors = details.monitors;}
if(details.allmonitors){formDetails.allmonitors = details.allmonitors;}
if(details.monitor_create){formDetails.monitor_create = details.monitor_create;}
if(details.video_delete){formDetails.video_delete = details.video_delete;}
if(details.video_view){formDetails.video_view = details.video_view;}
if(details.monitor_edit){formDetails.monitor_edit = details.monitor_edit;}
if(details.size){formDetails.size = details.size;}
if(details.days){formDetails.days = details.days;}
}
var newSize = parseFloat(formDetails.size) || 10000
//load addStorageUse
var currentStorageNumber = 0
var readStorageArray = function(){
var storage = s.listOfStorage[currentStorageNumber]
if(!storage){
//done all checks, move on to next user
return
}
var path = storage.value
if(path === ''){
++currentStorageNumber
readStorageArray()
return
}
var detailContainer = formDetails || s.group[r.ke].init
var storageId = path
var detailsContainerAddStorage = s.parseJSON(detailContainer.addStorage)
if(!s.group[d.ke].addStorageUse[storageId])s.group[d.ke].addStorageUse[storageId] = {}
var storageIndex = s.group[d.ke].addStorageUse[storageId]
storageIndex.name = storage.name
storageIndex.path = path
storageIndex.usedSpace = storageIndex.usedSpace || 0
if(detailsContainerAddStorage && detailsContainerAddStorage[path] && detailsContainerAddStorage[path].limit){
storageIndex.sizeLimit = parseFloat(detailsContainerAddStorage[path].limit)
}else{
storageIndex.sizeLimit = newSize
}
}
readStorageArray()
///
formDetails = s.mergeDeep(details,formDetails)
if(formDetails.monitorListOrder)formDetails.monitorListOrder[0] = filterMonitorListOrder(d.ke,formDetails);
formDetailsString = JSON.stringify(s.mergeDeep(details,formDetails))
///
const updateQuery = {}
if(form.pass && form.pass !== ''){
form.pass = s.createHash(form.pass)
}else{
delete(form.pass)
}
delete(form.password_again)
Object.keys(form).forEach(function(key){
const value = form[key]
updateQuery[key] = value
})
updateQuery.details = formDetailsString
s.knexQuery({
action: "update",
table: "Users",
update: updateQuery,
where: [
['ke','=',d.ke],
['uid','=',d.uid],
]
},() => {
const user = Object.assign({ke : d.ke},form)
if(!details.sub){
s.group[d.ke].sizeLimit = parseFloat(newSize)
resetAllStorageCounters(d.ke)
if(!dontRunExtensions){
s.unloadGroupAppExtensions.forEach(function(extender){
extender(user)
})
s.loadGroupApps(d)
}
}
s.onAccountSaveExtensions.forEach(function(extender){
extender(s.group[d.ke],formDetails,user)
})
if(d.cnid)s.tx({f:'user_settings_change',uid:d.uid,ke:d.ke,form:form},d.cnid)
})
}
}
})
}
s.findPreset = function(presetQueryVals,callback){
//presetQueryVals = [ke, type, name]
s.knexQuery({
action: "select",
columns: "*",
table: "Presets",
where: [
['ke','=',presetQueryVals[0]],
['type','=',presetQueryVals[1]],
['name','=',presetQueryVals[2]],
],
limit: 1
},function(err,presets) {
var preset
var notFound = false
if(presets && presets[0]){
preset = presets[0]
s.checkDetails(preset)
}else{
notFound = true
}
callback(notFound,preset)
})
}
s.checkUserPurgeLock = function(groupKey){
var userGroup = s.group[groupKey]
if(s.group[groupKey].usedSpace > s.group[groupKey].sizeLimit){
s.group[groupKey].sizePurgeQueue = []
s.group[groupKey].sizePurging = false
s.systemLog(lang.sizePurgeLockedText + ' : ' + groupKey)
s.onStalePurgeLockExtensions.forEach(function(extender){
extender(groupKey,s.group[groupKey].usedSpace,s.group[groupKey].sizeLimit)
})
}
}
if(config.cron.deleteOverMax === true){
s.checkForStalePurgeLocks = function(){
var doCheck = function(){
Object.keys(s.group).forEach(function(groupKey){
s.checkUserPurgeLock(groupKey)
})
}
clearTimeout(s.checkForStalePurgeLocksInterval)
s.checkForStalePurgeLocksInterval = setInterval(function(){
doCheck()
},1000 * 60 * 60)
doCheck()
}
}else{
s.checkForStalePurgeLocks = function(){}
}
}