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