2017-08-28 12:27:45 -04:00
/ * *
2018-01-04 15:15:21 -05:00
* @ description MeshCentral database module
2017-08-28 12:27:45 -04:00
* @ author Ylian Saint - Hilaire
2019-01-03 19:22:15 -05:00
* @ copyright Intel Corporation 2018 - 2019
2018-01-04 15:15:21 -05:00
* @ license Apache - 2.0
2017-08-28 12:27:45 -04:00
* @ version v0 . 0.2
* /
2018-08-29 20:40:30 -04:00
/*xjslint node: true */
/*xjslint plusplus: true */
/*xjslint maxlen: 256 */
/*jshint node: true */
/*jshint strict: false */
/*jshint esversion: 6 */
"use strict" ;
2018-08-27 15:24:15 -04:00
2017-08-28 12:27:45 -04:00
//
// Construct Meshcentral database object
//
// The default database is NeDB
// https://github.com/louischatriot/nedb
//
// Alternativety, MongoDB can be used
// https://www.mongodb.com/
// Just run with --mongodb [connectionstring], where the connection string is documented here: https://docs.mongodb.com/manual/reference/connection-string/
// The default collection is "meshcentral", but you can override it using --mongodbcol [collection]
//
2018-07-13 22:18:43 -04:00
module . exports . CreateDB = function ( parent ) {
2017-08-28 12:27:45 -04:00
var obj = { } ;
2018-08-29 20:40:30 -04:00
var Datastore = null ;
2019-02-18 17:32:55 -05:00
var expireEventsSeconds = ( 60 * 60 * 24 * 20 ) ; // By default, expire events after 20 days. (Seconds * Minutes * Hours * Days)
var expirePowerEventsSeconds = ( 60 * 60 * 24 * 10 ) ; // By default, expire power events after 10 days. (Seconds * Minutes * Hours * Days)
2017-08-28 12:27:45 -04:00
obj . path = require ( 'path' ) ;
2018-07-13 22:18:43 -04:00
obj . parent = parent ;
2017-09-13 14:25:57 -04:00
obj . identifier = null ;
2019-01-24 15:08:48 -05:00
obj . dbKey = null ;
2017-09-13 14:25:57 -04:00
2019-02-19 01:20:25 -05:00
// Read expiration time from configuration file
if ( typeof obj . parent . args . dbexpire == 'object' ) {
if ( typeof obj . parent . args . dbexpire . events == 'number' ) { expireEventsSeconds = obj . parent . args . dbexpire . events ; }
if ( typeof obj . parent . args . dbexpire . powerevents == 'number' ) { expirePowerEventsSeconds = obj . parent . args . dbexpire . powerevents ; }
}
2018-07-13 22:18:43 -04:00
if ( obj . parent . args . mongodb ) {
2017-08-28 12:27:45 -04:00
// Use MongoDB
obj . databaseType = 2 ;
2018-08-29 20:40:30 -04:00
Datastore = require ( 'mongojs' ) ;
2018-07-13 22:18:43 -04:00
var db = Datastore ( obj . parent . args . mongodb ) ;
2017-08-28 12:27:45 -04:00
var dbcollection = 'meshcentral' ;
2018-07-13 22:18:43 -04:00
if ( obj . parent . args . mongodbcol ) { dbcollection = obj . parent . args . mongodbcol ; }
2019-02-18 17:32:55 -05:00
// Setup MongoDB main collection and indexes
2017-08-28 12:27:45 -04:00
obj . file = db . collection ( dbcollection ) ;
2019-02-18 17:32:55 -05:00
obj . file . getIndexes ( function ( err , indexes ) {
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
if ( ( indexCount != 4 ) || ( indexesByName [ 'TypeDomainMesh1' ] == null ) || ( indexesByName [ 'Email1' ] == null ) || ( indexesByName [ 'Mesh1' ] == null ) ) {
console . log ( 'Resetting main indexes...' ) ;
obj . file . dropIndexes ( function ( err ) {
obj . file . createIndex ( { type : 1 , domain : 1 , meshid : 1 } , { sparse : 1 , name : 'TypeDomainMesh1' } ) ; // Speeds up GetAllTypeNoTypeField() and GetAllTypeNoTypeFieldMeshFiltered()
obj . file . createIndex ( { email : 1 } , { sparse : 1 , name : 'Email1' } ) ; // Speeds up GetUserWithEmail() and GetUserWithVerifiedEmail()
obj . file . createIndex ( { meshid : 1 } , { sparse : 1 , name : 'Mesh1' } ) ; // Speeds up RemoveMesh()
} ) ;
}
} ) ;
// Setup MongoDB events collection and indexes
2019-02-18 21:29:13 -05:00
obj . eventsfile = db . collection ( 'events' ) ; // Collection containing all events
2019-02-18 17:32:55 -05:00
obj . eventsfile . getIndexes ( function ( err , indexes ) {
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
2019-02-20 18:26:27 -05:00
if ( ( indexCount != 5 ) || ( indexesByName [ 'Username1' ] == null ) || ( indexesByName [ 'DomainNodeTime1' ] == null ) || ( indexesByName [ 'IdsAndTime1' ] == null ) || ( indexesByName [ 'ExpireTime1' ] == null ) ) {
2019-02-18 17:32:55 -05:00
// Reset all indexes
console . log ( 'Resetting events indexes...' ) ;
obj . eventsfile . dropIndexes ( function ( err ) {
2019-02-20 18:26:27 -05:00
obj . eventsfile . createIndex ( { username : 1 } , { sparse : 1 , name : 'Username1' } ) ;
obj . eventsfile . createIndex ( { domain : 1 , nodeid : 1 , time : - 1 } , { sparse : 1 , name : 'DomainNodeTime1' } ) ;
2019-02-18 17:32:55 -05:00
obj . eventsfile . createIndex ( { ids : 1 , time : - 1 } , { sparse : 1 , name : 'IdsAndTime1' } ) ;
obj . eventsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
} else if ( indexesByName [ 'ExpireTime1' ] . expireAfterSeconds != expireEventsSeconds ) {
// Reset the timeout index
console . log ( 'Resetting events expire index...' ) ;
obj . eventsfile . dropIndex ( "ExpireTime1" , function ( err ) {
obj . eventsfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expireEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
}
} ) ;
// Setup MongoDB power events collection and indexes
2019-02-18 21:29:13 -05:00
obj . powerfile = db . collection ( 'power' ) ; // Collection containing all power events
2019-02-18 17:32:55 -05:00
obj . powerfile . getIndexes ( function ( err , indexes ) {
// Check if we need to reset indexes
var indexesByName = { } , indexCount = 0 ;
for ( var i in indexes ) { indexesByName [ indexes [ i ] . name ] = indexes [ i ] ; indexCount ++ ; }
if ( ( indexCount != 3 ) || ( indexesByName [ 'NodeIdAndTime1' ] == null ) || ( indexesByName [ 'ExpireTime1' ] == null ) ) {
// Reset all indexes
console . log ( 'Resetting power events indexes...' ) ;
obj . powerfile . dropIndexes ( function ( err ) {
// Create all indexes
obj . powerfile . createIndex ( { nodeid : 1 , time : 1 } , { sparse : 1 , name : 'NodeIdAndTime1' } ) ;
obj . powerfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expirePowerEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
} else if ( indexesByName [ 'ExpireTime1' ] . expireAfterSeconds != expirePowerEventsSeconds ) {
// Reset the timeout index
console . log ( 'Resetting power events expire index...' ) ;
obj . powerfile . dropIndex ( "ExpireTime1" , function ( err ) {
// Reset the expire power events index
obj . powerfile . createIndex ( { "time" : 1 } , { expireAfterSeconds : expirePowerEventsSeconds , name : 'ExpireTime1' } ) ;
} ) ;
}
} ) ;
2019-02-16 15:56:33 -05:00
2017-08-28 12:27:45 -04:00
} else {
// Use NeDB (The default)
obj . databaseType = 1 ;
2018-08-29 20:40:30 -04:00
Datastore = require ( 'nedb' ) ;
2019-01-24 15:08:48 -05:00
var datastoreOptions = { filename : obj . parent . getConfigFilePath ( 'meshcentral.db' ) , autoload : true } ;
// If a DB encryption key is provided, perform database encryption
2019-01-27 15:23:38 -05:00
if ( ( typeof obj . parent . args . dbencryptkey == 'string' ) && ( obj . parent . args . dbencryptkey . length != 0 ) ) {
2019-01-24 15:08:48 -05:00
// Hash the database password into a AES256 key and setup encryption and decryption.
obj . dbKey = obj . parent . crypto . createHash ( 'sha384' ) . update ( obj . parent . args . dbencryptkey ) . digest ( "raw" ) . slice ( 0 , 32 ) ;
datastoreOptions . afterSerialization = function ( plaintext ) {
const iv = obj . parent . crypto . randomBytes ( 16 ) ;
const aes = obj . parent . crypto . createCipheriv ( 'aes-256-cbc' , obj . dbKey , iv ) ;
var ciphertext = aes . update ( plaintext ) ;
ciphertext = Buffer . concat ( [ iv , ciphertext , aes . final ( ) ] ) ;
return ciphertext . toString ( 'base64' ) ;
}
datastoreOptions . beforeDeserialization = function ( ciphertext ) {
const ciphertextBytes = Buffer . from ( ciphertext , 'base64' ) ;
const iv = ciphertextBytes . slice ( 0 , 16 ) ;
const data = ciphertextBytes . slice ( 16 ) ;
const aes = obj . parent . crypto . createDecipheriv ( 'aes-256-cbc' , obj . dbKey , iv ) ;
var plaintextBytes = Buffer . from ( aes . update ( data ) ) ;
plaintextBytes = Buffer . concat ( [ plaintextBytes , aes . final ( ) ] ) ;
return plaintextBytes . toString ( ) ;
}
}
2019-02-18 17:32:55 -05:00
// Start NeDB main collection and setup indexes
2019-01-24 15:08:48 -05:00
obj . file = new Datastore ( datastoreOptions ) ;
2019-02-18 17:32:55 -05:00
obj . file . persistence . setAutocompactionInterval ( 36000 ) ;
2019-02-16 15:56:33 -05:00
obj . file . ensureIndex ( { fieldName : 'type' } ) ;
obj . file . ensureIndex ( { fieldName : 'domain' } ) ;
2019-02-18 17:32:55 -05:00
obj . file . ensureIndex ( { fieldName : 'meshid' , sparse : true } ) ;
obj . file . ensureIndex ( { fieldName : 'nodeid' , sparse : true } ) ;
obj . file . ensureIndex ( { fieldName : 'email' , sparse : true } ) ;
// Setup the events collection and setup indexes
obj . eventsfile = new Datastore ( { filename : obj . parent . getConfigFilePath ( 'meshcentral-events.db' ) , autoload : true } ) ;
obj . eventsfile . persistence . setAutocompactionInterval ( 36000 ) ;
obj . eventsfile . ensureIndex ( { fieldName : 'ids' } ) ; // TODO: Not sure if this is a good index, this is a array field.
obj . eventsfile . ensureIndex ( { fieldName : 'nodeid' , sparse : true } ) ;
obj . eventsfile . ensureIndex ( { fieldName : 'time' , expireAfterSeconds : 60 * 60 * 24 * 20 } ) ; // Limit the power event log to 20 days (Seconds * Minutes * Hours * Days)
// Setup the power collection and setup indexes
obj . powerfile = new Datastore ( { filename : obj . parent . getConfigFilePath ( 'meshcentral-power.db' ) , autoload : true } ) ;
obj . powerfile . persistence . setAutocompactionInterval ( 36000 ) ;
obj . powerfile . ensureIndex ( { fieldName : 'nodeid' } ) ;
obj . powerfile . ensureIndex ( { fieldName : 'time' , expireAfterSeconds : 60 * 60 * 24 * 10 } ) ; // Limit the power event log to 10 days (Seconds * Minutes * Hours * Days)
2017-08-28 12:27:45 -04:00
}
2018-08-29 20:40:30 -04:00
2017-08-28 12:27:45 -04:00
obj . SetupDatabase = function ( func ) {
2017-09-13 14:25:57 -04:00
// Check if the database unique identifier is present
// This is used to check that in server peering mode, everyone is using the same database.
obj . Get ( 'DatabaseIdentifier' , function ( err , docs ) {
if ( ( docs . length == 1 ) && ( docs [ 0 ] . value != null ) ) {
obj . identifier = docs [ 0 ] . value ;
} else {
2019-01-02 21:03:34 -05:00
obj . identifier = Buffer . from ( require ( 'crypto' ) . randomBytes ( 48 ) , 'binary' ) . toString ( 'hex' ) ;
2017-09-13 14:25:57 -04:00
obj . Set ( { _id : 'DatabaseIdentifier' , value : obj . identifier } ) ;
}
} ) ;
2017-08-28 12:27:45 -04:00
// Load database schema version and check if we need to update
obj . Get ( 'SchemaVersion' , function ( err , docs ) {
var ver = 0 ;
if ( docs && docs . length == 1 ) { ver = docs [ 0 ] . value ; }
2017-10-16 23:11:03 -04:00
if ( ver == 1 ) { console . log ( 'This is an unsupported beta 1 database, delete it to create a new one.' ) ; process . exit ( 0 ) ; }
2017-08-28 12:27:45 -04:00
2017-10-15 02:22:19 -04:00
// TODO: Any schema upgrades here...
2017-10-16 23:11:03 -04:00
obj . Set ( { _id : 'SchemaVersion' , value : 2 } ) ;
2017-08-28 12:27:45 -04:00
2017-10-15 02:22:19 -04:00
func ( ver ) ;
2017-08-28 12:27:45 -04:00
} ) ;
2018-08-29 20:40:30 -04:00
} ;
2017-08-28 12:27:45 -04:00
2019-01-24 20:01:50 -05:00
obj . cleanup = function ( func ) {
2017-08-28 12:27:45 -04:00
// TODO: Remove all mesh links to invalid users
// TODO: Remove all meshes that dont have any links
2019-02-18 17:32:55 -05:00
// Remote all the events and power events from the main collection.
// They are all in two seperate collections now.
obj . file . remove ( { type : 'event' } , { multi : true } ) ;
obj . file . remove ( { type : 'power' } , { multi : true } ) ;
2017-08-28 12:27:45 -04:00
// Remove all objects that have a "meshid" that no longer points to a valid mesh.
obj . GetAllType ( 'mesh' , function ( err , docs ) {
var meshlist = [ ] ;
2019-02-18 17:32:55 -05:00
if ( ( err == null ) && ( docs . length > 0 ) ) { for ( var i in docs ) { meshlist . push ( docs [ i ] . _id ) ; } }
2017-08-28 12:27:45 -04:00
obj . file . remove ( { meshid : { $exists : true , $nin : meshlist } } , { multi : true } ) ;
2017-12-12 19:04:54 -05:00
2019-01-24 20:01:50 -05:00
// Fix all of the creating & login to ticks by seconds, not milliseconds.
obj . GetAllType ( 'user' , function ( err , docs ) {
if ( err == null && docs . length > 0 ) {
for ( var i in docs ) {
var fixed = false ;
// Fix account creation
if ( docs [ i ] . creation ) {
if ( docs [ i ] . creation > 1300000000000 ) { docs [ i ] . creation = Math . floor ( docs [ i ] . creation / 1000 ) ; fixed = true ; }
if ( ( docs [ i ] . creation % 1 ) != 0 ) { docs [ i ] . creation = Math . floor ( docs [ i ] . creation ) ; fixed = true ; }
}
// Fix last account login
if ( docs [ i ] . login ) {
if ( docs [ i ] . login > 1300000000000 ) { docs [ i ] . login = Math . floor ( docs [ i ] . login / 1000 ) ; fixed = true ; }
if ( ( docs [ i ] . login % 1 ) != 0 ) { docs [ i ] . login = Math . floor ( docs [ i ] . login ) ; fixed = true ; }
}
// Fix last password change
if ( docs [ i ] . passchange ) {
if ( docs [ i ] . passchange > 1300000000000 ) { docs [ i ] . passchange = Math . floor ( docs [ i ] . passchange / 1000 ) ; fixed = true ; }
if ( ( docs [ i ] . passchange % 1 ) != 0 ) { docs [ i ] . passchange = Math . floor ( docs [ i ] . passchange ) ; fixed = true ; }
}
// Fix subscriptions
if ( docs [ i ] . subscriptions != null ) { delete docs [ i ] . subscriptions ; fixed = true ; }
// Save the user if needed
if ( fixed ) { obj . Set ( docs [ i ] ) ; }
// We are done
if ( func ) { func ( ) ; }
}
}
} ) ;
2017-12-12 19:04:54 -05:00
} ) ;
2018-08-29 20:40:30 -04:00
} ;
2017-08-28 12:27:45 -04:00
2019-02-18 17:32:55 -05:00
// Database actions on the main collection
2018-08-29 20:40:30 -04:00
obj . Set = function ( data , func ) { obj . file . update ( { _id : data . _id } , data , { upsert : true } , func ) ; } ;
obj . Get = function ( id , func ) { obj . file . find ( { _id : id } , func ) ; } ;
obj . GetAll = function ( func ) { obj . file . find ( { } , func ) ; } ;
obj . GetAllTypeNoTypeField = function ( type , domain , func ) { obj . file . find ( { type : type , domain : domain } , { type : 0 } , func ) ; } ;
2019-01-29 17:08:29 -05:00
obj . GetAllTypeNoTypeFieldMeshFiltered = function ( meshes , domain , type , id , func ) { var x = { type : type , domain : domain , meshid : { $in : meshes } } ; if ( id ) { x . _id = id ; } obj . file . find ( x , { type : 0 } , func ) ; } ;
2018-08-29 20:40:30 -04:00
obj . GetAllType = function ( type , func ) { obj . file . find ( { type : type } , func ) ; } ;
obj . GetAllIdsOfType = function ( ids , domain , type , func ) { obj . file . find ( { type : type , domain : domain , _id : { $in : ids } } , func ) ; } ;
obj . GetUserWithEmail = function ( domain , email , func ) { obj . file . find ( { type : 'user' , domain : domain , email : email } , { type : 0 } , func ) ; } ;
obj . GetUserWithVerifiedEmail = function ( domain , email , func ) { obj . file . find ( { type : 'user' , domain : domain , email : email , emailVerified : true } , { type : 0 } , func ) ; } ;
obj . Remove = function ( id ) { obj . file . remove ( { _id : id } ) ; } ;
obj . RemoveNode = function ( id ) { obj . file . remove ( { node : id } , { multi : true } ) ; } ;
obj . RemoveAll = function ( func ) { obj . file . remove ( { } , { multi : true } , func ) ; } ;
obj . RemoveAllOfType = function ( type , func ) { obj . file . remove ( { type : type } , { multi : true } , func ) ; } ;
obj . InsertMany = function ( data , func ) { obj . file . insert ( data , func ) ; } ;
2019-02-18 17:32:55 -05:00
obj . RemoveMeshDocuments = function ( id ) { obj . file . remove ( { meshid : id } , { multi : true } ) ; obj . file . remove ( { _id : 'nt' + id } ) ; } ;
2018-08-29 20:40:30 -04:00
obj . MakeSiteAdmin = function ( username , domain ) { obj . Get ( 'user/' + domain + '/' + username , function ( err , docs ) { if ( docs . length == 1 ) { docs [ 0 ] . siteadmin = 0xFFFFFFFF ; obj . Set ( docs [ 0 ] ) ; } } ) ; } ;
obj . DeleteDomain = function ( domain , func ) { obj . file . remove ( { domain : domain } , { multi : true } , func ) ; } ;
obj . SetUser = function ( user ) { var u = Clone ( user ) ; if ( u . subscriptions ) { delete u . subscriptions ; } obj . Set ( u ) ; } ;
obj . dispose = function ( ) { for ( var x in obj ) { if ( obj [ x ] . close ) { obj [ x ] . close ( ) ; } delete obj [ x ] ; } } ;
obj . getLocalAmtNodes = function ( func ) { obj . file . find ( { type : 'node' , host : { $exists : true , $ne : null } , intelamt : { $exists : true } } , func ) ; } ;
obj . getAmtUuidNode = function ( meshid , uuid , func ) { obj . file . find ( { type : 'node' , meshid : meshid , 'intelamt.uuid' : uuid } , func ) ; } ;
2019-02-11 17:41:15 -05:00
obj . isMaxType = function ( max , type , domainid , func ) { if ( max == null ) { func ( false ) ; } else { obj . file . count ( { type : type , domain : domainid } , function ( err , count ) { func ( ( err != null ) || ( count > max ) ) ; } ) ; } }
2017-08-28 12:27:45 -04:00
2019-02-18 17:32:55 -05:00
// Database actions on the events collection
obj . GetAllEvents = function ( func ) { obj . eventsfile . find ( { } , func ) ; } ;
obj . StoreEvent = function ( event ) { obj . eventsfile . insert ( event ) ; } ;
obj . GetEvents = function ( ids , domain , func ) { if ( obj . databaseType == 1 ) { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } , { _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . exec ( func ) ; } else { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } , func ) ; } } ;
obj . GetEventsWithLimit = function ( ids , domain , limit , func ) { if ( obj . databaseType == 1 ) { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } , { _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . exec ( func ) ; } else { obj . eventsfile . find ( { domain : domain , ids : { $in : ids } } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit , func ) ; } } ;
obj . GetUserEvents = function ( ids , domain , username , func ) {
if ( obj . databaseType == 1 ) {
obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . exec ( func ) ;
} else {
obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } , func ) ;
}
} ;
obj . GetUserEventsWithLimit = function ( ids , domain , username , limit , func ) {
if ( obj . databaseType == 1 ) {
obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . exec ( func ) ;
} else {
obj . eventsfile . find ( { domain : domain , $or : [ { ids : { $in : ids } } , { username : username } ] } , { type : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 } ) . sort ( { time : - 1 } ) . limit ( limit , func ) ;
}
} ;
obj . GetNodeEventsWithLimit = function ( nodeid , domain , limit , func ) { if ( obj . databaseType == 1 ) { obj . eventsfile . find ( { domain : domain , nodeid : nodeid } , { type : 0 , etype : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 , nodeid : 0 } ) . sort ( { time : - 1 } ) . limit ( limit ) . exec ( func ) ; } else { obj . eventsfile . find ( { domain : domain , nodeid : nodeid } , { type : 0 , etype : 0 , _id : 0 , domain : 0 , ids : 0 , node : 0 , nodeid : 0 } ) . sort ( { time : - 1 } ) . limit ( limit , func ) ; } } ;
obj . RemoveAllEvents = function ( domain ) { obj . eventsfile . remove ( { domain : domain } , { multi : true } ) ; } ;
// Database actions on the power collection
obj . getAllPower = function ( func ) { obj . powerfile . find ( { } , func ) ; } ;
obj . storePowerEvent = function ( event , multiServer , func ) { if ( multiServer != null ) { event . server = multiServer . serverid ; } obj . powerfile . insert ( event , func ) ; } ;
obj . getPowerTimeline = function ( nodeid , func ) { if ( obj . databaseType == 1 ) { obj . powerfile . find ( { nodeid : { $in : [ '*' , nodeid ] } } , { _id : 0 , nodeid : 0 , s : 0 } ) . sort ( { time : 1 } ) . exec ( func ) ; } else { obj . powerfile . find ( { nodeid : { $in : [ '*' , nodeid ] } } , { _id : 0 , nodeid : 0 , s : 0 } ) . sort ( { time : 1 } , func ) ; } } ;
obj . removeAllPowerEvents = function ( domain ) { obj . powerfile . remove ( { } , { multi : true } ) ; } ;
2019-02-02 17:54:36 -05:00
// Read a configuration file from the database
obj . getConfigFile = function ( path , func ) { obj . Get ( 'cfile/' + path , func ) ; }
// Write a configuration file to the database
obj . setConfigFile = function ( path , data , func ) { obj . Set ( { _id : 'cfile/' + path , type : 'cfile' , data : data . toString ( 'base64' ) } , func ) ; }
// List all configuration files
obj . listConfigFiles = function ( func ) { obj . file . find ( { type : 'cfile' } ) . sort ( { _id : 1 } ) . exec ( func ) ; }
// Get all configuration files
obj . getAllConfigFiles = function ( password , func ) {
obj . file . find ( { type : 'cfile' } , function ( err , docs ) {
if ( err != null ) { func ( null ) ; return ; }
var r = null ;
for ( var i = 0 ; i < docs . length ; i ++ ) {
var name = docs [ i ] . _id . split ( '/' ) [ 1 ] ;
var data = obj . decryptData ( password , docs [ i ] . data ) ;
if ( data != null ) { if ( r == null ) { r = { } ; } r [ name ] = data ; }
}
func ( r ) ;
} ) ;
}
2019-02-01 18:16:46 -05:00
2019-02-02 17:54:36 -05:00
// Get encryption key
obj . getEncryptDataKey = function ( password ) {
if ( typeof password != 'string' ) return null ;
return obj . parent . crypto . createHash ( 'sha384' ) . update ( password ) . digest ( "raw" ) . slice ( 0 , 32 ) ;
}
2019-02-01 18:16:46 -05:00
2019-02-02 17:54:36 -05:00
// Encrypt data
obj . encryptData = function ( password , plaintext ) {
var key = obj . getEncryptDataKey ( password ) ;
if ( key == null ) return null ;
const iv = obj . parent . crypto . randomBytes ( 16 ) ;
const aes = obj . parent . crypto . createCipheriv ( 'aes-256-cbc' , key , iv ) ;
var ciphertext = aes . update ( plaintext ) ;
ciphertext = Buffer . concat ( [ iv , ciphertext , aes . final ( ) ] ) ;
return ciphertext . toString ( 'base64' ) ;
}
// Decrypt data
obj . decryptData = function ( password , ciphertext ) {
try {
var key = obj . getEncryptDataKey ( password ) ;
if ( key == null ) return null ;
const ciphertextBytes = Buffer . from ( ciphertext , 'base64' ) ;
const iv = ciphertextBytes . slice ( 0 , 16 ) ;
const data = ciphertextBytes . slice ( 16 ) ;
const aes = obj . parent . crypto . createDecipheriv ( 'aes-256-cbc' , key , iv ) ;
var plaintextBytes = Buffer . from ( aes . update ( data ) ) ;
plaintextBytes = Buffer . concat ( [ plaintextBytes , aes . final ( ) ] ) ;
return plaintextBytes ;
} catch ( ex ) { return null ; }
}
2019-02-01 18:16:46 -05:00
2019-02-16 15:56:33 -05:00
// Get the number of records in the database for various types, this is the slow NeDB way.
// WARNING: This is a terrible query for database performance. Only do this when needed. This query will look at almost every document in the database.
2018-09-26 17:58:55 -04:00
obj . getStats = function ( func ) {
2019-02-16 15:56:33 -05:00
if ( obj . databaseType == 2 ) {
// MongoDB version
obj . file . aggregate ( [ { "$group" : { _id : "$type" , count : { $sum : 1 } } } ] , function ( err , docs ) {
var counters = { } , totalCount = 0 ;
for ( var i in docs ) { if ( docs [ i ] . _id != null ) { counters [ docs [ i ] . _id ] = docs [ i ] . count ; totalCount += docs [ i ] . count ; } }
2019-02-18 17:32:55 -05:00
func ( { nodes : counters [ 'node' ] , meshes : counters [ 'mesh' ] , users : counters [ 'user' ] , total : totalCount } ) ;
2019-02-16 15:56:33 -05:00
} )
} else {
// NeDB version
obj . file . count ( { type : 'node' } , function ( err , nodeCount ) {
obj . file . count ( { type : 'mesh' } , function ( err , meshCount ) {
2019-02-18 17:32:55 -05:00
obj . file . count ( { type : 'user' } , function ( err , userCount ) {
obj . file . count ( { } , function ( err , totalCount ) {
func ( { nodes : nodeCount , meshes : meshCount , powerEvents : powerCount , users : userCount , nodeInterfaces : nodeInterfaceCount , notes : noteCount , connectEvent : nodeLastConnectCount , smbios : nodeSmbiosCount , total : totalCount } ) ;
2018-09-26 17:58:55 -04:00
} ) ;
} ) ;
} ) ;
} ) ;
2019-02-16 15:56:33 -05:00
}
2018-09-26 17:58:55 -04:00
}
2017-09-05 20:19:28 -04:00
// This is used to rate limit a number of operation per day. Returns a startValue each new days, but you can substract it and save the value in the db.
2018-08-29 20:40:30 -04:00
obj . getValueOfTheDay = function ( id , startValue , func ) { obj . Get ( id , function ( err , docs ) { var date = new Date ( ) , t = date . toLocaleDateString ( ) ; if ( docs . length == 1 ) { var r = docs [ 0 ] ; if ( r . day == t ) { func ( { _id : id , value : r . value , day : t } ) ; return ; } } func ( { _id : id , value : startValue , day : t } ) ; } ) ; } ;
2017-09-05 20:19:28 -04:00
2017-08-28 12:27:45 -04:00
function Clone ( v ) { return JSON . parse ( JSON . stringify ( v ) ) ; }
return obj ;
2018-08-29 20:40:30 -04:00
} ;