mfp seems like ready
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dailycounter.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
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<quint64> m_counter {0};
|
||||
};
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dbmanager.h"
|
||||
#include "g.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlResult>
|
||||
#include <QSqlError>
|
||||
#include <QSqlRecord>
|
||||
#include <QDateTime>
|
||||
#include <QRegularExpression>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "g.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QSqlDatabase>
|
||||
#include <QMutex>
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -24,6 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <QCryptographicHash>
|
||||
#include <QMutex>
|
||||
#include <QFile>
|
||||
#include <QRandomGenerator>
|
||||
#include <iostream>
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -19,10 +19,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "proxyinstanse.h"
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
|
||||
class QFile;
|
||||
class ProxyInstanse;
|
||||
class QThread;
|
||||
|
||||
using TopDestinationList = QList<QPair<QString, quint64>>;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
+11
-20
@@ -19,6 +19,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "proxyinstanse.h"
|
||||
#include "httpserver.h"
|
||||
#include "dbmanager.h"
|
||||
#include "g.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
@@ -46,7 +47,6 @@ void usage()
|
||||
" -i --instanse <3proxy>,<3proxy.cfg>\n"
|
||||
" -w --working-directory <data>\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();
|
||||
|
||||
+58
-32
@@ -18,6 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "statistics.h"
|
||||
#include "dbmanager.h"
|
||||
#include "g.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
@@ -25,68 +26,93 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
#include <QDebug>
|
||||
#include <QMutex>
|
||||
#include <QFile>
|
||||
|
||||
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<quint64> Statistics::m_dailyUpload {0};
|
||||
std::atomic<quint64> Statistics::m_dailyDownload {0};
|
||||
std::atomic<quint64> Statistics::m_totalUpload {0};
|
||||
std::atomic<quint64> 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<uint>(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";
|
||||
}
|
||||
|
||||
+22
-19
@@ -19,13 +19,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dailycounter.h"
|
||||
#include "g.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
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<quint64> m_dailyUpload;
|
||||
static std::atomic<quint64> m_dailyDownload;
|
||||
static std::atomic<quint64> m_totalUpload;
|
||||
static std::atomic<quint64> m_totalDownload;
|
||||
|
||||
static TopDestinationList m_dailyTopDestinations;
|
||||
static TopDestinationList m_totalTopDestinations;
|
||||
};
|
||||
|
||||
|
||||
+9
-6
@@ -18,6 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "webpage.h"
|
||||
#include "proxyinstanse.h"
|
||||
#include "g.h"
|
||||
#include "statistics.h"
|
||||
|
||||
@@ -31,11 +32,11 @@ const QString WebPage::m_lastDestinationItem = "\
|
||||
{{VALUE}}\n\
|
||||
</li>\n";
|
||||
const QString WebPage::m_dailyTopDestinationItem = "\
|
||||
<li class=\"topDestinations__item\" title=\"{{VALUE}}\">\n\
|
||||
<li class=\"topDestinations__item\" title=\"{{COUNT}}\">\n\
|
||||
{{VALUE}}\n\
|
||||
</li>\n";
|
||||
const QString WebPage::m_totalTopDestinationItem = "\
|
||||
<li class=\"topDestinations__item\" title=\"{{VALUE}}\">\n\
|
||||
<li class=\"topDestinations__item\" title=\"{{COUNT}}\">\n\
|
||||
{{VALUE}}\n\
|
||||
</li>\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;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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<QString, QString> bytesToHumanReadableString(quint64 bytes); // count, measure
|
||||
|
||||
|
||||
Reference in New Issue
Block a user