chore: sync dev to master
Some checks failed
Build and Push Docker image / 🔧 Prepare Metadata (push) Has been cancelled
Build and Push Docker image / 🐳 Docker Build (${{ matrix.arch }}) (push) Has been cancelled
Build and Push Docker image / 🪟 Windows Build (amd64) (push) Has been cancelled
Build and Push Docker image / 🔗 Docker Manifest (push) Has been cancelled
Build and Push Docker image / 🚀 Release Artifacts (push) Has been cancelled
Some checks failed
Build and Push Docker image / 🔧 Prepare Metadata (push) Has been cancelled
Build and Push Docker image / 🐳 Docker Build (${{ matrix.arch }}) (push) Has been cancelled
Build and Push Docker image / 🪟 Windows Build (amd64) (push) Has been cancelled
Build and Push Docker image / 🔗 Docker Manifest (push) Has been cancelled
Build and Push Docker image / 🚀 Release Artifacts (push) Has been cancelled
This commit is contained in:
@@ -175,6 +175,8 @@ flush_interval=5
|
||||
;none records all countries as unknown.
|
||||
geo_provider=header
|
||||
country_headers=CF-IPCountry,X-Geo-Country,X-Vercel-IP-Country,CloudFront-Viewer-Country
|
||||
;Used for China regional statistics when visitor location headers are added by a trusted edge.
|
||||
china_region_headers=CF-Region-Code,cf-region-code,X-Geo-Subdivision
|
||||
;Optional Basic authentication for /dashboard and /dashboard/data.
|
||||
;Only applies when statistics enabled=true.
|
||||
;Missing or false keeps the dashboard password disabled.
|
||||
|
||||
@@ -95,6 +95,8 @@ statistics:
|
||||
# none records all countries as unknown.
|
||||
provider: header
|
||||
country_headers: ["CF-IPCountry", "X-Geo-Country", "X-Vercel-IP-Country", "CloudFront-Viewer-Country"]
|
||||
# Used for China regional statistics when visitor location headers are added by a trusted edge.
|
||||
china_region_headers: ["CF-Region-Code", "cf-region-code", "X-Geo-Subdivision"]
|
||||
dashboard_auth:
|
||||
# Optional Basic authentication for /dashboard and /dashboard/data.
|
||||
# Only applies when statistics.enabled is true.
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#ifndef CPPHTTPLIB_HTTPLIB_H
|
||||
#define CPPHTTPLIB_HTTPLIB_H
|
||||
|
||||
#define CPPHTTPLIB_VERSION "0.46.0"
|
||||
#define CPPHTTPLIB_VERSION_NUM "0x002e00"
|
||||
#define CPPHTTPLIB_VERSION "0.46.1"
|
||||
#define CPPHTTPLIB_VERSION_NUM "0x002e01"
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1030,10 +1030,10 @@ std::string page(Request &request, Response &response) {
|
||||
<footer>
|
||||
<span data-lang="en">SubConverter-Extended )html" +
|
||||
std::string(VERSION) +
|
||||
R"html( · <a href="/version">Version</a></span>
|
||||
R"html( · <a href="/version">Version</a> · Source Code: <a href="https://github.com/Aethersailor/SubConverter-Extended" target="_blank" rel="noopener noreferrer">GitHub</a> · License: <a href="https://www.gnu.org/licenses/gpl-3.0.html" target="_blank" rel="noopener noreferrer">GPL-3.0</a></span>
|
||||
<span data-lang="zh">SubConverter-Extended )html" +
|
||||
std::string(VERSION) +
|
||||
R"html( · <a href="/version">版本信息</a></span>
|
||||
R"html( · <a href="/version">版本信息</a> · 源代码:<a href="https://github.com/Aethersailor/SubConverter-Extended" target="_blank" rel="noopener noreferrer">GitHub</a> · 许可证:<a href="https://www.gnu.org/licenses/gpl-3.0.html" target="_blank" rel="noopener noreferrer">GPL-3.0</a></span>
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -708,6 +708,12 @@ void readYAMLConf(YAML::Node &node) {
|
||||
if (!country_headers.empty())
|
||||
global.statisticsCountryHeaders = country_headers;
|
||||
}
|
||||
if (stats["geo"]["china_region_headers"].IsSequence()) {
|
||||
string_array region_headers;
|
||||
stats["geo"]["china_region_headers"] >> region_headers;
|
||||
if (!region_headers.empty())
|
||||
global.statisticsChinaRegionHeaders = region_headers;
|
||||
}
|
||||
}
|
||||
if (stats["dashboard_auth"].IsDefined()) {
|
||||
YAML::Node auth = stats["dashboard_auth"];
|
||||
@@ -942,6 +948,10 @@ void readTOMLConf(toml::value &root) {
|
||||
section_statistics_geo, "country_headers", string_array{});
|
||||
if (!country_headers.empty())
|
||||
global.statisticsCountryHeaders = country_headers;
|
||||
string_array region_headers = toml::find_or<string_array>(
|
||||
section_statistics_geo, "china_region_headers", string_array{});
|
||||
if (!region_headers.empty())
|
||||
global.statisticsChinaRegionHeaders = region_headers;
|
||||
auto section_dashboard_auth =
|
||||
toml::find_or(section_statistics, "dashboard_auth",
|
||||
toml::value(toml::table()));
|
||||
@@ -978,6 +988,8 @@ void readConf() {
|
||||
global.statisticsCountryHeaders = {"CF-IPCountry", "X-Geo-Country",
|
||||
"X-Vercel-IP-Country",
|
||||
"CloudFront-Viewer-Country"};
|
||||
global.statisticsChinaRegionHeaders = {"CF-Region-Code", "cf-region-code",
|
||||
"X-Geo-Subdivision"};
|
||||
global.dashboardAuthEnabled = false;
|
||||
global.dashboardAuthUsername.clear();
|
||||
global.dashboardAuthPassword.clear();
|
||||
@@ -1273,6 +1285,18 @@ void readConf() {
|
||||
if (!country_headers.empty())
|
||||
global.statisticsCountryHeaders = country_headers;
|
||||
}
|
||||
if (ini.item_exist("china_region_headers")) {
|
||||
string_array region_headers =
|
||||
split(ini.get("china_region_headers"), ",");
|
||||
for (std::string &header : region_headers)
|
||||
header = trimWhitespace(header, true, true);
|
||||
region_headers.erase(
|
||||
std::remove_if(region_headers.begin(), region_headers.end(),
|
||||
[](const std::string &value) { return value.empty(); }),
|
||||
region_headers.end());
|
||||
if (!region_headers.empty())
|
||||
global.statisticsChinaRegionHeaders = region_headers;
|
||||
}
|
||||
ini.get_bool_if_exist("dashboard_auth_enabled",
|
||||
global.dashboardAuthEnabled);
|
||||
ini.get_if_exist("dashboard_auth_username",
|
||||
|
||||
@@ -90,6 +90,8 @@ struct Settings {
|
||||
string_array statisticsCountryHeaders = {
|
||||
"CF-IPCountry", "X-Geo-Country", "X-Vercel-IP-Country",
|
||||
"CloudFront-Viewer-Country"};
|
||||
string_array statisticsChinaRegionHeaders = {
|
||||
"CF-Region-Code", "cf-region-code", "X-Geo-Subdivision"};
|
||||
bool dashboardAuthEnabled = false;
|
||||
std::string dashboardAuthUsername, dashboardAuthPassword;
|
||||
int dashboardAuthMaxFailures = 5, dashboardAuthWindowSeconds = 300,
|
||||
|
||||
@@ -49,12 +49,14 @@ struct Bucket {
|
||||
int64_t minute = 0;
|
||||
Counters counters;
|
||||
std::vector<CountryBucketEntry> countries;
|
||||
std::vector<CountryBucketEntry> china_regions;
|
||||
};
|
||||
|
||||
struct DailyBucket {
|
||||
int64_t day = 0;
|
||||
Counters counters;
|
||||
std::vector<CountryBucketEntry> countries;
|
||||
std::vector<CountryBucketEntry> china_regions;
|
||||
};
|
||||
|
||||
struct SnapshotCountry {
|
||||
@@ -62,6 +64,11 @@ struct SnapshotCountry {
|
||||
CountryCounters counters;
|
||||
};
|
||||
|
||||
struct GeoLocation {
|
||||
std::string country_code;
|
||||
std::string china_region_code;
|
||||
};
|
||||
|
||||
struct State {
|
||||
bool initialized = false;
|
||||
bool dirty = false;
|
||||
@@ -76,6 +83,8 @@ struct State {
|
||||
Counters lifetime;
|
||||
std::map<std::string, CountryCounters> startup_countries;
|
||||
std::map<std::string, CountryCounters> lifetime_countries;
|
||||
std::map<std::string, CountryCounters> startup_china_regions;
|
||||
std::map<std::string, CountryCounters> lifetime_china_regions;
|
||||
std::array<Bucket, kBucketCount> buckets;
|
||||
std::array<DailyBucket, kDailyBucketCount> daily_buckets;
|
||||
};
|
||||
@@ -175,6 +184,30 @@ std::string normalizeCountryCode(std::string value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
bool validChinaRegionSuffix(const std::string &value) {
|
||||
static const std::array<const char *, 35> codes = {
|
||||
"AH", "BJ", "CQ", "FJ", "GD", "GS", "GX", "GZ", "HA", "HB",
|
||||
"HE", "HI", "HK", "HL", "HN", "JL", "JS", "JX", "LN", "MO",
|
||||
"NM", "NX", "QH", "SC", "SD", "SH", "SN", "SX", "TJ", "TW",
|
||||
"XJ", "XZ", "YN", "ZJ", "XX"};
|
||||
return std::any_of(codes.begin(), codes.end(), [&](const char *code) {
|
||||
return value == code;
|
||||
});
|
||||
}
|
||||
|
||||
std::string normalizeChinaRegionCode(std::string value) {
|
||||
value = toUpper(trimWhitespace(value, true, true));
|
||||
for (char &ch : value) {
|
||||
if (ch == '_')
|
||||
ch = '-';
|
||||
}
|
||||
if (value.rfind("CN-", 0) == 0)
|
||||
value = value.substr(3);
|
||||
if (!validChinaRegionSuffix(value))
|
||||
return "";
|
||||
return "CN-" + value;
|
||||
}
|
||||
|
||||
std::string countryFromHeaders(const Request &request) {
|
||||
if (toLower(global.statisticsGeoProvider) == "none")
|
||||
return "ZZ";
|
||||
@@ -190,6 +223,33 @@ std::string countryFromHeaders(const Request &request) {
|
||||
return "ZZ";
|
||||
}
|
||||
|
||||
std::string chinaRegionFromHeaders(const Request &request,
|
||||
const std::string &country) {
|
||||
if (country == "HK" || country == "MO" || country == "TW")
|
||||
return "CN-" + country;
|
||||
if (country != "CN")
|
||||
return "";
|
||||
|
||||
for (const std::string &header : global.statisticsChinaRegionHeaders) {
|
||||
auto iter = request.headers.find(header);
|
||||
if (iter == request.headers.end())
|
||||
continue;
|
||||
std::string code = normalizeChinaRegionCode(iter->second);
|
||||
if (!code.empty())
|
||||
return code;
|
||||
}
|
||||
return "CN-XX";
|
||||
}
|
||||
|
||||
GeoLocation geoLocationFromHeaders(const Request &request) {
|
||||
GeoLocation location;
|
||||
location.country_code = countryFromHeaders(request);
|
||||
if (toLower(global.statisticsGeoProvider) != "none")
|
||||
location.china_region_code =
|
||||
chinaRegionFromHeaders(request, location.country_code);
|
||||
return location;
|
||||
}
|
||||
|
||||
void addCounters(Counters &target, uint64_t requests, uint64_t rules) {
|
||||
target.subscription_requests += requests;
|
||||
target.rule_conversions += rules;
|
||||
@@ -254,6 +314,29 @@ std::vector<SnapshotCountry> countryWindowLocked(int64_t now_minute,
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<SnapshotCountry> chinaRegionWindowLocked(int64_t now_minute,
|
||||
int minutes) {
|
||||
std::map<std::string, CountryCounters> totals;
|
||||
if (!g_state)
|
||||
return {};
|
||||
int64_t earliest = now_minute - minutes + 1;
|
||||
for (const Bucket &bucket : g_state->buckets) {
|
||||
if (bucket.minute < earliest || bucket.minute > now_minute)
|
||||
continue;
|
||||
for (const CountryBucketEntry &entry : bucket.china_regions) {
|
||||
addCountryCounters(totals, entry.code,
|
||||
entry.counters.subscription_requests,
|
||||
entry.counters.rule_conversions);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SnapshotCountry> result;
|
||||
result.reserve(totals.size());
|
||||
for (const auto &entry : totals)
|
||||
result.push_back({entry.first, entry.second});
|
||||
return result;
|
||||
}
|
||||
|
||||
Counters dailyWindowCountersLocked(int64_t now_day, int days) {
|
||||
Counters result;
|
||||
if (!g_state)
|
||||
@@ -291,6 +374,29 @@ std::vector<SnapshotCountry> countryDailyWindowLocked(int64_t now_day,
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<SnapshotCountry> chinaRegionDailyWindowLocked(int64_t now_day,
|
||||
int days) {
|
||||
std::map<std::string, CountryCounters> totals;
|
||||
if (!g_state)
|
||||
return {};
|
||||
int64_t earliest = now_day - days + 1;
|
||||
for (const DailyBucket &bucket : g_state->daily_buckets) {
|
||||
if (bucket.day < earliest || bucket.day > now_day)
|
||||
continue;
|
||||
for (const CountryBucketEntry &entry : bucket.china_regions) {
|
||||
addCountryCounters(totals, entry.code,
|
||||
entry.counters.subscription_requests,
|
||||
entry.counters.rule_conversions);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SnapshotCountry> result;
|
||||
result.reserve(totals.size());
|
||||
for (const auto &entry : totals)
|
||||
result.push_back({entry.first, entry.second});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<SnapshotCountry>
|
||||
countrySnapshotLocked(const std::map<std::string, CountryCounters> &source) {
|
||||
std::vector<SnapshotCountry> result;
|
||||
@@ -374,6 +480,7 @@ void seedDailyBucketsFromMinuteBucketsLocked() {
|
||||
g_state->daily_buckets[index].day = day;
|
||||
g_state->daily_buckets[index].counters = Counters();
|
||||
g_state->daily_buckets[index].countries.clear();
|
||||
g_state->daily_buckets[index].china_regions.clear();
|
||||
}
|
||||
addCounters(g_state->daily_buckets[index].counters,
|
||||
bucket.counters.subscription_requests,
|
||||
@@ -383,6 +490,11 @@ void seedDailyBucketsFromMinuteBucketsLocked() {
|
||||
entry.counters.subscription_requests,
|
||||
entry.counters.rule_conversions);
|
||||
}
|
||||
for (const CountryBucketEntry &entry : bucket.china_regions) {
|
||||
addCountryCounters(g_state->daily_buckets[index].china_regions,
|
||||
entry.code, entry.counters.subscription_requests,
|
||||
entry.counters.rule_conversions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,7 +518,7 @@ void loadLocked() {
|
||||
try {
|
||||
json root = json::parse(content);
|
||||
int schema = root.value("schema", 1);
|
||||
if (schema < 1 || schema > 3)
|
||||
if (schema < 1 || schema > 4)
|
||||
return;
|
||||
|
||||
g_state->last_flush = root.value("updated_at", 0LL);
|
||||
@@ -436,6 +548,20 @@ void loadLocked() {
|
||||
g_state->lifetime_countries[code] = counters;
|
||||
}
|
||||
|
||||
auto china_regions = root.value("china_regions", json::object());
|
||||
for (auto iter = china_regions.begin(); iter != china_regions.end();
|
||||
++iter) {
|
||||
std::string code = normalizeChinaRegionCode(iter.key());
|
||||
if (code.empty())
|
||||
continue;
|
||||
CountryCounters counters;
|
||||
counters.subscription_requests =
|
||||
iter.value().value("subscription_requests", 0ULL);
|
||||
counters.rule_conversions = iter.value().value("rule_conversions", 0ULL);
|
||||
if (counters.subscription_requests || counters.rule_conversions)
|
||||
g_state->lifetime_china_regions[code] = counters;
|
||||
}
|
||||
|
||||
auto buckets = root.value("buckets", json::array());
|
||||
for (const auto &item : buckets) {
|
||||
int64_t minute = item.value("minute", 0LL);
|
||||
@@ -448,6 +574,7 @@ void loadLocked() {
|
||||
g_state->buckets[index].counters.rule_conversions =
|
||||
item.value("rule_conversions", 0ULL);
|
||||
g_state->buckets[index].countries.clear();
|
||||
g_state->buckets[index].china_regions.clear();
|
||||
|
||||
auto country_items = item.value("countries", json::array());
|
||||
for (const auto &country_item : country_items) {
|
||||
@@ -460,6 +587,20 @@ void loadLocked() {
|
||||
addCountryCounters(g_state->buckets[index].countries, code, requests,
|
||||
rules);
|
||||
}
|
||||
|
||||
auto region_items = item.value("china_regions", json::array());
|
||||
for (const auto ®ion_item : region_items) {
|
||||
std::string code =
|
||||
normalizeChinaRegionCode(region_item.value("code", ""));
|
||||
if (code.empty())
|
||||
continue;
|
||||
uint64_t requests =
|
||||
region_item.value("subscription_requests", 0ULL);
|
||||
uint64_t rules = region_item.value("rule_conversions", 0ULL);
|
||||
if (requests || rules)
|
||||
addCountryCounters(g_state->buckets[index].china_regions, code,
|
||||
requests, rules);
|
||||
}
|
||||
}
|
||||
|
||||
bool loaded_daily_buckets = false;
|
||||
@@ -475,6 +616,7 @@ void loadLocked() {
|
||||
g_state->daily_buckets[index].counters.rule_conversions =
|
||||
item.value("rule_conversions", 0ULL);
|
||||
g_state->daily_buckets[index].countries.clear();
|
||||
g_state->daily_buckets[index].china_regions.clear();
|
||||
|
||||
auto country_items = item.value("countries", json::array());
|
||||
for (const auto &country_item : country_items) {
|
||||
@@ -487,6 +629,19 @@ void loadLocked() {
|
||||
addCountryCounters(g_state->daily_buckets[index].countries, code,
|
||||
requests, rules);
|
||||
}
|
||||
auto region_items = item.value("china_regions", json::array());
|
||||
for (const auto ®ion_item : region_items) {
|
||||
std::string code =
|
||||
normalizeChinaRegionCode(region_item.value("code", ""));
|
||||
if (code.empty())
|
||||
continue;
|
||||
uint64_t requests =
|
||||
region_item.value("subscription_requests", 0ULL);
|
||||
uint64_t rules = region_item.value("rule_conversions", 0ULL);
|
||||
if (requests || rules)
|
||||
addCountryCounters(g_state->daily_buckets[index].china_regions, code,
|
||||
requests, rules);
|
||||
}
|
||||
if (g_state->daily_buckets[index].counters.subscription_requests ||
|
||||
g_state->daily_buckets[index].counters.rule_conversions)
|
||||
loaded_daily_buckets = true;
|
||||
@@ -513,7 +668,7 @@ bool flushLocked(bool stopping, int64_t now) {
|
||||
g_state->last_stopped_at = now;
|
||||
|
||||
json root;
|
||||
root["schema"] = 3;
|
||||
root["schema"] = 4;
|
||||
root["updated_at"] = now;
|
||||
root["runtime"] = {
|
||||
{"first_started_at", g_state->first_started_at},
|
||||
@@ -525,6 +680,8 @@ bool flushLocked(bool stopping, int64_t now) {
|
||||
{"last_stopped_at", g_state->last_stopped_at}};
|
||||
root["lifetime"] = countersJson(g_state->lifetime);
|
||||
root["countries"] = countriesObjectJson(g_state->lifetime_countries);
|
||||
root["china_regions"] =
|
||||
countriesObjectJson(g_state->lifetime_china_regions);
|
||||
|
||||
json buckets = json::array();
|
||||
for (const Bucket &bucket : g_state->buckets) {
|
||||
@@ -544,12 +701,24 @@ bool flushLocked(bool stopping, int64_t now) {
|
||||
{"rule_conversions",
|
||||
entry.counters.rule_conversions}});
|
||||
}
|
||||
json china_regions = json::array();
|
||||
for (const CountryBucketEntry &entry : bucket.china_regions) {
|
||||
if (!entry.counters.subscription_requests &&
|
||||
!entry.counters.rule_conversions)
|
||||
continue;
|
||||
china_regions.push_back({{"code", entry.code},
|
||||
{"subscription_requests",
|
||||
entry.counters.subscription_requests},
|
||||
{"rule_conversions",
|
||||
entry.counters.rule_conversions}});
|
||||
}
|
||||
buckets.push_back({{"minute", bucket.minute},
|
||||
{"subscription_requests",
|
||||
bucket.counters.subscription_requests},
|
||||
{"rule_conversions",
|
||||
bucket.counters.rule_conversions},
|
||||
{"countries", countries}});
|
||||
{"countries", countries},
|
||||
{"china_regions", china_regions}});
|
||||
}
|
||||
root["buckets"] = buckets;
|
||||
|
||||
@@ -571,12 +740,24 @@ bool flushLocked(bool stopping, int64_t now) {
|
||||
{"rule_conversions",
|
||||
entry.counters.rule_conversions}});
|
||||
}
|
||||
json china_regions = json::array();
|
||||
for (const CountryBucketEntry &entry : bucket.china_regions) {
|
||||
if (!entry.counters.subscription_requests &&
|
||||
!entry.counters.rule_conversions)
|
||||
continue;
|
||||
china_regions.push_back({{"code", entry.code},
|
||||
{"subscription_requests",
|
||||
entry.counters.subscription_requests},
|
||||
{"rule_conversions",
|
||||
entry.counters.rule_conversions}});
|
||||
}
|
||||
daily_buckets.push_back({{"day", bucket.day},
|
||||
{"subscription_requests",
|
||||
bucket.counters.subscription_requests},
|
||||
{"rule_conversions",
|
||||
bucket.counters.rule_conversions},
|
||||
{"countries", countries}});
|
||||
{"countries", countries},
|
||||
{"china_regions", china_regions}});
|
||||
}
|
||||
root["daily_buckets"] = daily_buckets;
|
||||
|
||||
@@ -662,7 +843,7 @@ void recordSubscriptionConversion(const Request &request,
|
||||
int64_t now = nowSeconds();
|
||||
int64_t minute = now / 60;
|
||||
int64_t day = now / (24 * 60 * 60);
|
||||
std::string country = countryFromHeaders(request);
|
||||
GeoLocation location = geoLocationFromHeaders(request);
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
if (!g_state || !g_state->initialized)
|
||||
@@ -670,30 +851,45 @@ void recordSubscriptionConversion(const Request &request,
|
||||
|
||||
addCounters(g_state->startup, 1, rule_conversions);
|
||||
addCounters(g_state->lifetime, 1, rule_conversions);
|
||||
addCountryCounters(g_state->startup_countries, country, 1, rule_conversions);
|
||||
addCountryCounters(g_state->lifetime_countries, country, 1,
|
||||
addCountryCounters(g_state->startup_countries, location.country_code, 1,
|
||||
rule_conversions);
|
||||
addCountryCounters(g_state->lifetime_countries, location.country_code, 1,
|
||||
rule_conversions);
|
||||
if (!location.china_region_code.empty()) {
|
||||
addCountryCounters(g_state->startup_china_regions,
|
||||
location.china_region_code, 1, rule_conversions);
|
||||
addCountryCounters(g_state->lifetime_china_regions,
|
||||
location.china_region_code, 1, rule_conversions);
|
||||
}
|
||||
|
||||
size_t index = static_cast<size_t>(minute % kBucketCount);
|
||||
if (g_state->buckets[index].minute != minute) {
|
||||
g_state->buckets[index].minute = minute;
|
||||
g_state->buckets[index].counters = Counters();
|
||||
g_state->buckets[index].countries.clear();
|
||||
g_state->buckets[index].china_regions.clear();
|
||||
}
|
||||
addCounters(g_state->buckets[index].counters, 1, rule_conversions);
|
||||
addCountryCounters(g_state->buckets[index].countries, country, 1,
|
||||
rule_conversions);
|
||||
addCountryCounters(g_state->buckets[index].countries, location.country_code,
|
||||
1, rule_conversions);
|
||||
if (!location.china_region_code.empty())
|
||||
addCountryCounters(g_state->buckets[index].china_regions,
|
||||
location.china_region_code, 1, rule_conversions);
|
||||
|
||||
size_t daily_index = static_cast<size_t>(day % kDailyBucketCount);
|
||||
if (g_state->daily_buckets[daily_index].day != day) {
|
||||
g_state->daily_buckets[daily_index].day = day;
|
||||
g_state->daily_buckets[daily_index].counters = Counters();
|
||||
g_state->daily_buckets[daily_index].countries.clear();
|
||||
g_state->daily_buckets[daily_index].china_regions.clear();
|
||||
}
|
||||
addCounters(g_state->daily_buckets[daily_index].counters, 1,
|
||||
rule_conversions);
|
||||
addCountryCounters(g_state->daily_buckets[daily_index].countries, country, 1,
|
||||
rule_conversions);
|
||||
addCountryCounters(g_state->daily_buckets[daily_index].countries,
|
||||
location.country_code, 1, rule_conversions);
|
||||
if (!location.china_region_code.empty())
|
||||
addCountryCounters(g_state->daily_buckets[daily_index].china_regions,
|
||||
location.china_region_code, 1, rule_conversions);
|
||||
|
||||
g_state->dirty = true;
|
||||
}
|
||||
@@ -758,6 +954,30 @@ std::string dashboardData(RESPONSE_CALLBACK_ARGS) {
|
||||
root["country_windows"] = country_windows;
|
||||
root["countries"] = country_windows["lifetime"];
|
||||
|
||||
json china_region_windows = json::object();
|
||||
china_region_windows["startup"] =
|
||||
countriesJson(g_state
|
||||
? countrySnapshotLocked(g_state->startup_china_regions)
|
||||
: std::vector<SnapshotCountry>());
|
||||
china_region_windows["hour"] =
|
||||
countriesJson(chinaRegionWindowLocked(now_minute, 60));
|
||||
china_region_windows["day"] =
|
||||
countriesJson(chinaRegionWindowLocked(now_minute, 24 * 60));
|
||||
china_region_windows["seven_days"] =
|
||||
countriesJson(chinaRegionWindowLocked(now_minute, 7 * 24 * 60));
|
||||
china_region_windows["thirty_days"] =
|
||||
countriesJson(chinaRegionWindowLocked(now_minute, 30 * 24 * 60));
|
||||
china_region_windows["half_year"] =
|
||||
countriesJson(chinaRegionDailyWindowLocked(now_day, 183));
|
||||
china_region_windows["year"] =
|
||||
countriesJson(chinaRegionDailyWindowLocked(now_day, 365));
|
||||
china_region_windows["lifetime"] =
|
||||
countriesJson(g_state ? countrySnapshotLocked(
|
||||
g_state->lifetime_china_regions)
|
||||
: std::vector<SnapshotCountry>());
|
||||
root["china_region_windows"] = china_region_windows;
|
||||
root["china_regions"] = china_region_windows["lifetime"];
|
||||
|
||||
json series = json::array();
|
||||
std::vector<Counters> hourly = hourlySeriesLocked(now_minute, 24);
|
||||
int64_t current_hour = now_minute / 60;
|
||||
|
||||
Reference in New Issue
Block a user