2019-07-17 13:37:18 -04:00
# include "LicenseManager.h"
# include <log/LogUtils.h>
using namespace std ;
using namespace std : : chrono ;
using namespace license ;
using namespace license : : server ;
using namespace sql ;
LicenseManager : : LicenseManager ( sql : : SqlManager * database ) : database ( database ) {
this - > id_cache = make_shared < KeyIdCache > ( this ) ;
}
LicenseManager : : ~ LicenseManager ( ) { }
/*
LicenseType type ;
std : : string username ;
std : : string first_name ;
std : : string last_name ;
std : : string email ;
std : : chrono : : system_clock : : time_point start ;
std : : chrono : : system_clock : : time_point end ;
std : : chrono : : system_clock : : time_point creation ;
*/
# define CTBL(cmd) \
res = command ( this - > database , cmd ) . execute ( ) ; \
if ( ! res ) { \
error = " Could not setup tables! ( " + res . fmtStr ( ) + " ) " ; \
return false ; \
}
# define CIDX(cmd) \
res = command ( this - > database , cmd ) . execute ( ) ; \
if ( ! res & & res . msg ( ) . find ( " Duplicate " ) = = string : : npos & & res . msg ( ) . find ( " exist " ) = = string : : npos ) { \
error = " Could not setup indexes! ( " + res . fmtStr ( ) + " ) " ; \
return false ; \
}
# define SET_VERSION(ver) \
version = ver ; \
sql : : command ( this - > database , " UPDATE `general` SET `value` = :version WHERE `key` = 'version' " , variable { " :version " , version } ) . execute ( ) ;
bool LicenseManager : : setup ( std : : string & error ) {
result res ;
int version = - 1 ;
sql : : command ( this - > database , " SELECT `value` FROM `general` WHERE `key` = 'version' " ) . query ( [ ] ( int * version , int lnegth , string * values , string * names ) {
* version = stoll ( values [ 0 ] ) ;
return 0 ;
} , & version ) ;
switch ( version ) {
case - 1 :
CTBL ( " CREATE TABLE IF NOT EXISTS `license` (`key` BINARY(64) NOT NULL PRIMARY KEY, `type` INT, `deleted` BOOL, `issuer` TEXT) " ) ;
CTBL ( " CREATE TABLE IF NOT EXISTS `license_info` (`key` BINARY(64) NOT NULL PRIMARY KEY, `username` VARCHAR(128), `first_name` TEXT, `last_name` TEXT, `email` TEXT, `begin` BIGINT, `end` BIGINT, `generated` BIGINT) " ) ;
CTBL ( " CREATE TABLE IF NOT EXISTS `license_request` (`key` BINARY(64) NOT NULL, `ip` TEXT, `timestamp` BIGINT, `result` INT) " ) ;
CTBL ( " CREATE TABLE IF NOT EXISTS `general` (`key` VARCHAR(64), `value` TEXT) " ) ;
CTBL ( " INSERT INTO `general` (`key`, `value`) VALUES ('version', '0'); " ) ;
SET_VERSION ( 0 ) ;
case 0 :
2020-02-06 07:49:39 -05:00
logMessage ( LOG_GENERAL , " Updating database! To version 1 " ) ;
2019-07-17 13:37:18 -04:00
CTBL ( " CREATE TABLE IF NOT EXISTS `history_speach` (`keyId` INT, `timestamp` BIGINT, `total` BIGINT, `dead` BIGINT, `online` BIGINT, `varianz` BIGINT) " ) ;
CTBL ( " CREATE TABLE IF NOT EXISTS `history_online` (`keyId` INT, `timestamp` BIGINT, `server` INT, `clients` INT, `web` INT, `music` INT, `queries` INT) " ) ;
CIDX ( " CREATE INDEX `license_key` ON `license` (`key`) " ) ;
CIDX ( " CREATE INDEX `license_info_key` ON `license_info` (`key`) " ) ;
CTBL ( " ALTER TABLE `license` DROP PRIMARY KEY, ADD `keyId` INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; " ) ;
CTBL ( " ALTER TABLE `license_info` DROP PRIMARY KEY, ADD `keyId` INT NOT NULL PRIMARY KEY AUTO_INCREMENT FIRST; " ) ;
//Fixing key id's
CTBL ( " UPDATE `license_info` LEFT JOIN `license` ON `license`.`key` = `license_info`.`key` SET `license_info`.`keyId` = `license`.`keyId` " ) ;
//Deleting the key blob and removing auto
CTBL ( " ALTER TABLE `license_info` CHANGE `keyId` `keyId` INT NOT NULL UNIQUE, DROP COLUMN `key` " ) ;
//Fixing request table
CTBL ( " ALTER TABLE license_request ADD COLUMN `keyId` INT(0) NOT NULL; " ) ;
CTBL ( " UPDATE license_request LEFT JOIN license ON license.key = license_request.key SET license_request.keyId = license.keyId; " ) ;
CTBL ( " ALTER TABLE license_request DROP COLUMN `key`; " ) ;
//IDK why but i failed stuff :D
CTBL ( " ALTER TABLE license DROP PRIMARY KEY, CHANGE `keyId` `keyId` INT NOT NULL AUTO_INCREMENT PRIMARY KEY " ) ;
SET_VERSION ( 1 ) ;
case 1 :
CTBL ( " ALTER TABLE history_online ADD COLUMN `ip` TEXT AFTER `keyId`; " ) ;
CTBL ( " ALTER TABLE history_speach ADD COLUMN `ip` TEXT AFTER `keyId`; " ) ;
SET_VERSION ( 2 ) ;
case 2 :
CTBL ( " CREATE TABLE `history_version` (`keyId` INT, `ip` VARCHAR(64), `timestamp` BIGINT, `version` VARCHAR(126)); " ) ;
SET_VERSION ( 3 ) ;
case 3 :
/*
CTBL ( " CREATE TABLE `save_history_online` AS SELECT * FROM `history_online`; " ) ;
CTBL ( " CREATE TABLE `save_history_speach` AS SELECT * FROM `history_speach`; " ) ;
CTBL ( " CREATE TABLE `save_history_version` AS SELECT * FROM `history_version`; " ) ;
*/
CTBL ( " ALTER TABLE `history_online` CHANGE `ip` `unique_id` VARCHAR(64) NOT NULL; " ) ;
CTBL ( " ALTER TABLE `history_speach` CHANGE `ip` `unique_id` VARCHAR(64) NOT NULL; " ) ;
CTBL ( " ALTER TABLE `history_version` CHANGE `ip` `unique_id` VARCHAR(64) NOT NULL; " ) ;
CTBL ( " ALTER TABLE `license_request` ADD `unique_id` VARCHAR(64) NOT NULL AFTER `ip`; " ) ;
SET_VERSION ( 4 ) ;
case 4 :
CTBL ( " CREATE TABLE IF NOT EXISTS `users` (`username` VARCHAR(64) NOT NULL PRIMARY KEY, `password_hash` VARCHAR(128), `status` INT, `owner` VARCHAR(64)) " ) ;
default : ;
}
return true ;
}
bool LicenseManager : : registerLicense ( const std : : string & key , const shared_ptr < LicenseInfo > & info , const std : : string & issuer ) {
result res ;
res = command ( this - > database , " INSERT INTO `license` (`key`, `type`, `deleted`, `issuer`) VALUES (:key, :type, :deleted, :issuer) " , variable { " :key " , key } , variable { " :type " , ( uint32_t ) info - > type } , variable { " :deleted " , false } , variable { " :issuer " , issuer } ) . execute ( ) ;
if ( ! res ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not register new license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return false ;
}
auto keyId = this - > id_cache - > getKeyId ( key ) ;
if ( keyId = = 0 ) return false ;
res = command ( this - > database , " INSERT INTO `license_info` (`keyId`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`) VALUES (:key, :username, :first_name, :last_name, :email, :begin, :end, :generated) " ,
variable { " :key " , keyId } ,
variable { " :username " , info - > username } ,
variable { " :first_name " , info - > first_name } ,
variable { " :last_name " , info - > last_name } ,
variable { " :email " , info - > email } ,
variable { " :generated " , duration_cast < milliseconds > ( info - > creation . time_since_epoch ( ) ) . count ( ) } ,
variable { " :begin " , duration_cast < milliseconds > ( info - > start . time_since_epoch ( ) ) . count ( ) } ,
variable { " :end " , duration_cast < milliseconds > ( info - > end . time_since_epoch ( ) ) . count ( ) }
) . execute ( ) ;
if ( ! res ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not register new license info ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return false ;
}
return true ;
}
bool LicenseManager : : deleteLicense ( const std : : string & key , bool full ) {
if ( full ) {
auto keyId = this - > id_cache - > getKeyId ( key ) ;
if ( keyId = = 0 ) return false ; //Never exists
auto res = command ( this - > database , " DELETE FROM `license` WHERE `key` = :key " , variable { " :key " , key } ) . execute ( ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not delete license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
res = command ( this - > database , " DELETE FROM `license_info` WHERE `keyId` = :key " , variable { " :keyId " , keyId } ) . execute ( ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not delete license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return ! ! res ;
} else {
auto res = command ( this - > database , " UPDATE `license` SET `deleted` = :true WHERE `key` = :key " , variable { " :true " , true } , variable { " :key " , key } ) . execute ( ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not delete license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return ! ! res ;
}
}
bool LicenseManager : : validLicenseKey ( const std : : string & key ) {
bool valid = false ;
auto res = command ( this - > database , " SELECT * FROM `license` WHERE `key` = :key AND `deleted` = :false LIMIT 1 " , variable { " :false " , false } , variable { " :key " , key } ) . query ( [ ] ( bool * flag , int , char * * , char * * ) {
* flag = true ;
return 0 ;
} , & valid ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not validate license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return ! ! res & & valid ;
}
inline std : : map < std : : string , std : : shared_ptr < LicenseInfo > > query_license ( SqlManager * mgr , std : : string key , int offset , int length ) {
std : : map < std : : string , std : : shared_ptr < LicenseInfo > > result ;
auto query = string ( ) + " SELECT `key`, `username`, `first_name`, `last_name`, `email`, `begin`, `end`, `generated`, `deleted` FROM `license_info` INNER JOIN `license` ON `license_info`.`keyId` = `license`.`keyId` " ;
if ( ! key . empty ( ) )
query + = " WHERE `key` = :key " ;
else
query + = " WHERE `deleted` = :false " ;
if ( offset > 0 | | length > 0 )
query + = " LIMIT " + to_string ( offset ) + " , " + to_string ( length ) ;
logTrace ( LOG_GENERAL , " Executing query {} " , query ) ;
auto res = command ( mgr , query , variable { " :key " , key } , variable { " :false " , false } ) . query ( [ ] ( std : : map < std : : string , std : : shared_ptr < LicenseInfo > > * list , int length , std : : string * values , std : : string * names ) {
auto info = make_shared < LicenseInfo > ( ) ;
info - > deleted = false ;
string k ;
for ( int index = 0 ; index < length ; index + + ) {
try {
if ( names [ index ] = = " username " )
info - > username = values [ index ] ;
else if ( names [ index ] = = " first_name " )
info - > first_name = values [ index ] ;
else if ( names [ index ] = = " key " )
k = values [ index ] ;
else if ( names [ index ] = = " last_name " )
info - > last_name = values [ index ] ;
else if ( names [ index ] = = " email " )
info - > email = values [ index ] ;
else if ( names [ index ] = = " begin " )
info - > start = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " end " )
info - > end = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " generated " )
info - > creation = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " deleted " )
info - > deleted = values [ index ] = = " 1 " | | values [ index ] = = " true " ;
else
logError ( LOG_GENERAL , " Unknown field {} " , names [ index ] ) ;
} catch ( std : : exception & ex ) {
logError ( LOG_GENERAL , " Failed to parse field {} ({}). Message: {} " , names [ index ] , values [ index ] , ex . what ( ) ) ;
return 1 ;
}
}
( * list ) [ k ] = info ;
return 0 ;
} , & result ) ;
logTrace ( LOG_GENERAL , " Query returned {} results " , result . size ( ) ) ;
2020-02-06 07:49:39 -05:00
if ( ! res ) logError ( LOG_GENERAL , " Could not query license ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return result ;
}
std : : shared_ptr < LicenseInfo > LicenseManager : : licenseInfo ( const std : : string & key ) {
auto result = query_license ( this - > database , key , 0 , 0 ) ;
if ( result . empty ( ) ) return nullptr ;
return result . begin ( ) - > second ;
}
std : : map < std : : string , std : : shared_ptr < LicenseInfo > > LicenseManager : : listLicenses ( int offset , int limit ) {
return query_license ( this - > database , " " , offset , limit ) ;
}
bool LicenseManager : : logRequest ( const std : : string & key , const std : : string & unique_id , const std : : string & ip , const std : : string & version , int state ) {
result res ;
auto keyId = this - > id_cache - > getKeyId ( key ) ;
if ( keyId = = 0 ) {
logError ( LOG_GENERAL , " Failed to log license request (could not resolve key id) " ) ;
return false ;
}
auto timestamp = duration_cast < milliseconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
//SELECT * FROM `license_info` WHERE `keyId` IN (SELECT `keyId` FROM `license` WHERE `key` = '000')
res = command ( this - > database , " INSERT INTO `license_request` (`keyId`, `ip`, `unique_id`, `timestamp`, `result`) VALUES (:key, :ip, :unique_id, :timestamp, :result) " ,
variable { " :key " , keyId } ,
variable { " :ip " , ip } ,
variable { " :timestamp " , timestamp } ,
variable { " :unique_id " , unique_id } ,
variable { " :result " , state } ) . execute ( ) ;
if ( ! res ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not log license validation ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return false ;
}
{ //Log version
res = command ( this - > database , " INSERT INTO `history_version`(`keyId`, `unique_id`, `timestamp`, `version`) VALUES (:key, :unique_id, :time, :version) " ,
variable { " :key " , keyId } ,
variable { " :time " , timestamp } ,
variable { " :unique_id " , unique_id } ,
variable { " :version " , version }
) . execute ( ) ;
if ( ! res )
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not log license version statistic ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
res = { } ;
}
return true ;
}
bool LicenseManager : : logStatistic ( const std : : string & key , const std : : string & unique_id , const std : : string & ip ,
const ts : : proto : : license : : PropertyUpdateRequest & data ) {
result res ;
auto keyId = this - > id_cache - > getKeyId ( key ) ;
if ( keyId = = 0 ) return false ;
auto time = duration_cast < milliseconds > ( system_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
{ //Log online
res = command ( this - > database , " INSERT INTO `history_online`(`keyId`, `unique_id`, `timestamp`, `server`, `clients`, `web`, `music`, `queries`) VALUES (:key, :unique_id, :time, :server, :client, :web, :music, :query) " ,
variable { " :key " , keyId } ,
variable { " :unique_id " , unique_id } ,
variable { " :time " , time } ,
variable { " :server " , data . servers_online ( ) } ,
variable { " :client " , data . clients_online ( ) } ,
variable { " :web " , data . web_clients_online ( ) } ,
variable { " :music " , data . bots_online ( ) } ,
variable { " :query " , data . queries_online ( ) }
) . execute ( ) ;
if ( ! res )
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not log license statistics ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
res = { } ;
}
//SELECT * FROM `license_info` WHERE `keyId` IN (SELECT `keyId` FROM `license` WHERE `key` = '000')
{
res = command ( this - > database , " INSERT INTO `history_speach`(`keyId`, `unique_id`, `timestamp`, `total`, `dead`, `online`, `varianz`) VALUES (:key, :unique_id, :time, :total, :dead, :online, :varianz) " ,
variable { " :key " , keyId } ,
variable { " :unique_id " , unique_id } ,
variable { " :time " , time } ,
variable { " :total " , data . speach_total ( ) } ,
variable { " :dead " , data . speach_dead ( ) } ,
variable { " :online " , data . speach_online ( ) } ,
variable { " :varianz " , data . speach_varianz ( ) }
) . execute ( ) ;
if ( ! res )
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not log license statistics ( " + res . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
res = { } ;
}
return true ;
}
2019-08-25 16:16:42 -04:00
std : : shared_ptr < LicenseManager : : UserHistory > LicenseManager : : list_statistics_user ( const system_clock : : time_point & begin , const system_clock : : time_point & end , const milliseconds & interval ) {
2019-07-17 13:37:18 -04:00
auto timeout = hours ( 2 ) + minutes ( 10 ) ; //TODO timeout configurable?
2019-08-25 16:16:42 -04:00
/* initialize the result */
auto allocated_records = ( size_t ) floor ( ( end - begin ) / interval ) + 1 ;
auto _result = ( UserHistory * ) malloc ( sizeof ( UserHistory ) + sizeof ( GlobalUserStatistics ) * allocated_records ) ;
new ( _result ) UserHistory ( ) ;
auto result = shared_ptr < UserHistory > ( _result , [ ] ( UserHistory * ptr ) {
ptr - > ~ UserHistory ( ) ;
free ( ptr ) ;
} ) ;
result - > record_count = 0 ;
result - > begin = begin ;
result - > end = end ;
result - > interval = interval ;
memset ( & result - > history [ 0 ] , 0 , allocated_records * sizeof ( GlobalUserStatistics ) ) ;
auto info = & _result - > history [ 0 ] ;
/* temp db variables */
map < std : : string , LicenseManager : : DatabaseUserStatistics > server_statistics ;
bool have_key , have_uid ;
LicenseManager : : DatabaseUserStatistics temp_stats ; /* temp struct for stats parsing */
LicenseManager : : DatabaseUserStatistics * stats_ptr ; /* pointer to the target stats */
std : : chrono : : system_clock : : time_point current_timestamp = begin + interval ; /* upper limit of the current interval */
auto state = command ( this - > database , " SELECT * FROM `history_online` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC " ,
2019-07-17 13:37:18 -04:00
variable { " :timestamp_start " , duration_cast < milliseconds > ( begin . time_since_epoch ( ) - timeout ) . count ( ) } ,
variable { " :timestamp_end " , duration_cast < milliseconds > ( end . time_since_epoch ( ) ) . count ( ) }
2019-08-25 16:16:42 -04:00
) . query ( [ & ] ( int columns , std : : string * values , std : : string * names ) {
/* find the user statistic ptr */
{
size_t key_id = 0 ;
std : : string unique_id ;
have_key = false ;
have_uid = false ;
for ( int index = 0 ; index < columns ; index + + ) {
2020-02-06 07:49:39 -05:00
if ( names [ index ] = = " keyId " ) {
key_id = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
if ( key_id = = 0 ) return 0 ; /* invalid key id */
have_key = true ;
if ( have_key & & have_uid ) goto process_tag ;
} else if ( names [ index ] = = " unique_id " ) {
unique_id = values [ index ] ;
have_uid = true ;
if ( have_key & & have_uid ) goto process_tag ;
}
2019-08-25 16:16:42 -04:00
}
2020-02-06 07:49:39 -05:00
return 0 ; /* key or uid haven't been found */
2019-08-25 16:16:42 -04:00
2020-02-06 07:49:39 -05:00
process_tag :
2019-08-25 16:16:42 -04:00
stats_ptr = & server_statistics [ to_string ( key_id ) + " _ " + unique_id ] ;
}
2019-07-17 13:37:18 -04:00
for ( int index = 0 ; index < columns ; index + + ) {
2020-02-06 07:49:39 -05:00
if ( names [ index ] = = " timestamp " )
temp_stats . timestamp = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " server " )
temp_stats . servers_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
else if ( names [ index ] = = " clients " )
temp_stats . clients_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
else if ( names [ index ] = = " web " )
temp_stats . web_clients_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
else if ( names [ index ] = = " music " )
temp_stats . bots_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
else if ( names [ index ] = = " queries " )
temp_stats . queries_online = strtol ( values [ index ] . c_str ( ) , nullptr , 10 ) ;
2019-07-17 13:37:18 -04:00
}
2019-08-25 16:16:42 -04:00
/* because the query could only be oldest to newest */
while ( temp_stats . timestamp > current_timestamp ) {
assert ( _result - > record_count < allocated_records ) ; /* ensure we write to valid memory */
auto min_timestamp = current_timestamp - timeout ;
for ( auto & server : server_statistics ) {
auto & second = server . second ;
if ( second . timestamp < min_timestamp | | second . timestamp . time_since_epoch ( ) . count ( ) = = 0 )
continue ; /* last server request is too old to be counted */
info - > instance_online + + ;
info - > queries_online + = second . queries_online ;
info - > bots_online + = second . bots_online ;
info - > web_clients_online + = second . web_clients_online ;
info - > clients_online + = second . clients_online ;
info - > servers_online + = second . servers_online ;
}
info + + ;
_result - > record_count + + ;
current_timestamp + = interval ; /* lets gather for the next interval */
}
/* write the "new" statistic */
memcpy ( stats_ptr , & temp_stats , sizeof ( temp_stats ) ) ;
2019-07-17 13:37:18 -04:00
return 0 ;
} ) ;
if ( ! state ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not read license statistics ( " + state . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return { } ;
}
2019-08-25 16:16:42 -04:00
/* flush the last record */
do {
assert ( _result - > record_count < allocated_records ) ; /* ensure we write to valid memory */
2019-07-17 13:37:18 -04:00
2019-08-25 16:16:42 -04:00
auto min_timestamp = current_timestamp - timeout ;
2019-07-17 13:37:18 -04:00
for ( auto & server : server_statistics ) {
2019-08-25 16:16:42 -04:00
auto & second = server . second ;
if ( second . timestamp < min_timestamp )
continue ; /* last server request is too old to be counted */
info - > instance_online + + ;
info - > queries_online + = second . queries_online ;
info - > bots_online + = second . bots_online ;
info - > web_clients_online + = second . web_clients_online ;
info - > clients_online + = second . clients_online ;
info - > servers_online + = second . servers_online ;
2019-07-17 13:37:18 -04:00
}
2019-08-25 16:16:42 -04:00
info + + ;
_result - > record_count + + ;
2019-07-17 13:37:18 -04:00
current_timestamp + = interval ;
2019-08-25 16:16:42 -04:00
} while ( current_timestamp < end ) ;
2019-07-17 13:37:18 -04:00
return result ;
}
std : : deque < std : : unique_ptr < LicenseManager : : GlobalVersionsStatistic > > LicenseManager : : list_statistics_version ( const std : : chrono : : system_clock : : time_point & begin , const std : : chrono : : system_clock : : time_point & end , const std : : chrono : : milliseconds & interval ) {
map < std : : string , deque < unique_ptr < LicenseManager : : GlobalVersionsStatistic > > > server_statistics ;
auto timeout = hours ( 2 ) + minutes ( 10 ) ; //TODO timeout configurable?
2019-08-25 16:16:42 -04:00
auto state = command ( this - > database , " SELECT * FROM `history_version` WHERE `timestamp` >= :timestamp_start AND `timestamp` <= :timestamp_end ORDER BY `timestamp` ASC " ,
2019-07-17 13:37:18 -04:00
variable { " :timestamp_start " , duration_cast < milliseconds > ( begin . time_since_epoch ( ) - timeout ) . count ( ) } ,
variable { " :timestamp_end " , duration_cast < milliseconds > ( end . time_since_epoch ( ) ) . count ( ) }
) . query ( [ & server_statistics ] ( int columns , std : : string * values , std : : string * names ) {
size_t key_id = 0 ;
std : : string unique_id ;
auto info = make_unique < LicenseManager : : GlobalVersionsStatistic > ( ) ;
for ( int index = 0 ; index < columns ; index + + ) {
try {
if ( names [ index ] = = " keyId " )
key_id = stoull ( values [ index ] ) ;
else if ( names [ index ] = = " unique_id " )
unique_id = values [ index ] ;
else if ( names [ index ] = = " timestamp " )
info - > timestamp = system_clock : : time_point ( ) + milliseconds ( stoll ( values [ index ] ) ) ;
else if ( names [ index ] = = " version " )
info - > versions [ values [ index ] ] = 1 ;
} catch ( std : : exception & ex ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Failed to parse column " + names [ index ] + " => " + ex . what ( ) + " (Value: " + values [ index ] + " ) " ) ;
2019-07-17 13:37:18 -04:00
return 0 ;
}
}
if ( key_id = = 0 ) return 0 ;
server_statistics [ to_string ( key_id ) + " _ " + unique_id ] . push_back ( std : : move ( info ) ) ;
return 0 ;
} ) ;
if ( ! state ) {
2020-02-06 07:49:39 -05:00
logError ( LOG_GENERAL , " Could not read license statistics ( " + state . fmtStr ( ) + " ) " ) ;
2019-07-17 13:37:18 -04:00
return { } ;
}
std : : deque < std : : unique_ptr < LicenseManager : : GlobalVersionsStatistic > > result ;
system_clock : : time_point current_timestamp = begin ;
while ( current_timestamp < = end ) {
auto info = make_unique < LicenseManager : : GlobalVersionsStatistic > ( ) ;
info - > timestamp = current_timestamp ;
for ( auto & server : server_statistics ) {
while ( ! server . second . empty ( ) ) {
auto & first = * server . second . begin ( ) ;
if ( first - > timestamp > current_timestamp ) break ; //Entry within the future
if ( first - > timestamp + timeout < current_timestamp ) { //Entry within the past
server . second . pop_front ( ) ;
continue ;
}
if ( server . second . size ( ) > 1 ) {
auto & second = * ( server . second . begin ( ) + 1 ) ;
if ( second - > timestamp < = current_timestamp ) {
server . second . pop_front ( ) ; //The next entry is more up 2 date
continue ;
}
}
for ( const auto & entry : first - > versions )
info - > versions [ entry . first ] + = entry . second ;
break ;
}
}
result . push_back ( std : : move ( info ) ) ;
current_timestamp + = interval ;
}
return result ;
}