From 8073c4bd7b6d627268240c0d233611d3c9dfcc60 Mon Sep 17 00:00:00 2001 From: partysan Date: Sat, 19 Nov 2022 21:15:33 +0300 Subject: [PATCH] mfp seems like ready --- src/3proxy-eagle.pro | 6 +- src/dailycounter.cpp | 86 ----------- src/dailycounter.h | 40 ------ src/dbmanager.cpp | 334 +++++++++++++++++++++++++++++++++++++++++++ src/dbmanager.h | 78 ++++++++++ src/g.cpp | 23 +++ src/g.h | 12 +- src/main.cpp | 31 ++-- src/statistics.cpp | 90 +++++++----- src/statistics.h | 41 +++--- src/webpage.cpp | 15 +- src/webpage.h | 4 +- 12 files changed, 550 insertions(+), 210 deletions(-) delete mode 100644 src/dailycounter.cpp delete mode 100644 src/dailycounter.h create mode 100644 src/dbmanager.cpp create mode 100644 src/dbmanager.h diff --git a/src/3proxy-eagle.pro b/src/3proxy-eagle.pro index 5fe064b..1825ddb 100644 --- a/src/3proxy-eagle.pro +++ b/src/3proxy-eagle.pro @@ -1,11 +1,11 @@ QT -= gui -QT += network +QT += network sql CONFIG += c++17 console CONFIG -= app_bundle SOURCES += \ - dailycounter.cpp \ + dbmanager.cpp \ g.cpp \ httpdocument.cpp \ httpserver.cpp \ @@ -18,7 +18,7 @@ SOURCES += \ webserverbase.cpp HEADERS += \ - dailycounter.h \ + dbmanager.h \ g.h \ httpdocument.h \ httpserver.h \ diff --git a/src/dailycounter.cpp b/src/dailycounter.cpp deleted file mode 100644 index 420e3ff..0000000 --- a/src/dailycounter.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* -3proxy-eagle: Accumulate ethical 3proxy statistics with web interface. -Source code: https://notabug.org/acetone/3proxy-eagle. -Copyright (C) 2022, acetone - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "dailycounter.h" - -#include - -DailyCounter::DailyCounter() -{ -} - -quint64 DailyCounter::value() -{ - const auto factDay = getCurrentDay(); - if (day() != factDay) - { - m_day = factDay; - m_counter = 0; - } - return m_counter; -} - -QString DailyCounter::day() const -{ - return m_day; -} - -QJsonObject DailyCounter::serialize() -{ - QJsonObject me; - me["day"] = day(); - me["count"] = QString::number( value() ); - return me; -} - -void DailyCounter::deserialize(const QJsonObject &json) -{ - const QString serializedDay = json.value("day").toString(); - const QString factDay = getCurrentDay(); - - m_day = factDay; - auto serializedCounter = json.value("count").toString().toULongLong(); - if (serializedDay == factDay) - { - m_counter = serializedCounter; - } - else - { - m_counter = 0; - } -} - -void DailyCounter::increment(quint64 value) -{ - const auto factDay = getCurrentDay(); - if (day() == factDay) - { - m_counter += value; - } - else - { - m_day = factDay; - m_counter = value; - } -} - -QString DailyCounter::getCurrentDay() -{ - return QDateTime::currentDateTimeUtc().toString(Qt::DateFormat::RFC2822Date); -} diff --git a/src/dailycounter.h b/src/dailycounter.h deleted file mode 100644 index b968a83..0000000 --- a/src/dailycounter.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -3proxy-eagle: Accumulate ethical 3proxy statistics with web interface. -Source code: https://notabug.org/acetone/3proxy-eagle. -Copyright (C) 2022, acetone - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#pragma once - -#include -#include - -class DailyCounter -{ -public: - DailyCounter(); - quint64 value(); - QString day() const; - QJsonObject serialize(); - void deserialize(const QJsonObject& json); - void increment(quint64); - - static QString getCurrentDay(); - -private: - QString m_day; - std::atomic m_counter {0}; -}; diff --git a/src/dbmanager.cpp b/src/dbmanager.cpp new file mode 100644 index 0000000..ed750e3 --- /dev/null +++ b/src/dbmanager.cpp @@ -0,0 +1,334 @@ +/* +3proxy-eagle: Accumulate ethical 3proxy statistics with web interface. +Source code: https://notabug.org/acetone/3proxy-eagle. +Copyright (C) 2022, acetone + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "dbmanager.h" +#include "g.h" + +#include +#include +#include +#include +#include +#include +#include + +QMutex DBManager::m_mtx; +qint64 DBManager::m_dailyTopTableLastActualizeTimestamp = 0; +qint64 DBManager::m_dailyTrafficTableLastActualizeTimestamp = 0; + +constexpr int DB_VERSION = 1; + +DBManager::DBManager() : m_connName(g::randomString()) +{ + m_mtx.lock(); + m_db = QSqlDatabase::addDatabase("QSQLITE", m_connName); + m_db.setDatabaseName(g::p::WORKING_DIR+"/statistics.db"); + + if (not m_db.open()) + { + qFatal("Connection with database failed (maybe working directory not found or not writable)"); + } +} + +void DBManager::initAtStart() +{ + QSqlQuery query(m_db); + + bool appSettings = query.exec ("CREATE TABLE app_settings(param VARCHAR PRIMARY KEY, val VARCHAR)"); + if (appSettings) + { + setStandaloneVariable("db_version", QString::number(DB_VERSION)); + } + else + { + QString version = getStandaloneVariable("db_version"); + if (version != QString::number(DB_VERSION)) + { + qWarning().noquote() << "Your database version is [ " + (version.isEmpty() ? "UNKNOWN" : version) + " ]"; + qWarning().noquote() << "Required version is [ " + QString::number(DB_VERSION) + " ]"; + qWarning().noquote() << "You should delete old database and restart app. Old statistics will be lost. Database file path: " + g::p::WORKING_DIR + "/statistics.db"; + qFatal("Database version error"); + } + } + + query.exec ("CREATE TABLE IF NOT EXISTS total_history (name VARCHAR PRIMARY KEY, request_counter UNSIGNED BIGINT)"); + query.exec ("CREATE TABLE IF NOT EXISTS daily_history (name VARCHAR PRIMARY KEY, request_counter UNSIGNED BIGINT, date_of_record VARCHAR)"); + + // For traffic used: + // total_download_traffic + // total_upload_traffic + // daily_upload_traffic + daily_upload_traffic_date + // daily_download_traffic + daily_download_traffic_date + + query.exec ("VACUUM"); +} + +TopDestinationList DBManager::totalTop() const +{ + QSqlQuery query(m_db); + + TopDestinationList result; + + if (not query.exec("SELECT * FROM total_history ORDER BY request_counter DESC LIMIT " + QString::number(g::c::LAST_AND_TOP_LIST_SIZE))) + { + qCritical() << "Db query failed:" << query.lastError().text(); + } + else + { + while (query.next()) + { + result.push_back( {query.value(0).toString(), query.value(1).toULongLong()} ); + } + } + + return result; +} + +TopDestinationList DBManager::dailyTop() const +{ + QSqlQuery query(m_db); + + TopDestinationList result; + + if (not query.exec("SELECT * FROM daily_history ORDER BY request_counter DESC LIMIT " + QString::number(g::c::LAST_AND_TOP_LIST_SIZE))) + { + qCritical() << "Db query failed:" << query.lastError().text(); + } + else + { + const QString currd = currentDate(); + bool oldExists = false; + + while (query.next()) + { + if (query.value(2).toString() == currd) + { + result.push_back( {query.value(0).toString(), query.value(1).toULongLong()} ); + } + else + { + oldExists = true; + } + } + + if (oldExists) + { + actualizeDailyTopTable(); + } + } + + return result; +} + +quint64 DBManager::totalTopCount(const QString &dest) const +{ + QSqlQuery query(m_db); + + if (not query.exec("SELECT request_counter FROM total_history WHERE name = '" + escaped(dest) + "'")) + { + qCritical() << "Db query failed:" << query.lastError().text(); + return 0; + } + + if (query.next()) + { + return query.value(0).toULongLong(); + } + + return 0; +} + +quint64 DBManager::dailyTopCount(const QString &dest) const +{ + QSqlQuery query(m_db); + + if (not query.exec("SELECT * FROM daily_history WHERE name = '" + escaped(dest) + "'")) + { + qCritical() << "Db query failed:" << query.lastError().text(); + return 0; + } + + if (query.next()) + { + if (query.value(2).toString() != currentDate()) + { + actualizeDailyTopTable(); + return 0; + } + else + { + return query.value(1).toULongLong(); + } + } + + return 0; +} + +quint64 DBManager::incrementTotalTopCount(const QString &dest) +{ + QSqlQuery query(m_db); + quint64 newValue = totalTopCount(dest) + 1; + if (not query.exec("INSERT OR REPLACE INTO total_history(name, request_counter) VALUES ('"+ escaped(dest) +"', "+ QString::number(newValue) +")")) + { + qCritical() << "Db query failed:" << query.lastError().text(); + } + return newValue; +} + +quint64 DBManager::incrementDailyTopCount(const QString &dest) +{ + QSqlQuery query(m_db); + quint64 newValue = dailyTopCount(dest) + 1; + if (not query.exec("INSERT OR REPLACE INTO daily_history(name, request_counter, date_of_record) VALUES " + "('"+ escaped(dest) +"', '"+ QString::number(newValue) +"', '"+ currentDate() +"')")) + { + qCritical() << "Db query failed:" << query.lastError().text(); + } + return newValue; +} + +quint64 DBManager::incrementDailyUploadTraffic(quint64 bytes) +{ + quint64 newValue = dailyUploadTraffic() + bytes; + setStandaloneVariable("daily_upload_traffic", QString::number(newValue)); + return newValue; +} + +quint64 DBManager::incrementTotalUploadTraffic(quint64 bytes) +{ + quint64 newValue = totalUploadTraffic() + bytes; + setStandaloneVariable("total_upload_traffic", QString::number(newValue)); + return newValue; +} + +quint64 DBManager::incrementDailyDownloadTraffic(quint64 bytes) +{ + quint64 newValue = dailyDownloadTraffic() + bytes; + setStandaloneVariable("daily_download_traffic", QString::number(newValue)); + return newValue; +} + +quint64 DBManager::incrementTotalDownloadTraffic(quint64 bytes) +{ + quint64 newValue = totalDownloadTraffic() + bytes; + setStandaloneVariable("total_download_traffic", QString::number(newValue)); + return newValue; +} + +quint64 DBManager::dailyUploadTraffic() const +{ + const QString currd = currentDate(); + if (getStandaloneVariable("daily_upload_traffic_date") != currd) + { + setStandaloneVariable("daily_upload_traffic_date", currd); + setStandaloneVariable("daily_upload_traffic", "0"); + return 0; + } + return getStandaloneVariable("daily_upload_traffic").toULongLong(); +} + +quint64 DBManager::totalUploadTraffic() const +{ + return getStandaloneVariable("total_upload_traffic").toULongLong(); +} + +quint64 DBManager::dailyDownloadTraffic() const +{ + const QString currd = currentDate(); + if (getStandaloneVariable("daily_download_traffic_date") != currd) + { + setStandaloneVariable("daily_download_traffic_date", currd); + setStandaloneVariable("daily_download_traffic", "0"); + return 0; + } + return getStandaloneVariable("daily_download_traffic").toULongLong(); +} + +quint64 DBManager::totalDownloadTraffic() const +{ + return getStandaloneVariable("total_download_traffic").toULongLong(); +} + +DBManager::~DBManager() +{ + m_db.close(); + m_db = QSqlDatabase(); + QSqlDatabase::removeDatabase(m_connName); + m_mtx.unlock(); +} + +void DBManager::actualizeDailyTopTable() const +{ + qint64 started = QDateTime::currentMSecsSinceEpoch(); + if (started - m_dailyTopTableLastActualizeTimestamp < g::c::DB_DAILY_TOP_TABLE_ACTUALIZE_MINIMAL_INTERVAL_MS) + { + qDebug() << "Database daily history table actualizing rejected by minimal interval:" << g::c::DB_DAILY_TOP_TABLE_ACTUALIZE_MINIMAL_INTERVAL_MS << "ms"; + return; + } + + QSqlQuery query(m_db); + if (not query.exec("DELETE FROM daily_history WHERE date_of_record != '"+ currentDate() + "'")) + { + qCritical() << "Db query failed:" << query.lastError().text(); + } + + m_dailyTopTableLastActualizeTimestamp = QDateTime::currentMSecsSinceEpoch(); + qInfo() << "Daily history database table actualized in" << m_dailyTopTableLastActualizeTimestamp-started << "ms"; +} + +QString DBManager::currentDate() +{ + return QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd"); +} + +DBManager::ResultPair DBManager::setStandaloneVariable(const QString &key, const QString &value) const +{ + QSqlQuery query(m_db); + bool status = query.exec("INSERT OR REPLACE INTO app_settings(param, val) VALUES ('"+ escaped(key) +"', '"+ escaped(value) +"')"); + return status ? ResultPair(true) : ResultPair(false, query.lastError().text()); +} + +QString DBManager::getStandaloneVariable(const QString &key) const +{ + QSqlQuery query(m_db); + + if (not query.exec("SELECT val FROM app_settings WHERE param = '" + escaped(key) + "'")) + { + qCritical() << "Db query failed:" << query.lastError().text(); + return QString(); + } + + if (query.next()) + { + return query.value(0).toString(); + } + else + { + qDebug() << "Db query failed (empty result for " + key + "):" << query.lastError().text(); + return QString(); + } +} + +QString DBManager::escaped(const QString &str) +{ + QString result {str}; + static QRegularExpression rgx("[\\'\\;\\!\\-\\@\\#\\$\\%\\^\\&\\*\\(\\)\\`\\~\\+\\=\\\\/\\]\\[ ]"); + result.replace(rgx, "_"); + return result; +} diff --git a/src/dbmanager.h b/src/dbmanager.h new file mode 100644 index 0000000..42077e1 --- /dev/null +++ b/src/dbmanager.h @@ -0,0 +1,78 @@ +/* +3proxy-eagle: Accumulate ethical 3proxy statistics with web interface. +Source code: https://notabug.org/acetone/3proxy-eagle. +Copyright (C) 2022, acetone + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#pragma once + +#include "g.h" + +#include +#include +#include + +class DBManager +{ +public: + class ResultPair + { + public: + ResultPair (bool status, QString string = QString()) : m_str(string), m_bool(status) {} + bool status() const { return m_bool; } + QString string() const { return m_str; } + + private: + QString m_str; + bool m_bool; + }; + + DBManager(); + void initAtStart(); + + TopDestinationList totalTop() const; + TopDestinationList dailyTop() const; + quint64 totalTopCount(const QString& dest) const; + quint64 dailyTopCount(const QString& dest) const; + quint64 incrementTotalTopCount(const QString& dest); + quint64 incrementDailyTopCount(const QString& dest); + + quint64 incrementDailyUploadTraffic(quint64 bytes); + quint64 incrementTotalUploadTraffic(quint64 bytes); + quint64 incrementDailyDownloadTraffic(quint64 bytes); + quint64 incrementTotalDownloadTraffic(quint64 bytes); + quint64 dailyUploadTraffic() const; + quint64 totalUploadTraffic() const; + quint64 dailyDownloadTraffic() const; + quint64 totalDownloadTraffic() const; + + static QString currentDate(); + static QString escaped(const QString &str); + + ~DBManager(); + +private: + void actualizeDailyTopTable() const; + + ResultPair setStandaloneVariable(const QString& key, const QString& value) const; + QString getStandaloneVariable(const QString& key) const; + + const QString m_connName; + QSqlDatabase m_db; + static QMutex m_mtx; + static qint64 m_dailyTopTableLastActualizeTimestamp; + static qint64 m_dailyTrafficTableLastActualizeTimestamp; +}; diff --git a/src/g.cpp b/src/g.cpp index 95cf8bb..3dc4fca 100644 --- a/src/g.cpp +++ b/src/g.cpp @@ -24,6 +24,7 @@ along with this program. If not, see . #include #include #include +#include #include namespace g { @@ -32,6 +33,9 @@ namespace c { const QString SOFTWARE_NAME = "3proxy-eagle"; const QString SOFTWARE_VERSION = "0.0.1a"; const QString COPYRIGHT = "GPLv3 (c) acetone, 2022"; + const int LAST_AND_TOP_LIST_SIZE = 10; + const qint64 DB_DAILY_TOP_TABLE_ACTUALIZE_MINIMAL_INTERVAL_MS = 300000; // 5 min + const qint64 CACHE_ACTUALIZE_TOP_LISTS_FROM_DB_MINIMAL_INTERVAL_MS = 5000; // 5 sec const QString HA_PAGE_TITLE = "{{PAGE_TITLE}}"; const QString HA_CUSTOM_CSS = "{{CUSTOM_CSS}}"; const QString HA_DAILY_UPLOAD = "{{DAILY_UPLOAD}}"; @@ -188,6 +192,25 @@ QString getValue(const QString &string, const QString &key, GetValueType type) return result; } +QString randomString(int length) +{ + static const QString table + {"0123456789" + "abcdefghij" + "klmnkpqrst" + "uvwxyzABCD" + "EFGHIJKLMN" + "hPQRSTUVWX"}; + + static const int posLimit = table.size() - 1; + + QString value; + while(value.size() < length) + { + value += table[ QRandomGenerator::system()->bounded (0, posLimit) ]; + } + return value; +} QString hash(const QByteArray &data) { diff --git a/src/g.h b/src/g.h index e1fb8a3..4bc2396 100644 --- a/src/g.h +++ b/src/g.h @@ -19,10 +19,15 @@ along with this program. If not, see . #pragma once -#include "proxyinstanse.h" #include +#include +#include class QFile; +class ProxyInstanse; +class QThread; + +using TopDestinationList = QList>; namespace g /* for Global */ { @@ -30,6 +35,9 @@ namespace c /* for Constants */ { extern const QString SOFTWARE_NAME; extern const QString SOFTWARE_VERSION; extern const QString COPYRIGHT; + extern const int LAST_AND_TOP_LIST_SIZE; + extern const qint64 DB_DAILY_TOP_TABLE_ACTUALIZE_MINIMAL_INTERVAL_MS; + extern const qint64 CACHE_ACTUALIZE_TOP_LISTS_FROM_DB_MINIMAL_INTERVAL_MS; extern const QString HA_PAGE_TITLE; extern const QString HA_CUSTOM_CSS; extern const QString HA_DAILY_UPLOAD; @@ -50,7 +58,6 @@ namespace p /* for Parameters */ { extern QString WORKING_DIR; extern QStringList IGNORED_DESTINATIONS; extern QString SERVICE_TITLE; - extern uint LAST_AND_TOP_LIST_SIZE; extern QString BIND_TO_ADDRESS; extern quint16 BIND_TO_PORT; } // namespace p @@ -74,6 +81,7 @@ enum class GetValueType { // QString getValue() HttpHeader = 3 }; QString getValue(const QString &string, const QString &key, GetValueType type = GetValueType::Default); +QString randomString(int length = 10); QString hash(const QByteArray &data); QString hash(QFile file); diff --git a/src/main.cpp b/src/main.cpp index 4b06ded..a5c9a57 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,7 @@ along with this program. If not, see . #include "proxyinstanse.h" #include "httpserver.h" +#include "dbmanager.h" #include "g.h" #include @@ -46,7 +47,6 @@ void usage() " -i --instanse <3proxy>,<3proxy.cfg>\n" " -w --working-directory \n" " -t --service-title <3proxy-eagle>\n" - " -s --last-and-top-list-size <5>\n" " -I --ignored-destinations <[0.0.0.0],127.0.0.1>\n" " -a --bind-to-address <127.0.0.1>\n" " -p --bind-to-port <8161>\n" @@ -136,18 +136,6 @@ int main(int argc, char *argv[]) g::p::WORKING_DIR = value; } - else if ((key == "-s" or key == "--last-and-top-list-size") and not value.isEmpty()) - { - bool ok = false; - value.toUInt(&ok); - if (not ok) - { - qWarning() << "--last-and-top-list-size parsing failed, not a positive number:" << value; - continue; - } - g::p::LAST_AND_TOP_LIST_SIZE = value.toUInt(); - } - else if ((key == "-I" or key == "--ignored-destinations") and not value.isEmpty()) { g::p::IGNORED_DESTINATIONS = value.split(','); @@ -182,13 +170,12 @@ int main(int argc, char *argv[]) } } - qInfo().noquote() << "Instanses count:" << instanses.size(); - qInfo().noquote() << "Working directory:" << g::p::WORKING_DIR; - qInfo().noquote() << "Last and top dest list size:" << g::p::LAST_AND_TOP_LIST_SIZE; - qInfo().noquote() << "Ignored destinations:" << g::p::IGNORED_DESTINATIONS; - qInfo().noquote() << "Bind to address:" << g::p::BIND_TO_ADDRESS; - qInfo().noquote() << "Bind to port:" << g::p::BIND_TO_PORT; - qInfo() << ""; + if (instanses.isEmpty()) + { + qFatal("Instanses not defined. Read --help information"); + } + + DBManager().initAtStart(); for (const auto& pair: instanses) { @@ -203,6 +190,10 @@ int main(int argc, char *argv[]) g::instanses.push_back( {thread, pi} ); } + qInfo().noquote() << "Instanses count:" << instanses.size(); + qInfo().noquote() << "Working directory:" << g::p::WORKING_DIR; + qInfo().noquote() << "Ignored destinations:" << g::p::IGNORED_DESTINATIONS; + (new HttpServer)/*->killTheCapitalism()*/; return a.exec(); diff --git a/src/statistics.cpp b/src/statistics.cpp index 21c06a1..a54c315 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . */ #include "statistics.h" +#include "dbmanager.h" #include "g.h" #include @@ -25,68 +26,93 @@ along with this program. If not, see . #include #include #include +#include #include QStringList Statistics::m_lastDestinations; -qint64 Statistics::m_lastSerializeTimestamp = 0; -bool Statistics::m_deserialized = false; -DailyCounter Statistics::m_dailyUpload; -DailyCounter Statistics::m_dailyDownload; -quint64 Statistics::m_totalUpload = 0; -quint64 Statistics::m_totalDownload = 0; + +std::atomic Statistics::m_dailyUpload {0}; +std::atomic Statistics::m_dailyDownload {0}; +std::atomic Statistics::m_totalUpload {0}; +std::atomic Statistics::m_totalDownload {0}; + +TopDestinationList Statistics::m_dailyTopDestinations; +TopDestinationList Statistics::m_totalTopDestinations; + +bool Statistics::m_cacheUpdatedAfterLastReport = false; +qint64 Statistics::m_actualizeTopListsLastTimestamp = 0; void Statistics::report(const LogEvent& event) { - // mutex enabled in ProxyInstanse::reader() - - deserialize(); - if (g::p::IGNORED_DESTINATIONS.contains(event.dest)) { qDebug() << "Destination" << event.dest << "ignored"; return; } + DBManager db; + m_dailyDownload = db.incrementDailyDownloadTraffic(event.from); + m_dailyUpload = db.incrementDailyUploadTraffic(event.to); + m_totalDownload = db.incrementTotalDownloadTraffic(event.from); + m_totalUpload = db.incrementTotalUploadTraffic(event.to); + addToDestinationsList(event.dest); - incrementUpload(event.to); - incrementDownload(event.from); - // type not handled - serialize(); + m_cacheUpdatedAfterLastReport = false; } -void Statistics::incrementUpload(quint64 value) +const QStringList Statistics::lastDestinations() { - m_dailyUpload.increment(value); - m_totalUpload += value; + return m_lastDestinations; } -void Statistics::incrementDownload(quint64 value) +const TopDestinationList Statistics::dailyTopDestinations() { - m_dailyDownload.increment(value); - m_totalDownload += value; + if (not m_cacheUpdatedAfterLastReport) + { + actualizeTopLists(); + } + return m_dailyTopDestinations; +} + +const TopDestinationList Statistics::totalTopDestinations() +{ + return m_totalTopDestinations; } void Statistics::addToDestinationsList(const QString &dest) { - if (static_cast(lastDestinations().size()) >= g::p::LAST_AND_TOP_LIST_SIZE) + static QMutex mutex; + mutex.lock(); + if (lastDestinations().size() >= g::c::LAST_AND_TOP_LIST_SIZE) { m_lastDestinations.pop_back(); } m_lastDestinations.push_front(dest); + mutex.unlock(); - + DBManager db; + db.incrementTotalTopCount(dest); + db.incrementDailyTopCount(dest); } -void Statistics::serialize() +void Statistics::actualizeTopLists() { - QFile file(g::p::WORKING_DIR + "/statistics.json"); -} - -void Statistics::deserialize() -{ - if (m_deserialized) return; - - QFile file(g::p::WORKING_DIR + "/statistics.json"); - + static QMutex mutex; + QMutexLocker lock (&mutex); + + qint64 started = QDateTime::currentMSecsSinceEpoch(); + if (started - m_actualizeTopListsLastTimestamp < g::c::CACHE_ACTUALIZE_TOP_LISTS_FROM_DB_MINIMAL_INTERVAL_MS) + { + qDebug() << "Cache actualizing rejected by minimal interval:" << g::c::CACHE_ACTUALIZE_TOP_LISTS_FROM_DB_MINIMAL_INTERVAL_MS << "ms"; + return; + } + + DBManager db; + m_dailyTopDestinations = db.dailyTop(); + m_totalTopDestinations = db.totalTop(); + + m_cacheUpdatedAfterLastReport = true; + m_actualizeTopListsLastTimestamp = QDateTime::currentMSecsSinceEpoch(); + qInfo() << "Cache actualized in" << m_actualizeTopListsLastTimestamp-started << "ms"; } diff --git a/src/statistics.h b/src/statistics.h index a2e0695..876f52c 100644 --- a/src/statistics.h +++ b/src/statistics.h @@ -19,13 +19,13 @@ along with this program. If not, see . #pragma once -#include "dailycounter.h" +#include "g.h" #include struct LogEvent { - QString type; + QString type; // not handled QString dest; quint64 to = 0; quint64 from = 0; @@ -37,28 +37,31 @@ public: Statistics() = delete; static void report (const LogEvent& event); - static const QStringList lastDestinations() { return QStringList{"google.com"}; /*m_lastDestinations;*/ } - static const QStringList dailyTopDestinations() { return QStringList{"cloudflare.com", "yahoo.com"}; } - static const QStringList totalTopDestinations() { return QStringList{"domain.com", "top.org"}; } - static quint64 dailyUpload() { return 124; } - static quint64 totalUpload() { return 241298412; } - static quint64 dailyDownload() { return 21312312312412; } - static quint64 totalDownload() { return 23112; } + static const QStringList lastDestinations(); + + static const TopDestinationList dailyTopDestinations(); + static const TopDestinationList totalTopDestinations(); + + static quint64 dailyUpload() { return m_dailyUpload; } + static quint64 totalUpload() { return m_totalUpload; } + static quint64 dailyDownload() { return m_dailyDownload; } + static quint64 totalDownload() { return m_totalDownload; } private: - static void incrementUpload(quint64 value); - static void incrementDownload(quint64 value); static void addToDestinationsList(const QString& dest); - static void serialize(); - static void deserialize(); + static void actualizeTopLists(); static QStringList m_lastDestinations; - static qint64 m_lastSerializeTimestamp; - static bool m_deserialized; - static DailyCounter m_dailyUpload; - static DailyCounter m_dailyDownload; - static quint64 m_totalUpload; - static quint64 m_totalDownload; + static bool m_cacheUpdatedAfterLastReport; + static qint64 m_actualizeTopListsLastTimestamp; + + static std::atomic m_dailyUpload; + static std::atomic m_dailyDownload; + static std::atomic m_totalUpload; + static std::atomic m_totalDownload; + + static TopDestinationList m_dailyTopDestinations; + static TopDestinationList m_totalTopDestinations; }; diff --git a/src/webpage.cpp b/src/webpage.cpp index bc900ea..102e483 100644 --- a/src/webpage.cpp +++ b/src/webpage.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . */ #include "webpage.h" +#include "proxyinstanse.h" #include "g.h" #include "statistics.h" @@ -31,11 +32,11 @@ const QString WebPage::m_lastDestinationItem = "\ {{VALUE}}\n\ \n"; const QString WebPage::m_dailyTopDestinationItem = "\ -
  • \n\ +
  • \n\ {{VALUE}}\n\
  • \n"; const QString WebPage::m_totalTopDestinationItem = "\ -
  • \n\ +
  • \n\ {{VALUE}}\n\
  • \n"; @@ -143,14 +144,14 @@ void WebPage::topDestinations(QString &document) QString dailyTopList; for (const auto& dTop: Statistics::dailyTopDestinations()) { - dailyTopList += dailyTopDestinationItem(dTop); + dailyTopList += dailyTopDestinationItem(dTop.first, dTop.second); } document.replace(g::c::HA_DAILY_TOP_DESTINATIONS_LIST, dailyTopList); QString totalTopList; for (const auto& dTop: Statistics::totalTopDestinations()) { - totalTopList += totalTopDestinationItem(dTop); + totalTopList += totalTopDestinationItem(dTop.first, dTop.second); } document.replace(g::c::HA_TOTAL_TOP_DESTINATIONS_LIST, totalTopList); } @@ -230,17 +231,19 @@ QString WebPage::lastDestinationItem(const QString &destination) return result; } -QString WebPage::dailyTopDestinationItem(const QString &destination) +QString WebPage::dailyTopDestinationItem(const QString &destination, quint64 count) { QString result {m_dailyTopDestinationItem}; result.replace("{{VALUE}}", destination); + result.replace("{{COUNT}}", QString::number(count)); return result; } -QString WebPage::totalTopDestinationItem(const QString &destination) +QString WebPage::totalTopDestinationItem(const QString &destination, quint64 count) { QString result {m_totalTopDestinationItem}; result.replace("{{VALUE}}", destination); + result.replace("{{COUNT}}", QString::number(count)); return result; } diff --git a/src/webpage.h b/src/webpage.h index f317010..af6ecfb 100644 --- a/src/webpage.h +++ b/src/webpage.h @@ -39,8 +39,8 @@ private: static void copyright(QString& document); static QString lastDestinationItem(const QString& destination); - static QString dailyTopDestinationItem(const QString& destination); - static QString totalTopDestinationItem(const QString& destination); + static QString dailyTopDestinationItem(const QString& destination, quint64 count); + static QString totalTopDestinationItem(const QString& destination, quint64 count); static QString blockedDestinationItem(const QString& destination, const QStringList& addresses); static QPair bytesToHumanReadableString(quint64 bytes); // count, measure