Add /admin/api/stats/database/overTime/history - this allows users to select a different time interval on the dash board than only "last 24 hours".

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER 2019-12-05 23:05:44 +00:00
parent 0f7ead8663
commit f2412873b8
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
4 changed files with 182 additions and 1 deletions

View File

@ -17,7 +17,7 @@ DNSMASQ_OPTS = -DHAVE_DNSSEC -DHAVE_DNSSEC_STATIC -DHAVE_IDN
FTL_DEPS = *.h database/*.h api/*.h version.h
FTL_DB_OBJ = database/common.o database/query-table.o database/network-table.o database/gravity-db.o database/database-thread.o \
database/sqlite3-ext.o database/message-table.o
FTL_API_OBJ = api/http-common.o api/routes.o api/ftl.o api/stats.o api/dns.o api/version.o api/auth.o api/settings.o
FTL_API_OBJ = api/http-common.o api/routes.o api/ftl.o api/stats.o api/dns.o api/version.o api/auth.o api/settings.o api/stats_database.o
FTL_OBJ = $(FTL_DB_OBJ) $(FTL_API_OBJ) main.o memory.o log.o daemon.o datastructure.o signals.o files.o setupVars.o args.o gc.o config.o dnsmasq_interface.o resolve.o regex.o shmem.o capabilities.o overTime.o timers.o vector.o
DNSMASQ_DEPS = config.h dhcp-protocol.h dns-protocol.h radv-protocol.h dhcp6-protocol.h dnsmasq.h ip6addr.h metrics.h ../dnsmasq_interface.h

View File

@ -107,6 +107,10 @@ int api_handler(struct mg_connection *conn, void *ignored)
{
ret = api_stats_recentblocked(conn);
}
else if(startsWith("/api/stats/database/overTime/history", request->local_uri))
{
ret = api_stats_database_overTime_history(conn);
}
/******************************** api/version ****************************/
else if(startsWith("/api/version", request->local_uri))
{

View File

@ -27,6 +27,9 @@ int api_stats_top_clients(bool blocked, struct mg_connection *conn);
int api_stats_history(struct mg_connection *conn);
int api_stats_recentblocked(struct mg_connection *conn);
// Statistics methods (database)
int api_stats_database_overTime_history(struct mg_connection *conn);
// FTL methods
int api_ftl_clientIP(struct mg_connection *conn);
int api_ftl_dnsmasq_log(struct mg_connection *conn);

174
src/api/stats_database.c Normal file
View File

@ -0,0 +1,174 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2019 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* FTL Engine
* API database statistics implementation
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
#include "FTL.h"
#include "http-common.h"
#include "routes.h"
#include "json_macros.h"
#include "shmem.h"
#include "datastructure.h"
// logg()
#include "log.h"
// FTL_db
#include "../database/common.h"
int api_stats_database_overTime_history(struct mg_connection *conn)
{
int from = 0, until = 0;
const struct mg_request_info *request = mg_get_request_info(conn);
if(request->query_string != NULL)
{
int num;
if((num = get_int_var(request->query_string, "from")) > 0)
from = num;
if((num = get_int_var(request->query_string, "until")) > 0)
until = num;
}
// Check if we received the required information
if(from == 0 || until == 0)
{
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 400,
"bad_request",
"You need to specify both \"from\" and \"until\" in the request.",
json);
}
// Unlock shared memory (DNS resolver can continue to work while we're preforming database queries)
unlock_shm();
// Open the database (this also locks the database)
dbopen();
const int interval = 600;
// Build SQL string
const char *querystr = "SELECT (timestamp/:interval)*:interval interval,status,COUNT(*) FROM queries "
"WHERE (status != 0) AND timestamp >= :from AND timestamp <= :until "
"GROUP by interval,status ORDER by interval";
// Prepare SQLite statement
sqlite3_stmt *stmt;
int rc = sqlite3_prepare_v2(FTL_db, querystr, -1, &stmt, NULL);
if( rc != SQLITE_OK ){
logg("api_stats_database_overTime_history() - SQL error prepare (%i): %s",
rc, sqlite3_errmsg(FTL_db));
return false;
}
// Bind interval to prepared statement
if((rc = sqlite3_bind_int(stmt, 1, interval)) != SQLITE_OK)
{
logg("api_stats_database_overTime_history(): Failed to bind interval (error %d) - %s",
rc, sqlite3_errmsg(FTL_db));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
"internal_error",
"Failed to bind interval",
json);
}
// Bind from to prepared statement
if((rc = sqlite3_bind_int(stmt, 2, from)) != SQLITE_OK)
{
logg("api_stats_database_overTime_history(): Failed to bind from (error %d) - %s",
rc, sqlite3_errmsg(FTL_db));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
"internal_error",
"Failed to bind from",
json);
}
// Bind until to prepared statement
if((rc = sqlite3_bind_int(stmt, 3, until)) != SQLITE_OK)
{
logg("api_stats_database_overTime_history(): Failed to bind until (error %d) - %s",
rc, sqlite3_errmsg(FTL_db));
sqlite3_reset(stmt);
sqlite3_finalize(stmt);
cJSON *json = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(json, "from", from);
JSON_OBJ_ADD_NUMBER(json, "until", until);
return send_json_error(conn, 500,
"internal_error",
"Failed to bind until",
json);
}
// Loop over and accumulate results
cJSON *json = JSON_NEW_ARRAY();
cJSON *item = NULL;
int previous_timestamp = 0, blocked = 0, total = 0;
while((rc = sqlite3_step(stmt)) == SQLITE_ROW)
{
const int timestamp = sqlite3_column_int(stmt, 0);
// Begin new array item for each new timestamp
if(timestamp != previous_timestamp)
{
previous_timestamp = timestamp;
if(item != NULL)
{
JSON_OBJ_ADD_NUMBER(item, "total_queries", total);
total = 0;
JSON_OBJ_ADD_NUMBER(item, "blocked_queries", blocked);
blocked = 0;
JSON_ARRAY_ADD_ITEM(json, item);
}
item = JSON_NEW_OBJ();
JSON_OBJ_ADD_NUMBER(item, "timestamp", timestamp);
}
const int status = sqlite3_column_int(stmt, 1);
const int count = sqlite3_column_int(stmt, 2);
// Always add to total count
total += count;
// Add to blocked count if this is the result for a blocked status
switch (status)
{
case QUERY_GRAVITY:
case QUERY_REGEX:
case QUERY_BLACKLIST:
case QUERY_EXTERNAL_BLOCKED_IP:
case QUERY_EXTERNAL_BLOCKED_NULL:
case QUERY_EXTERNAL_BLOCKED_NXRA:
blocked += count;
break;
default:
break;
}
}
// Finalize statement and close (= unlock) database connection
sqlite3_finalize(stmt);
dbclose();
// Re-lock shared memory before returning back to router subroutine
lock_shm();
JSON_SEND_OBJECT(json);
}