修复判断入口文件ip_check.php以及新增复制配置选项
This commit is contained in:
+40
-4
@@ -35,15 +35,25 @@ if (!empty($_REQUEST["action"]) && $_REQUEST["action"] == "delUrlArgs") {
|
|||||||
|
|
||||||
// 确定当前配置上下文(供表单回显用)
|
// 确定当前配置上下文(供表单回显用)
|
||||||
$isNewConfigDraft = false;
|
$isNewConfigDraft = false;
|
||||||
|
$configCopySource = '';
|
||||||
|
$copyFrom = dash_sanitize_config_name($_REQUEST['copy_from'] ?? '');
|
||||||
if (!empty($_REQUEST["name"])) {
|
if (!empty($_REQUEST["name"])) {
|
||||||
$check_name = trim($_REQUEST["name"]);
|
$check_name = trim($_REQUEST["name"]);
|
||||||
$config_name = str_replace(".php", "", $check_name);
|
$config_name = str_replace(".php", "", $check_name);
|
||||||
$config_file = "check_config/" . $config_name . "_config.php";
|
$config_file = "check_config/" . $config_name . "_config.php";
|
||||||
if (!file_exists($config_file)) {
|
if (!file_exists($config_file)) {
|
||||||
// 新建或未保存的配置:仅回显 index 模板,不在此写盘
|
|
||||||
$isNewConfigDraft = true;
|
$isNewConfigDraft = true;
|
||||||
|
$sourceFile = ($copyFrom !== '') ? "check_config/{$copyFrom}_config.php" : '';
|
||||||
|
if ($copyFrom !== '' && is_file($sourceFile)) {
|
||||||
|
$config_file = $sourceFile;
|
||||||
|
$configCopySource = $copyFrom;
|
||||||
|
if ($check_name === 'new_config' || $check_name === '') {
|
||||||
|
$check_name = dash_suggest_copy_config_name($copyFrom);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
$config_file = "check_config/index_config.php";
|
$config_file = "check_config/index_config.php";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$check_name = "index";
|
$check_name = "index";
|
||||||
$config_file = "check_config/index_config.php";
|
$config_file = "check_config/index_config.php";
|
||||||
@@ -435,6 +445,31 @@ function dash_check_redis() {
|
|||||||
} catch (Throwable $e) { return false; }
|
} catch (Throwable $e) { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dash_sanitize_config_name($name)
|
||||||
|
{
|
||||||
|
$name = trim(str_replace('.php', '', (string) $name));
|
||||||
|
if ($name === '' || !preg_match('/^[a-zA-Z0-9_-]+$/', $name)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dash_suggest_copy_config_name($source)
|
||||||
|
{
|
||||||
|
$source = dash_sanitize_config_name($source);
|
||||||
|
if ($source === '') {
|
||||||
|
return 'new_config';
|
||||||
|
}
|
||||||
|
$base = $source . '_copy';
|
||||||
|
$candidate = $base;
|
||||||
|
$suffix = 2;
|
||||||
|
while (file_exists(__DIR__ . "/check_config/{$candidate}_config.php")) {
|
||||||
|
$candidate = $base . $suffix;
|
||||||
|
$suffix++;
|
||||||
|
}
|
||||||
|
return $candidate;
|
||||||
|
}
|
||||||
|
|
||||||
function dash_cfg_get($raw, $name)
|
function dash_cfg_get($raw, $name)
|
||||||
{
|
{
|
||||||
if (preg_match("/define\('" . preg_quote($name, '/') . "',\s*'([^']*)'\)/", $raw, $m)) {
|
if (preg_match("/define\('" . preg_quote($name, '/') . "',\s*'([^']*)'\)/", $raw, $m)) {
|
||||||
@@ -662,7 +697,8 @@ $_d = function($name, $default) { return defined($name) ? constant($name) : $def
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-btn-group">
|
<div class="action-btn-group">
|
||||||
<a href="<?php echo $current_name . '?action=edit_config&name=' . $pf; ?>" class="btn btn-sm btn-outline-primary">编辑</a>
|
<a href="<?php echo $current_name . '?action=edit_config&name=' . urlencode($pf); ?>" class="btn btn-sm btn-outline-primary">编辑</a>
|
||||||
|
<a href="<?php echo $current_name . '?action=edit_config&name=new_config&copy_from=' . urlencode($pf); ?>" class="btn btn-sm btn-outline-info">复制</a>
|
||||||
<a href="/<?php echo $pf; ?>.php" target="_blank" class="btn btn-sm btn-outline-secondary">查看</a>
|
<a href="/<?php echo $pf; ?>.php" target="_blank" class="btn btn-sm btn-outline-secondary">查看</a>
|
||||||
<?php if ($pf !== 'index'): ?>
|
<?php if ($pf !== 'index'): ?>
|
||||||
<button class="btn btn-sm btn-outline-danger" onclick="if(confirm('确定删除配置「<?php echo htmlspecialchars($pf); ?>」?')) location='<?php echo $current_name . '?action=delete_config&name=' . $pf; ?>'">删除</button>
|
<button class="btn btn-sm btn-outline-danger" onclick="if(confirm('确定删除配置「<?php echo htmlspecialchars($pf); ?>」?')) location='<?php echo $current_name . '?action=delete_config&name=' . $pf; ?>'">删除</button>
|
||||||
@@ -776,7 +812,7 @@ $_d = function($name, $default) { return defined($name) ? constant($name) : $def
|
|||||||
|
|
||||||
<div class="card shadow-sm" style="border-radius:8px;overflow:hidden;">
|
<div class="card shadow-sm" style="border-radius:8px;overflow:hidden;">
|
||||||
<div class="card-header-custom">
|
<div class="card-header-custom">
|
||||||
<h5><?php echo $isNewConfigDraft ? '新增配置' : '编辑配置'; ?>:<?php echo htmlspecialchars($check_name); ?><?php if ($isNewConfigDraft): ?> <span class="badge badge-warning" style="font-size:.75rem;vertical-align:middle;">未保存</span><?php endif; ?></h5>
|
<h5><?php echo $isNewConfigDraft ? '新增配置' : '编辑配置'; ?>:<?php echo htmlspecialchars($check_name); ?><?php if ($isNewConfigDraft): ?> <span class="badge badge-warning" style="font-size:.75rem;vertical-align:middle;">未保存</span><?php endif; ?><?php if ($isNewConfigDraft && $configCopySource !== ''): ?> <span class="badge badge-info" style="font-size:.75rem;vertical-align:middle;">从 <?php echo htmlspecialchars($configCopySource); ?> 复制</span><?php endif; ?></h5>
|
||||||
<div>
|
<div>
|
||||||
<?php if (empty($_SESSION["check_result"]) || $_SESSION["check_result"] != "true"): ?>
|
<?php if (empty($_SESSION["check_result"]) || $_SESSION["check_result"] != "true"): ?>
|
||||||
<a href="<?php echo $current_name; ?>?action=setuvi&res=true&name=<?php echo $check_name; ?>" class="btn btn-sm btn-outline-success mr-1">设置当前浏览器为真实访客</a>
|
<a href="<?php echo $current_name; ?>?action=setuvi&res=true&name=<?php echo $check_name; ?>" class="btn btn-sm btn-outline-success mr-1">设置当前浏览器为真实访客</a>
|
||||||
@@ -802,7 +838,7 @@ $_d = function($name, $default) { return defined($name) ? constant($name) : $def
|
|||||||
<td class="form-label-cell"><strong>配置名称</strong></td>
|
<td class="form-label-cell"><strong>配置名称</strong></td>
|
||||||
<td class="form-val-cell">
|
<td class="form-val-cell">
|
||||||
<input type="text" name="check_name" class="form-control form-control-sm" value="<?php echo htmlspecialchars($check_name); ?>" style="max-width:300px;">
|
<input type="text" name="check_name" class="form-control form-control-sm" value="<?php echo htmlspecialchars($check_name); ?>" style="max-width:300px;">
|
||||||
<small class="text-muted">示例:f123、d258</small>
|
<small class="text-muted"><?php echo ($isNewConfigDraft && $configCopySource !== '') ? '已按「' . htmlspecialchars($configCopySource) . '」预填,请确认配置名称后保存' : '示例:f123、d258'; ?></small>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ require_once(dirname(__DIR__) . "/cong.php");
|
|||||||
require_once(dirname(__DIR__) . "/lib/DbHelper.php");
|
require_once(dirname(__DIR__) . "/lib/DbHelper.php");
|
||||||
require_once(dirname(__DIR__) . "/lib/Cloak/PipelineTimer.php");
|
require_once(dirname(__DIR__) . "/lib/Cloak/PipelineTimer.php");
|
||||||
require_once(dirname(__DIR__) . "/lib/Cloak/VisitorLogSchema.php");
|
require_once(dirname(__DIR__) . "/lib/Cloak/VisitorLogSchema.php");
|
||||||
|
require_once(dirname(__DIR__) . "/lib/Cloak/VisitorApiAudit.php");
|
||||||
session_start();
|
session_start();
|
||||||
|
|
||||||
$reasons = array(
|
$reasons = array(
|
||||||
@@ -117,7 +118,7 @@ if ($conn->connect_error) {
|
|||||||
$where .= " AND " . join(" AND ", $map);
|
$where .= " AND " . join(" AND ", $map);
|
||||||
}
|
}
|
||||||
// SQL语句
|
// SQL语句
|
||||||
$sql = "SELECT `id`, `campagin_id`, `visit_md5_code`, `domain`, `visit_date`, `IP`, `country`, `result`, `reason`, `fp_url`, `referer`, `vtimes`, `client`, `browser`, `page`, `language`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `fp_hdata`, `device`, `add_time` FROM `visitor_logs` WHERE {$where} ORDER BY `id` DESC LIMIT {$startPosition}, {$recordsPerPage}";
|
$sql = "SELECT `id`, `campagin_id`, `visit_md5_code`, `domain`, `visit_date`, `IP`, `country`, `result`, `reason`, `fp_url`, `referer`, `vtimes`, `client`, `browser`, `page`, `language`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `fp_hdata`, `api_by_request`, `api_by_response`, `api_risk_request`, `api_risk_response`, `device`, `add_time` FROM `visitor_logs` WHERE {$where} ORDER BY `id` DESC LIMIT {$startPosition}, {$recordsPerPage}";
|
||||||
|
|
||||||
// 执行查询并输出结果
|
// 执行查询并输出结果
|
||||||
$result = mysqli_query($conn, $sql);
|
$result = mysqli_query($conn, $sql);
|
||||||
@@ -167,6 +168,10 @@ if ($conn->connect_error) {
|
|||||||
$row['fp_hdata_explain_html'] = '';
|
$row['fp_hdata_explain_html'] = '';
|
||||||
$row['fp_hdata_explain'] = null;
|
$row['fp_hdata_explain'] = null;
|
||||||
}
|
}
|
||||||
|
$row['api_by_request_text'] = VisitorApiAudit::displayText($row['api_by_request'] ?? '');
|
||||||
|
$row['api_by_response_text'] = VisitorApiAudit::displayText($row['api_by_response'] ?? '');
|
||||||
|
$row['api_risk_request_text'] = VisitorApiAudit::displayText($row['api_risk_request'] ?? '');
|
||||||
|
$row['api_risk_response_text']= VisitorApiAudit::displayText($row['api_risk_response'] ?? '');
|
||||||
$jsonData[] = $row;
|
$jsonData[] = $row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ require_once(dirname(__DIR__) . "/cong.php");
|
|||||||
require_once(dirname(__DIR__) . "/lib/DbHelper.php");
|
require_once(dirname(__DIR__) . "/lib/DbHelper.php");
|
||||||
require_once(dirname(__DIR__) . "/lib/Cloak/PipelineTimer.php");
|
require_once(dirname(__DIR__) . "/lib/Cloak/PipelineTimer.php");
|
||||||
require_once(dirname(__DIR__) . "/lib/Cloak/VisitorLogSchema.php");
|
require_once(dirname(__DIR__) . "/lib/Cloak/VisitorLogSchema.php");
|
||||||
|
require_once(dirname(__DIR__) . "/lib/Cloak/VisitorApiAudit.php");
|
||||||
|
|
||||||
$reasons = array(
|
$reasons = array(
|
||||||
"blank_referrer" => "过滤无来源的访问",
|
"blank_referrer" => "过滤无来源的访问",
|
||||||
@@ -111,7 +112,7 @@ if ($conn->connect_error) {
|
|||||||
$where .= " AND " . join(" AND ", $map);
|
$where .= " AND " . join(" AND ", $map);
|
||||||
}
|
}
|
||||||
// SQL语句
|
// SQL语句
|
||||||
$sql = "SELECT `id`, `campagin_id`, `visit_md5_code`, `domain`, `visit_date`, `IP`, `country`, `result`, `reason`, `fp_url`, `referer`, `vtimes`, `client`, `browser`, `page`, `language`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `fp_hdata`, `device`, `add_time` FROM `visitor_logs` WHERE {$where} ORDER BY `id` DESC LIMIT {$startPosition}, {$recordsPerPage}";
|
$sql = "SELECT `id`, `campagin_id`, `visit_md5_code`, `domain`, `visit_date`, `IP`, `country`, `result`, `reason`, `fp_url`, `referer`, `vtimes`, `client`, `browser`, `page`, `language`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `fp_hdata`, `api_by_request`, `api_by_response`, `api_risk_request`, `api_risk_response`, `device`, `add_time` FROM `visitor_logs` WHERE {$where} ORDER BY `id` DESC LIMIT {$startPosition}, {$recordsPerPage}";
|
||||||
|
|
||||||
// 执行查询并输出结果
|
// 执行查询并输出结果
|
||||||
$result = mysqli_query($conn, $sql);
|
$result = mysqli_query($conn, $sql);
|
||||||
@@ -157,6 +158,10 @@ if ($conn->connect_error) {
|
|||||||
$row['fp_hdata_explain_html'] = '';
|
$row['fp_hdata_explain_html'] = '';
|
||||||
$row['fp_hdata_explain'] = null;
|
$row['fp_hdata_explain'] = null;
|
||||||
}
|
}
|
||||||
|
$row['api_by_request_text'] = VisitorApiAudit::displayText($row['api_by_request'] ?? '');
|
||||||
|
$row['api_by_response_text'] = VisitorApiAudit::displayText($row['api_by_response'] ?? '');
|
||||||
|
$row['api_risk_request_text'] = VisitorApiAudit::displayText($row['api_risk_request'] ?? '');
|
||||||
|
$row['api_risk_response_text']= VisitorApiAudit::displayText($row['api_risk_response'] ?? '');
|
||||||
$jsonData[] = $row;
|
$jsonData[] = $row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-5
@@ -37,6 +37,8 @@ require_once __DIR__ . '/lib/DbHelper.php';
|
|||||||
require_once __DIR__ . '/lib/bootstrap.php';
|
require_once __DIR__ . '/lib/bootstrap.php';
|
||||||
require_once __DIR__ . '/config/ConfigLoader.php';
|
require_once __DIR__ . '/config/ConfigLoader.php';
|
||||||
require_once __DIR__ . '/lib/Cloak/FpUrlHelper.php';
|
require_once __DIR__ . '/lib/Cloak/FpUrlHelper.php';
|
||||||
|
require_once __DIR__ . '/lib/Cloak/VisitorApiAudit.php';
|
||||||
|
require_once __DIR__ . '/lib/Cloak/RiskSecondaryGate.php';
|
||||||
|
|
||||||
$fp_host = parse_url($fingerprint_info['domain'], PHP_URL_HOST);
|
$fp_host = parse_url($fingerprint_info['domain'], PHP_URL_HOST);
|
||||||
if (empty($fp_host)) {
|
if (empty($fp_host)) {
|
||||||
@@ -44,6 +46,41 @@ if (empty($fp_host)) {
|
|||||||
}
|
}
|
||||||
$config_name = ConfigLoader::loadByHost($fp_host);
|
$config_name = ConfigLoader::loadByHost($fp_host);
|
||||||
|
|
||||||
|
$log_id = (int) ($fingerprint_info['log_id'] ?? 0);
|
||||||
|
|
||||||
|
if (!RiskSecondaryGate::isEnabled()) {
|
||||||
|
$_SESSION['visit_to_3'] = 3;
|
||||||
|
$_SESSION['check_result'] = 'true';
|
||||||
|
|
||||||
|
$resolved = FpUrlHelper::resolve($config_name, true);
|
||||||
|
$fp_url = $resolved['url'];
|
||||||
|
$response = [
|
||||||
|
'reason' => '',
|
||||||
|
'result' => true,
|
||||||
|
'link' => $fp_url !== '' ? $fp_url : FpUrlHelper::fallbackUrl(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($log_id > 0) {
|
||||||
|
$conn = new mysqli('localhost', DB_USERNAME, DB_PASSWORD, DB_NAME);
|
||||||
|
if (!$conn->connect_error) {
|
||||||
|
require_once __DIR__ . '/lib/Cloak/VisitorLogSchema.php';
|
||||||
|
VisitorLogSchema::ensureColumns($conn);
|
||||||
|
$resultEsc = cloak_db_escape($conn, 'true');
|
||||||
|
$reasonEsc = cloak_db_escape($conn, '二次风控已关闭');
|
||||||
|
$fpUrlEsc = cloak_db_escape($conn, strip_tags(cloak_db_string($response['link'])));
|
||||||
|
$sql = "UPDATE `visitor_logs` SET `result`='{$resultEsc}',`reason`='{$reasonEsc}',`fp_url`='{$fpUrlEsc}' WHERE `id`='" . $log_id . "'";
|
||||||
|
if ($conn->query($sql) !== true) {
|
||||||
|
$handle = fopen(dirname(__FILE__) . '/err.txt', 'a+');
|
||||||
|
fwrite($handle, date('Y-m-d H:i:s') . ' | f_check risk disabled update: ' . $conn->error . "\n");
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
$conn->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(json_encode($response));
|
||||||
|
}
|
||||||
|
|
||||||
$jsonData = [
|
$jsonData = [
|
||||||
'id' => COSTM_IP_SCORE,
|
'id' => COSTM_IP_SCORE,
|
||||||
'hdata' => $hdata,
|
'hdata' => $hdata,
|
||||||
@@ -54,7 +91,7 @@ $jsonData = [
|
|||||||
'risk_number' => CLOAK_RISK_NUMBER,
|
'risk_number' => CLOAK_RISK_NUMBER,
|
||||||
];
|
];
|
||||||
|
|
||||||
$ch = curl_init('https://www.tiktokba.com/cloak/byApiRisk');
|
$ch = curl_init('www.tiktokba.com/cloak/byApiRisk');
|
||||||
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||||
@@ -114,10 +151,8 @@ if($return['result']) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$log_id = (int) ($fingerprint_info['log_id'] ?? 0);
|
|
||||||
$update_log = [];
|
$update_log = [];
|
||||||
|
$update_log['result'] = $response['result'] ? 'true' : 'false';
|
||||||
$update_log['result'] = $response['result'] ? "true" : "false";
|
|
||||||
$update_log['reason'] = $response['reason'] ?? '';
|
$update_log['reason'] = $response['reason'] ?? '';
|
||||||
$update_log['fp_url'] = trim((string) ($response['link'] ?? ''));
|
$update_log['fp_url'] = trim((string) ($response['link'] ?? ''));
|
||||||
if ($update_log['fp_url'] === '') {
|
if ($update_log['fp_url'] === '') {
|
||||||
@@ -148,9 +183,11 @@ if ($conn->connect_error) {
|
|||||||
$fpHdataJson = '';
|
$fpHdataJson = '';
|
||||||
}
|
}
|
||||||
$fpHdataEsc = cloak_db_escape($conn, $fpHdataJson);
|
$fpHdataEsc = cloak_db_escape($conn, $fpHdataJson);
|
||||||
|
$apiRiskReqEsc = cloak_db_escape($conn, VisitorApiAudit::encode($jsonData));
|
||||||
|
$apiRiskRespEsc = cloak_db_escape($conn, VisitorApiAudit::encode($return));
|
||||||
require_once __DIR__ . '/lib/Cloak/VisitorLogSchema.php';
|
require_once __DIR__ . '/lib/Cloak/VisitorLogSchema.php';
|
||||||
VisitorLogSchema::ensureColumns($conn);
|
VisitorLogSchema::ensureColumns($conn);
|
||||||
$sql = "UPDATE `visitor_logs` SET `result`='{$resultEsc}',`reason`='{$reasonEsc}',`fp_url`='{$fpUrlEsc}',`fp_hdata`='{$fpHdataEsc}' WHERE `id`='" . (int) $log_id . "'";
|
$sql = "UPDATE `visitor_logs` SET `result`='{$resultEsc}',`reason`='{$reasonEsc}',`fp_url`='{$fpUrlEsc}',`fp_hdata`='{$fpHdataEsc}',`api_risk_request`='{$apiRiskReqEsc}',`api_risk_response`='{$apiRiskRespEsc}' WHERE `id`='" . (int) $log_id . "'";
|
||||||
|
|
||||||
if ($conn->query($sql) != TRUE) {
|
if ($conn->query($sql) != TRUE) {
|
||||||
$handle = fopen(dirname(__FILE__) . "/err.txt", "a+");
|
$handle = fopen(dirname(__FILE__) . "/err.txt", "a+");
|
||||||
|
|||||||
@@ -99,3 +99,5 @@
|
|||||||
2026-06-14 15:23:23 | 写入数据库 | | regression_test | async=1
|
2026-06-14 15:23:23 | 写入数据库 | | regression_test | async=1
|
||||||
2026-06-14 15:47:16 | 来源判断 | 无法识别访客国家|false | index | 127.0.0.1 | https://www.baidu.com/
|
2026-06-14 15:47:16 | 来源判断 | 无法识别访客国家|false | index | 127.0.0.1 | https://www.baidu.com/
|
||||||
2026-06-14 15:47:16 | 写入数据库 | false | index | async=1
|
2026-06-14 15:47:16 | 写入数据库 | false | index | async=1
|
||||||
|
2026-06-15 16:45:08 | 来源判断 | 无法识别访客国家|false | index | 127.0.0.1 | https://www.baidu.com/
|
||||||
|
2026-06-15 16:45:08 | 写入数据库 | false | index | async=1
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
/**
|
/**
|
||||||
* SHOW_SITE_COUNTRY 允许列表解析与比对
|
* SHOW_SITE_COUNTRY 允许列表解析与比对
|
||||||
*/
|
*/
|
||||||
|
require_once __DIR__ . '/CountryCodeNormalizer.php';
|
||||||
|
|
||||||
class CountryAllowlist
|
class CountryAllowlist
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -34,9 +36,10 @@ class CountryAllowlist
|
|||||||
if ($allowlist === []) {
|
if ($allowlist === []) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ($countryCode === null || $countryCode === '') {
|
$normalized = CountryCodeNormalizer::toAlpha2($countryCode);
|
||||||
|
if ($normalized === null || $normalized === '') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return in_array(strtoupper($countryCode), $allowlist, true);
|
return in_array($normalized, $allowlist, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 将 GeoIP / API 返回的国家值统一为 ISO 3166-1 alpha-2(两位大写国家码)。
|
||||||
|
*/
|
||||||
|
class CountryCodeNormalizer
|
||||||
|
{
|
||||||
|
/** @var array{names:array<string,string>,alpha3:array<string,string>}|null */
|
||||||
|
private static $maps = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value ISO2、ISO3 或英文国名
|
||||||
|
* @return string|null 两位国家码;无法识别时 null
|
||||||
|
*/
|
||||||
|
public static function toAlpha2($value)
|
||||||
|
{
|
||||||
|
$value = trim((string) $value);
|
||||||
|
if ($value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$upper = strtoupper($value);
|
||||||
|
if (preg_match('/^[A-Z]{2}$/', $upper)) {
|
||||||
|
return $upper;
|
||||||
|
}
|
||||||
|
|
||||||
|
$maps = self::maps();
|
||||||
|
if (preg_match('/^[A-Z]{3}$/', $upper) && isset($maps['alpha3'][$upper])) {
|
||||||
|
return $maps['alpha3'][$upper];
|
||||||
|
}
|
||||||
|
|
||||||
|
$nameKey = self::nameKey($value);
|
||||||
|
if (isset($maps['names'][$nameKey])) {
|
||||||
|
return $maps['names'][$nameKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{names:array<string,string>,alpha3:array<string,string>}
|
||||||
|
*/
|
||||||
|
private static function maps()
|
||||||
|
{
|
||||||
|
if (self::$maps === null) {
|
||||||
|
$file = __DIR__ . '/iso3166_maps.php';
|
||||||
|
$data = is_file($file) ? require $file : ['names' => [], 'alpha3' => []];
|
||||||
|
self::$maps = [
|
||||||
|
'names' => isset($data['names']) && is_array($data['names']) ? $data['names'] : [],
|
||||||
|
'alpha3' => isset($data['alpha3']) && is_array($data['alpha3']) ? $data['alpha3'] : [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return self::$maps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function nameKey($name)
|
||||||
|
{
|
||||||
|
$name = preg_replace('/\s+/u', ' ', trim((string) $name));
|
||||||
|
return strtoupper($name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
/**
|
/**
|
||||||
* IP → 国家码跨请求缓存(Redis 优先,文件降级,默认 TTL 30 天)
|
* IP → 国家码跨请求缓存(Redis 优先,文件降级,默认 TTL 30 天)
|
||||||
*/
|
*/
|
||||||
|
require_once __DIR__ . '/CountryCodeNormalizer.php';
|
||||||
|
|
||||||
class GeoIpCountryCache
|
class GeoIpCountryCache
|
||||||
{
|
{
|
||||||
const REDIS_PREFIX = 'cloak:geoip:cc:';
|
const REDIS_PREFIX = 'cloak:geoip:cc:';
|
||||||
@@ -28,7 +30,7 @@ class GeoIpCountryCache
|
|||||||
if ($value === false || $value === null) {
|
if ($value === false || $value === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return $value === self::NONE_SENTINEL ? null : (string) $value;
|
return self::normalizeCachedValue($value === self::NONE_SENTINEL ? null : (string) $value);
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
self::logError('Redis get failed: ' . $e->getMessage());
|
self::logError('Redis get failed: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
@@ -48,7 +50,12 @@ class GeoIpCountryCache
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = $countryCode !== null && $countryCode !== '' ? strtoupper($countryCode) : self::NONE_SENTINEL;
|
if ($countryCode !== null && $countryCode !== '') {
|
||||||
|
$normalized = CountryCodeNormalizer::toAlpha2($countryCode);
|
||||||
|
$value = $normalized !== null ? $normalized : strtoupper($countryCode);
|
||||||
|
} else {
|
||||||
|
$value = self::NONE_SENTINEL;
|
||||||
|
}
|
||||||
$ttl = self::cacheTtl();
|
$ttl = self::cacheTtl();
|
||||||
|
|
||||||
$redis = self::redisClient();
|
$redis = self::redisClient();
|
||||||
@@ -137,7 +144,20 @@ class GeoIpCountryCache
|
|||||||
@unlink($path);
|
@unlink($path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return $data['value'] === self::NONE_SENTINEL ? null : (string) $data['value'];
|
return self::normalizeCachedValue($data['value'] === self::NONE_SENTINEL ? null : (string) $data['value']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $value
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
private static function normalizeCachedValue($value)
|
||||||
|
{
|
||||||
|
if ($value === null || $value === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$normalized = CountryCodeNormalizer::toAlpha2($value);
|
||||||
|
return $normalized !== null ? $normalized : strtoupper((string) $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
require_once __DIR__ . '/CountryCodeNormalizer.php';
|
||||||
|
|
||||||
class GeoIpCountryResolver
|
class GeoIpCountryResolver
|
||||||
{
|
{
|
||||||
/** @var GeoIp2\Database\Reader|null */
|
/** @var GeoIp2\Database\Reader|null */
|
||||||
@@ -73,11 +75,14 @@ class GeoIpCountryResolver
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
$record = $reader->country($ip);
|
$record = $reader->country($ip);
|
||||||
$code = $record->country->isoCode ?? null;
|
$isoCode = $record->country->isoCode ?? null;
|
||||||
if ($code === null || $code === '') {
|
$name = $record->country->name ?? null;
|
||||||
return null;
|
|
||||||
|
$code = CountryCodeNormalizer::toAlpha2($isoCode);
|
||||||
|
if ($code === null && $name !== null && $name !== '') {
|
||||||
|
$code = CountryCodeNormalizer::toAlpha2($name);
|
||||||
}
|
}
|
||||||
return strtoupper((string) $code);
|
return $code;
|
||||||
} catch (GeoIp2\Exception\AddressNotFoundException $e) {
|
} catch (GeoIp2\Exception\AddressNotFoundException $e) {
|
||||||
return null;
|
return null;
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
require_once __DIR__ . '/../Page/FpPageRenderer.php';
|
require_once __DIR__ . '/../Page/FpPageRenderer.php';
|
||||||
require_once __DIR__ . '/../Page/RiskPageRenderer.php';
|
require_once __DIR__ . '/../Page/RiskPageRenderer.php';
|
||||||
require_once __DIR__ . '/../FpUrlHelper.php';
|
require_once __DIR__ . '/../FpUrlHelper.php';
|
||||||
|
require_once __DIR__ . '/../RiskSecondaryGate.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判定完成后的路由输出(真实页 / 安全页 / 二次风控)
|
* 判定完成后的路由输出(真实页 / 安全页 / 二次风控)
|
||||||
@@ -78,7 +79,7 @@ class RouteResolver
|
|||||||
$_SESSION['visit_to_3'] = '1';
|
$_SESSION['visit_to_3'] = '1';
|
||||||
}
|
}
|
||||||
if (($_SESSION['visit_to_3'] ?? '1') != '3'
|
if (($_SESSION['visit_to_3'] ?? '1') != '3'
|
||||||
&& (CLOAK_RISK_NUMBER > 0 && CLOAK_RISK_NUMBER <= 90)) {
|
&& RiskSecondaryGate::isEnabled()) {
|
||||||
// 二次风控:先写 wait 状态,f_check 完成后再更新 result / fp_url(避免 true+空跳转)
|
// 二次风控:先写 wait 状态,f_check 完成后再更新 result / fp_url(避免 true+空跳转)
|
||||||
$logs['result'] = 'wait';
|
$logs['result'] = 'wait';
|
||||||
$logs['reason'] = '二次风控检测中';
|
$logs['reason'] = '二次风控检测中';
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
$_SESSION["gcu_Client"] = '';
|
$_SESSION["gcu_Client"] = '';
|
||||||
$_SESSION["gcu_ip"] = '';
|
$_SESSION["gcu_ip"] = '';
|
||||||
$_SESSION["gcu_Browser"] = '';
|
$_SESSION["gcu_Browser"] = '';
|
||||||
|
if (class_exists('VisitorApiAudit', false)) {
|
||||||
|
VisitorApiAudit::clearByApi();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($_SESSION['visit_to_1'] == '1' && $_SESSION['visit_to_2'] != '2') // $_SESSION['visit_to_2'] 为2时表示已经判断过所有规则。
|
if($_SESSION['visit_to_1'] == '1' && $_SESSION['visit_to_2'] != '2') // $_SESSION['visit_to_2'] 为2时表示已经判断过所有规则。
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 二次风控(cloakjs + f_check + byApiRisk)是否启用,与 RouteResolver 判定一致。
|
||||||
|
*/
|
||||||
|
class RiskSecondaryGate
|
||||||
|
{
|
||||||
|
public static function isEnabled()
|
||||||
|
{
|
||||||
|
if (!defined('CLOAK_RISK_NUMBER')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$riskNumber = (int) CLOAK_RISK_NUMBER;
|
||||||
|
return $riskNumber > 0 && $riskNumber <= 90;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* 远程 cloak API(byApi / byApiRisk)请求与响应审计,供 visitor_logs 落库与日志页展示。
|
||||||
|
*/
|
||||||
|
class VisitorApiAudit
|
||||||
|
{
|
||||||
|
const SESSION_BY_REQUEST = 'cloak_api_by_request';
|
||||||
|
const SESSION_BY_RESPONSE = 'cloak_api_by_response';
|
||||||
|
|
||||||
|
public static function clearByApi()
|
||||||
|
{
|
||||||
|
unset($_SESSION[self::SESSION_BY_REQUEST], $_SESSION[self::SESSION_BY_RESPONSE]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|string $payload
|
||||||
|
*/
|
||||||
|
public static function recordByApiRequest($payload)
|
||||||
|
{
|
||||||
|
$_SESSION[self::SESSION_BY_REQUEST] = self::encode($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|string|null $payload
|
||||||
|
*/
|
||||||
|
public static function recordByApiResponse($payload)
|
||||||
|
{
|
||||||
|
if ($payload === null || $payload === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$_SESSION[self::SESSION_BY_RESPONSE] = self::encode($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入日志数组(仅合并当前请求内已调用的 byApi 审计数据)
|
||||||
|
*
|
||||||
|
* @param array $logs
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function mergeIntoLogs(array $logs)
|
||||||
|
{
|
||||||
|
$map = [
|
||||||
|
'api_by_request' => self::SESSION_BY_REQUEST,
|
||||||
|
'api_by_response' => self::SESSION_BY_RESPONSE,
|
||||||
|
];
|
||||||
|
foreach ($map as $col => $sessionKey) {
|
||||||
|
if (empty($logs[$col]) && !empty($_SESSION[$sessionKey])) {
|
||||||
|
$logs[$col] = (string) $_SESSION[$sessionKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $logs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $data
|
||||||
|
*/
|
||||||
|
public static function encode($data)
|
||||||
|
{
|
||||||
|
if (is_string($data)) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
return $json !== false ? $json : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志表格展示:未调用时返回「-」
|
||||||
|
*
|
||||||
|
* @param mixed $raw
|
||||||
|
*/
|
||||||
|
public static function displayText($raw)
|
||||||
|
{
|
||||||
|
$raw = trim((string) $raw);
|
||||||
|
return $raw === '' ? '-' : $raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once dirname(__DIR__) . '/Cloak/ReasonHelper.php';
|
require_once dirname(__DIR__) . '/Cloak/ReasonHelper.php';
|
||||||
|
require_once dirname(__DIR__) . '/Cloak/VisitorApiAudit.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 访客上下文与 API 判定(类名保留历史拼写 visitorInfo)
|
* 访客上下文与 API 判定(类名保留历史拼写 visitorInfo)
|
||||||
@@ -93,6 +94,7 @@ class visitorInfo
|
|||||||
$jsonData['country_code'] = $this->check_country; //设置该参数后,将替换"广告策略》访问者地理位置>过滤"的设置,填写国家代码,多个用逗号分隔,如:US,GB,CA,AU,IE,NZ
|
$jsonData['country_code'] = $this->check_country; //设置该参数后,将替换"广告策略》访问者地理位置>过滤"的设置,填写国家代码,多个用逗号分隔,如:US,GB,CA,AU,IE,NZ
|
||||||
$jsonData['referer'] = get_SERVER_value('HTTP_REFERER');
|
$jsonData['referer'] = get_SERVER_value('HTTP_REFERER');
|
||||||
$jsonData['headers'] = json_encode($headers);
|
$jsonData['headers'] = json_encode($headers);
|
||||||
|
VisitorApiAudit::recordByApiRequest($jsonData);
|
||||||
$ch = curl_init('www.tiktokba.com/cloak/byApi');
|
$ch = curl_init('www.tiktokba.com/cloak/byApi');
|
||||||
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
@@ -114,14 +116,19 @@ class visitorInfo
|
|||||||
if ($return == false) {
|
if ($return == false) {
|
||||||
$curl_error = curl_error($ch);
|
$curl_error = curl_error($ch);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
return ['result' => false, 'reason' => 'API请求失败:' . $curl_error, 'country' => ''];
|
$errorPayload = ['result' => false, 'reason' => 'API请求失败:' . $curl_error, 'country' => ''];
|
||||||
|
VisitorApiAudit::recordByApiResponse($errorPayload);
|
||||||
|
return $errorPayload;
|
||||||
}
|
}
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
$decoded = json_decode($return, true);
|
$decoded = json_decode($return, true);
|
||||||
if (!is_array($decoded)) {
|
if (!is_array($decoded)) {
|
||||||
|
$invalidPayload = ['result' => false, 'reason' => 'API响应失败,请检查API密钥以及广告策略 ID', 'country' => '', '_raw' => $return];
|
||||||
|
VisitorApiAudit::recordByApiResponse($invalidPayload);
|
||||||
return ['result' => false, 'reason' => 'API响应失败,请检查API密钥以及广告策略 ID', 'country' => ''];
|
return ['result' => false, 'reason' => 'API响应失败,请检查API密钥以及广告策略 ID', 'country' => ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VisitorApiAudit::recordByApiResponse($decoded);
|
||||||
return $decoded;
|
return $decoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ class VisitorLogSchema
|
|||||||
'accept_language_raw' => "ALTER TABLE `visitor_logs` ADD COLUMN `accept_language_raw` TEXT NULL COMMENT 'Accept-Language 完整值' AFTER `http_referer`",
|
'accept_language_raw' => "ALTER TABLE `visitor_logs` ADD COLUMN `accept_language_raw` TEXT NULL COMMENT 'Accept-Language 完整值' AFTER `http_referer`",
|
||||||
'judge_timing' => "ALTER TABLE `visitor_logs` ADD COLUMN `judge_timing` TEXT NULL COMMENT '判定用时 JSON' AFTER `accept_language_raw`",
|
'judge_timing' => "ALTER TABLE `visitor_logs` ADD COLUMN `judge_timing` TEXT NULL COMMENT '判定用时 JSON' AFTER `accept_language_raw`",
|
||||||
'fp_hdata' => "ALTER TABLE `visitor_logs` ADD COLUMN `fp_hdata` TEXT NULL COMMENT '指纹 hdata 解码 JSON' AFTER `judge_timing`",
|
'fp_hdata' => "ALTER TABLE `visitor_logs` ADD COLUMN `fp_hdata` TEXT NULL COMMENT '指纹 hdata 解码 JSON' AFTER `judge_timing`",
|
||||||
|
'api_by_request' => "ALTER TABLE `visitor_logs` ADD COLUMN `api_by_request` TEXT NULL COMMENT 'byApi 请求参数 JSON' AFTER `fp_hdata`",
|
||||||
|
'api_by_response' => "ALTER TABLE `visitor_logs` ADD COLUMN `api_by_response` TEXT NULL COMMENT 'byApi 响应 JSON' AFTER `api_by_request`",
|
||||||
|
'api_risk_request' => "ALTER TABLE `visitor_logs` ADD COLUMN `api_risk_request` TEXT NULL COMMENT 'byApiRisk 请求参数 JSON' AFTER `api_by_response`",
|
||||||
|
'api_risk_response' => "ALTER TABLE `visitor_logs` ADD COLUMN `api_risk_response` TEXT NULL COMMENT 'byApiRisk 响应 JSON' AFTER `api_risk_request`",
|
||||||
];
|
];
|
||||||
foreach ($columns as $name => $ddl) {
|
foreach ($columns as $name => $ddl) {
|
||||||
if (!self::columnExists($conn, 'visitor_logs', $name)) {
|
if (!self::columnExists($conn, 'visitor_logs', $name)) {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class VisitorLogWorker
|
|||||||
return array_key_exists($key, $data) ? $data[$key] : cloak_db_escape($conn, '');
|
return array_key_exists($key, $data) ? $data[$key] : cloak_db_escape($conn, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
$sql = "INSERT INTO `visitor_logs`(`campagin_id`, `visit_md5_code`, `domain`, `visit_date`, `IP`, `country`, `result`, `reason`, `referer`, `client`, `browser`, `page`, `language`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `fp_url`, `device`) VALUES ('"
|
$sql = "INSERT INTO `visitor_logs`(`campagin_id`, `visit_md5_code`, `domain`, `visit_date`, `IP`, `country`, `result`, `reason`, `referer`, `client`, `browser`, `page`, `language`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `fp_url`, `device`, `api_by_request`, `api_by_response`, `api_risk_request`, `api_risk_response`) VALUES ('"
|
||||||
. $d('campagin_id') . "','"
|
. $d('campagin_id') . "','"
|
||||||
. $d('visit_md5_code') . "','"
|
. $d('visit_md5_code') . "','"
|
||||||
. $d('site_name') . "','"
|
. $d('site_name') . "','"
|
||||||
@@ -89,7 +89,11 @@ class VisitorLogWorker
|
|||||||
. $d('accept_language_raw') . "','"
|
. $d('accept_language_raw') . "','"
|
||||||
. $d('judge_timing') . "','"
|
. $d('judge_timing') . "','"
|
||||||
. $d('fp_url') . "','"
|
. $d('fp_url') . "','"
|
||||||
. $d('device') . "')";
|
. $d('device') . "','"
|
||||||
|
. $d('api_by_request') . "','"
|
||||||
|
. $d('api_by_response') . "','"
|
||||||
|
. $d('api_risk_request') . "','"
|
||||||
|
. $d('api_risk_response') . "')";
|
||||||
|
|
||||||
if ($conn->query($sql) !== true) {
|
if ($conn->query($sql) !== true) {
|
||||||
VisitorLogQueue::logError('insert_full: ' . $conn->error . ' | ' . $sql);
|
VisitorLogQueue::logError('insert_full: ' . $conn->error . ' | ' . $sql);
|
||||||
@@ -169,6 +173,10 @@ class VisitorLogWorker
|
|||||||
'http_referer' => 'http_referer',
|
'http_referer' => 'http_referer',
|
||||||
'accept_language_raw' => 'accept_language_raw',
|
'accept_language_raw' => 'accept_language_raw',
|
||||||
'judge_timing' => 'judge_timing',
|
'judge_timing' => 'judge_timing',
|
||||||
|
'api_by_request' => 'api_by_request',
|
||||||
|
'api_by_response' => 'api_by_response',
|
||||||
|
'api_risk_request' => 'api_risk_request',
|
||||||
|
'api_risk_response' => 'api_risk_response',
|
||||||
];
|
];
|
||||||
foreach ($map as $col => $logKey) {
|
foreach ($map as $col => $logKey) {
|
||||||
if (array_key_exists($logKey, $data)) {
|
if (array_key_exists($logKey, $data)) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
require_once dirname(__DIR__) . '/DbHelper.php';
|
require_once dirname(__DIR__) . '/DbHelper.php';
|
||||||
require_once dirname(__DIR__) . '/CloakHttpHelpers.php';
|
require_once dirname(__DIR__) . '/CloakHttpHelpers.php';
|
||||||
|
require_once __DIR__ . '/VisitorApiAudit.php';
|
||||||
|
|
||||||
if (!function_exists('cloak_session_mark_real_visitor')) {
|
if (!function_exists('cloak_session_mark_real_visitor')) {
|
||||||
/**
|
/**
|
||||||
@@ -123,7 +124,7 @@ if (!function_exists('cloak_visitor_log_enrich')) {
|
|||||||
if (empty($logs['judge_timing']) && !empty($GLOBALS['__cloak_judge_timing'])) {
|
if (empty($logs['judge_timing']) && !empty($GLOBALS['__cloak_judge_timing'])) {
|
||||||
$logs['judge_timing'] = $GLOBALS['__cloak_judge_timing'];
|
$logs['judge_timing'] = $GLOBALS['__cloak_judge_timing'];
|
||||||
}
|
}
|
||||||
return $logs;
|
return VisitorApiAudit::mergeIntoLogs($logs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +210,7 @@ if (!function_exists('cloak_write_log_db')) {
|
|||||||
$d = static function ($key) use ($data, $conn) {
|
$d = static function ($key) use ($data, $conn) {
|
||||||
return array_key_exists($key, $data) ? $data[$key] : cloak_db_escape($conn, '');
|
return array_key_exists($key, $data) ? $data[$key] : cloak_db_escape($conn, '');
|
||||||
};
|
};
|
||||||
$sql = "INSERT INTO `visitor_logs`(`campagin_id`, `visit_md5_code`, `domain`, `visit_date`, `IP`, `country`, `result`, `reason`, `referer`, `client`, `browser`, `page`, `language`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `fp_url`, `device`) VALUES ('"
|
$sql = "INSERT INTO `visitor_logs`(`campagin_id`, `visit_md5_code`, `domain`, `visit_date`, `IP`, `country`, `result`, `reason`, `referer`, `client`, `browser`, `page`, `language`, `user_agent`, `http_referer`, `accept_language_raw`, `judge_timing`, `fp_url`, `device`, `api_by_request`, `api_by_response`, `api_risk_request`, `api_risk_response`) VALUES ('"
|
||||||
. $d('campagin_id') . "','"
|
. $d('campagin_id') . "','"
|
||||||
. $d('visit_md5_code') . "','"
|
. $d('visit_md5_code') . "','"
|
||||||
. $d('site_name') . "','"
|
. $d('site_name') . "','"
|
||||||
@@ -228,7 +229,11 @@ if (!function_exists('cloak_write_log_db')) {
|
|||||||
. $d('accept_language_raw') . "','"
|
. $d('accept_language_raw') . "','"
|
||||||
. $d('judge_timing') . "','"
|
. $d('judge_timing') . "','"
|
||||||
. $d('fp_url') . "','"
|
. $d('fp_url') . "','"
|
||||||
. $d('device') . "')";
|
. $d('device') . "','"
|
||||||
|
. $d('api_by_request') . "','"
|
||||||
|
. $d('api_by_response') . "','"
|
||||||
|
. $d('api_risk_request') . "','"
|
||||||
|
. $d('api_risk_response') . "')";
|
||||||
if ($conn->query($sql) === true) {
|
if ($conn->query($sql) === true) {
|
||||||
$last_id = $conn->insert_id;
|
$last_id = $conn->insert_id;
|
||||||
$conn->close();
|
$conn->close();
|
||||||
|
|||||||
@@ -0,0 +1,526 @@
|
|||||||
|
<?php
|
||||||
|
/** ISO 3166-1 英文国名 / Alpha-3 → Alpha-2 映射(GeoIP 国家比对用) */
|
||||||
|
return [
|
||||||
|
'names' => [
|
||||||
|
'AFGHANISTAN' => 'AF',
|
||||||
|
'ÅLAND ISLANDS' => 'AX',
|
||||||
|
'ALBANIA' => 'AL',
|
||||||
|
'ALGERIA' => 'DZ',
|
||||||
|
'AMERICAN SAMOA' => 'AS',
|
||||||
|
'ANDORRA' => 'AD',
|
||||||
|
'ANGOLA' => 'AO',
|
||||||
|
'ANGUILLA' => 'AI',
|
||||||
|
'ANTARCTICA' => 'AQ',
|
||||||
|
'ANTIGUA AND BARBUDA' => 'AG',
|
||||||
|
'ARGENTINA' => 'AR',
|
||||||
|
'ARMENIA' => 'AM',
|
||||||
|
'ARUBA' => 'AW',
|
||||||
|
'AUSTRALIA' => 'AU',
|
||||||
|
'AUSTRIA' => 'AT',
|
||||||
|
'AZERBAIJAN' => 'AZ',
|
||||||
|
'BAHAMAS' => 'BS',
|
||||||
|
'BAHRAIN' => 'BH',
|
||||||
|
'BANGLADESH' => 'BD',
|
||||||
|
'BARBADOS' => 'BB',
|
||||||
|
'BELARUS' => 'BY',
|
||||||
|
'BELGIUM' => 'BE',
|
||||||
|
'BELIZE' => 'BZ',
|
||||||
|
'BENIN' => 'BJ',
|
||||||
|
'BERMUDA' => 'BM',
|
||||||
|
'BHUTAN' => 'BT',
|
||||||
|
'BOLIVIA' => 'BO',
|
||||||
|
'BONAIRE, SINT EUSTATIUS AND SABA' => 'BQ',
|
||||||
|
'BOSNIA AND HERZEGOVINA' => 'BA',
|
||||||
|
'BOTSWANA' => 'BW',
|
||||||
|
'BOUVET ISLAND' => 'BV',
|
||||||
|
'BRAZIL' => 'BR',
|
||||||
|
'BRITISH INDIAN OCEAN TERRITORY' => 'IO',
|
||||||
|
'BRUNEI DARUSSALAM' => 'BN',
|
||||||
|
'BULGARIA' => 'BG',
|
||||||
|
'BURKINA FASO' => 'BF',
|
||||||
|
'BURUNDI' => 'BI',
|
||||||
|
'CABO VERDE' => 'CV',
|
||||||
|
'CAMBODIA' => 'KH',
|
||||||
|
'CAMEROON' => 'CM',
|
||||||
|
'CANADA' => 'CA',
|
||||||
|
'CAYMAN ISLANDS' => 'KY',
|
||||||
|
'CENTRAL AFRICAN REPUBLIC' => 'CF',
|
||||||
|
'CHAD' => 'TD',
|
||||||
|
'CHILE' => 'CL',
|
||||||
|
'CHINA' => 'CN',
|
||||||
|
'CHRISTMAS ISLAND' => 'CX',
|
||||||
|
'COCOS (KEELING) ISLANDS' => 'CC',
|
||||||
|
'COLOMBIA' => 'CO',
|
||||||
|
'COMOROS' => 'KM',
|
||||||
|
'CONGO' => 'CG',
|
||||||
|
'CONGO, DEMOCRATIC REPUBLIC OF THE' => 'CD',
|
||||||
|
'COOK ISLANDS' => 'CK',
|
||||||
|
'COSTA RICA' => 'CR',
|
||||||
|
'CÔTE D\'IVOIRE' => 'CI',
|
||||||
|
'CROATIA' => 'HR',
|
||||||
|
'CUBA' => 'CU',
|
||||||
|
'CURAÇAO' => 'CW',
|
||||||
|
'CYPRUS' => 'CY',
|
||||||
|
'CZECHIA' => 'CZ',
|
||||||
|
'DENMARK' => 'DK',
|
||||||
|
'DJIBOUTI' => 'DJ',
|
||||||
|
'DOMINICA' => 'DM',
|
||||||
|
'DOMINICAN REPUBLIC' => 'DO',
|
||||||
|
'ECUADOR' => 'EC',
|
||||||
|
'EGYPT' => 'EG',
|
||||||
|
'EL SALVADOR' => 'SV',
|
||||||
|
'EQUATORIAL GUINEA' => 'GQ',
|
||||||
|
'ERITREA' => 'ER',
|
||||||
|
'ESTONIA' => 'EE',
|
||||||
|
'ESWATINI' => 'SZ',
|
||||||
|
'ETHIOPIA' => 'ET',
|
||||||
|
'FALKLAND ISLANDS' => 'FK',
|
||||||
|
'FAROE ISLANDS' => 'FO',
|
||||||
|
'FIJI' => 'FJ',
|
||||||
|
'FINLAND' => 'FI',
|
||||||
|
'FRANCE' => 'FR',
|
||||||
|
'FRENCH GUIANA' => 'GF',
|
||||||
|
'FRENCH POLYNESIA' => 'PF',
|
||||||
|
'FRENCH SOUTHERN TERRITORIES' => 'TF',
|
||||||
|
'GABON' => 'GA',
|
||||||
|
'GAMBIA' => 'GM',
|
||||||
|
'GEORGIA' => 'GE',
|
||||||
|
'GERMANY' => 'DE',
|
||||||
|
'GHANA' => 'GH',
|
||||||
|
'GIBRALTAR' => 'GI',
|
||||||
|
'GREECE' => 'GR',
|
||||||
|
'GREENLAND' => 'GL',
|
||||||
|
'GRENADA' => 'GD',
|
||||||
|
'GUADELOUPE' => 'GP',
|
||||||
|
'GUAM' => 'GU',
|
||||||
|
'GUATEMALA' => 'GT',
|
||||||
|
'GUERNSEY' => 'GG',
|
||||||
|
'GUINEA' => 'GN',
|
||||||
|
'GUINEA-BISSAU' => 'GW',
|
||||||
|
'GUYANA' => 'GY',
|
||||||
|
'HAITI' => 'HT',
|
||||||
|
'HEARD ISLAND AND MCDONALD ISLANDS' => 'HM',
|
||||||
|
'HOLY SEE' => 'VA',
|
||||||
|
'HONDURAS' => 'HN',
|
||||||
|
'HONG KONG' => 'HK',
|
||||||
|
'HUNGARY' => 'HU',
|
||||||
|
'ICELAND' => 'IS',
|
||||||
|
'INDIA' => 'IN',
|
||||||
|
'INDONESIA' => 'ID',
|
||||||
|
'IRAN' => 'IR',
|
||||||
|
'IRAQ' => 'IQ',
|
||||||
|
'IRELAND' => 'IE',
|
||||||
|
'ISLE OF MAN' => 'IM',
|
||||||
|
'ISRAEL' => 'IL',
|
||||||
|
'ITALY' => 'IT',
|
||||||
|
'JAMAICA' => 'JM',
|
||||||
|
'JAPAN' => 'JP',
|
||||||
|
'JERSEY' => 'JE',
|
||||||
|
'JORDAN' => 'JO',
|
||||||
|
'KAZAKHSTAN' => 'KZ',
|
||||||
|
'KENYA' => 'KE',
|
||||||
|
'KIRIBATI' => 'KI',
|
||||||
|
'KOREA, DEMOCRATIC PEOPLE\'S REPUBLIC OF' => 'KP',
|
||||||
|
'KOREA, REPUBLIC OF' => 'KR',
|
||||||
|
'KUWAIT' => 'KW',
|
||||||
|
'KYRGYZSTAN' => 'KG',
|
||||||
|
'LAO PEOPLE\'S DEMOCRATIC REPUBLIC' => 'LA',
|
||||||
|
'LATVIA' => 'LV',
|
||||||
|
'LEBANON' => 'LB',
|
||||||
|
'LESOTHO' => 'LS',
|
||||||
|
'LIBERIA' => 'LR',
|
||||||
|
'LIBYA' => 'LY',
|
||||||
|
'LIECHTENSTEIN' => 'LI',
|
||||||
|
'LITHUANIA' => 'LT',
|
||||||
|
'LUXEMBOURG' => 'LU',
|
||||||
|
'MACAO' => 'MO',
|
||||||
|
'MADAGASCAR' => 'MG',
|
||||||
|
'MALAWI' => 'MW',
|
||||||
|
'MALAYSIA' => 'MY',
|
||||||
|
'MALDIVES' => 'MV',
|
||||||
|
'MALI' => 'ML',
|
||||||
|
'MALTA' => 'MT',
|
||||||
|
'MARSHALL ISLANDS' => 'MH',
|
||||||
|
'MARTINIQUE' => 'MQ',
|
||||||
|
'MAURITANIA' => 'MR',
|
||||||
|
'MAURITIUS' => 'MU',
|
||||||
|
'MAYOTTE' => 'YT',
|
||||||
|
'MEXICO' => 'MX',
|
||||||
|
'MICRONESIA' => 'FM',
|
||||||
|
'MOLDOVA' => 'MD',
|
||||||
|
'MONACO' => 'MC',
|
||||||
|
'MONGOLIA' => 'MN',
|
||||||
|
'MONTENEGRO' => 'ME',
|
||||||
|
'MONTSERRAT' => 'MS',
|
||||||
|
'MOROCCO' => 'MA',
|
||||||
|
'MOZAMBIQUE' => 'MZ',
|
||||||
|
'MYANMAR' => 'MM',
|
||||||
|
'NAMIBIA' => 'NA',
|
||||||
|
'NAURU' => 'NR',
|
||||||
|
'NEPAL' => 'NP',
|
||||||
|
'NETHERLANDS' => 'NL',
|
||||||
|
'NEW CALEDONIA' => 'NC',
|
||||||
|
'NEW ZEALAND' => 'NZ',
|
||||||
|
'NICARAGUA' => 'NI',
|
||||||
|
'NIGER' => 'NE',
|
||||||
|
'NIGERIA' => 'NG',
|
||||||
|
'NIUE' => 'NU',
|
||||||
|
'NORFOLK ISLAND' => 'NF',
|
||||||
|
'NORTH MACEDONIA' => 'MK',
|
||||||
|
'NORTHERN MARIANA ISLANDS' => 'MP',
|
||||||
|
'NORWAY' => 'NO',
|
||||||
|
'OMAN' => 'OM',
|
||||||
|
'PAKISTAN' => 'PK',
|
||||||
|
'PALAU' => 'PW',
|
||||||
|
'PALESTINE, STATE OF' => 'PS',
|
||||||
|
'PANAMA' => 'PA',
|
||||||
|
'PAPUA NEW GUINEA' => 'PG',
|
||||||
|
'PARAGUAY' => 'PY',
|
||||||
|
'PERU' => 'PE',
|
||||||
|
'PHILIPPINES' => 'PH',
|
||||||
|
'PITCAIRN' => 'PN',
|
||||||
|
'POLAND' => 'PL',
|
||||||
|
'PORTUGAL' => 'PT',
|
||||||
|
'PUERTO RICO' => 'PR',
|
||||||
|
'QATAR' => 'QA',
|
||||||
|
'RÉUNION' => 'RE',
|
||||||
|
'ROMANIA' => 'RO',
|
||||||
|
'RUSSIAN FEDERATION' => 'RU',
|
||||||
|
'RWANDA' => 'RW',
|
||||||
|
'SAINT BARTHÉLEMY' => 'BL',
|
||||||
|
'SAINT HELENA, ASCENSION AND TRISTAN DA CUNHA' => 'SH',
|
||||||
|
'SAINT KITTS AND NEVIS' => 'KN',
|
||||||
|
'SAINT LUCIA' => 'LC',
|
||||||
|
'SAINT MARTIN' => 'MF',
|
||||||
|
'SAINT PIERRE AND MIQUELON' => 'PM',
|
||||||
|
'SAINT VINCENT AND THE GRENADINES' => 'VC',
|
||||||
|
'SAMOA' => 'WS',
|
||||||
|
'SAN MARINO' => 'SM',
|
||||||
|
'SAO TOME AND PRINCIPE' => 'ST',
|
||||||
|
'SAUDI ARABIA' => 'SA',
|
||||||
|
'SENEGAL' => 'SN',
|
||||||
|
'SERBIA' => 'RS',
|
||||||
|
'SEYCHELLES' => 'SC',
|
||||||
|
'SIERRA LEONE' => 'SL',
|
||||||
|
'SINGAPORE' => 'SG',
|
||||||
|
'SINT MAARTEN' => 'SX',
|
||||||
|
'SLOVAKIA' => 'SK',
|
||||||
|
'SLOVENIA' => 'SI',
|
||||||
|
'SOLOMON ISLANDS' => 'SB',
|
||||||
|
'SOMALIA' => 'SO',
|
||||||
|
'SOUTH AFRICA' => 'ZA',
|
||||||
|
'SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS' => 'GS',
|
||||||
|
'SOUTH SUDAN' => 'SS',
|
||||||
|
'SPAIN' => 'ES',
|
||||||
|
'SRI LANKA' => 'LK',
|
||||||
|
'SUDAN' => 'SD',
|
||||||
|
'SURINAME' => 'SR',
|
||||||
|
'SVALBARD AND JAN MAYEN' => 'SJ',
|
||||||
|
'SWEDEN' => 'SE',
|
||||||
|
'SWITZERLAND' => 'CH',
|
||||||
|
'SYRIAN ARAB REPUBLIC' => 'SY',
|
||||||
|
'TAIWAN' => 'TW',
|
||||||
|
'TAJIKISTAN' => 'TJ',
|
||||||
|
'TANZANIA' => 'TZ',
|
||||||
|
'THAILAND' => 'TH',
|
||||||
|
'TIMOR-LESTE' => 'TL',
|
||||||
|
'TOGO' => 'TG',
|
||||||
|
'TOKELAU' => 'TK',
|
||||||
|
'TONGA' => 'TO',
|
||||||
|
'TRINIDAD AND TOBAGO' => 'TT',
|
||||||
|
'TUNISIA' => 'TN',
|
||||||
|
'TÜRKIYE' => 'TR',
|
||||||
|
'TURKMENISTAN' => 'TM',
|
||||||
|
'TURKS AND CAICOS ISLANDS' => 'TC',
|
||||||
|
'TUVALU' => 'TV',
|
||||||
|
'UGANDA' => 'UG',
|
||||||
|
'UKRAINE' => 'UA',
|
||||||
|
'UNITED ARAB EMIRATES' => 'AE',
|
||||||
|
'UNITED KINGDOM' => 'GB',
|
||||||
|
'UNITED STATES OF AMERICA' => 'US',
|
||||||
|
'UNITED STATES MINOR OUTLYING ISLANDS' => 'UM',
|
||||||
|
'URUGUAY' => 'UY',
|
||||||
|
'UZBEKISTAN' => 'UZ',
|
||||||
|
'VANUATU' => 'VU',
|
||||||
|
'VENEZUELA' => 'VE',
|
||||||
|
'VIET NAM' => 'VN',
|
||||||
|
'VIRGIN ISLANDS, BRITISH' => 'VG',
|
||||||
|
'VIRGIN ISLANDS, U.S.' => 'VI',
|
||||||
|
'WALLIS AND FUTUNA' => 'WF',
|
||||||
|
'WESTERN SAHARA' => 'EH',
|
||||||
|
'YEMEN' => 'YE',
|
||||||
|
'ZAMBIA' => 'ZM',
|
||||||
|
'ZIMBABWE' => 'ZW',
|
||||||
|
'UNITED STATES' => 'US',
|
||||||
|
'USA' => 'US',
|
||||||
|
'U.S.A.' => 'US',
|
||||||
|
'U.S.' => 'US',
|
||||||
|
'UK' => 'GB',
|
||||||
|
'GREAT BRITAIN' => 'GB',
|
||||||
|
'ENGLAND' => 'GB',
|
||||||
|
'RUSSIA' => 'RU',
|
||||||
|
'SOUTH KOREA' => 'KR',
|
||||||
|
'NORTH KOREA' => 'KP',
|
||||||
|
'KOREA' => 'KR',
|
||||||
|
'VIETNAM' => 'VN',
|
||||||
|
'CZECH REPUBLIC' => 'CZ',
|
||||||
|
'SYRIA' => 'SY',
|
||||||
|
'LAOS' => 'LA',
|
||||||
|
'MACAU' => 'MO',
|
||||||
|
'HOLLAND' => 'NL',
|
||||||
|
'THE NETHERLANDS' => 'NL',
|
||||||
|
'IVORY COAST' => 'CI',
|
||||||
|
'TURKEY' => 'TR',
|
||||||
|
],
|
||||||
|
'alpha3' => [
|
||||||
|
'AFG' => 'AF',
|
||||||
|
'ALA' => 'AX',
|
||||||
|
'ALB' => 'AL',
|
||||||
|
'DZA' => 'DZ',
|
||||||
|
'ASM' => 'AS',
|
||||||
|
'AND' => 'AD',
|
||||||
|
'AGO' => 'AO',
|
||||||
|
'AIA' => 'AI',
|
||||||
|
'ATA' => 'AQ',
|
||||||
|
'ATG' => 'AG',
|
||||||
|
'ARG' => 'AR',
|
||||||
|
'ARM' => 'AM',
|
||||||
|
'ABW' => 'AW',
|
||||||
|
'AUS' => 'AU',
|
||||||
|
'AUT' => 'AT',
|
||||||
|
'AZE' => 'AZ',
|
||||||
|
'BHS' => 'BS',
|
||||||
|
'BHR' => 'BH',
|
||||||
|
'BGD' => 'BD',
|
||||||
|
'BRB' => 'BB',
|
||||||
|
'BLR' => 'BY',
|
||||||
|
'BEL' => 'BE',
|
||||||
|
'BLZ' => 'BZ',
|
||||||
|
'BEN' => 'BJ',
|
||||||
|
'BMU' => 'BM',
|
||||||
|
'BTN' => 'BT',
|
||||||
|
'BOL' => 'BO',
|
||||||
|
'BES' => 'BQ',
|
||||||
|
'BIH' => 'BA',
|
||||||
|
'BWA' => 'BW',
|
||||||
|
'BVT' => 'BV',
|
||||||
|
'BRA' => 'BR',
|
||||||
|
'IOT' => 'IO',
|
||||||
|
'BRN' => 'BN',
|
||||||
|
'BGR' => 'BG',
|
||||||
|
'BFA' => 'BF',
|
||||||
|
'BDI' => 'BI',
|
||||||
|
'CPV' => 'CV',
|
||||||
|
'KHM' => 'KH',
|
||||||
|
'CMR' => 'CM',
|
||||||
|
'CAN' => 'CA',
|
||||||
|
'CYM' => 'KY',
|
||||||
|
'CAF' => 'CF',
|
||||||
|
'TCD' => 'TD',
|
||||||
|
'CHL' => 'CL',
|
||||||
|
'CHN' => 'CN',
|
||||||
|
'CXR' => 'CX',
|
||||||
|
'CCK' => 'CC',
|
||||||
|
'COL' => 'CO',
|
||||||
|
'COM' => 'KM',
|
||||||
|
'COG' => 'CG',
|
||||||
|
'COD' => 'CD',
|
||||||
|
'COK' => 'CK',
|
||||||
|
'CRI' => 'CR',
|
||||||
|
'CIV' => 'CI',
|
||||||
|
'HRV' => 'HR',
|
||||||
|
'CUB' => 'CU',
|
||||||
|
'CUW' => 'CW',
|
||||||
|
'CYP' => 'CY',
|
||||||
|
'CZE' => 'CZ',
|
||||||
|
'DNK' => 'DK',
|
||||||
|
'DJI' => 'DJ',
|
||||||
|
'DMA' => 'DM',
|
||||||
|
'DOM' => 'DO',
|
||||||
|
'ECU' => 'EC',
|
||||||
|
'EGY' => 'EG',
|
||||||
|
'SLV' => 'SV',
|
||||||
|
'GNQ' => 'GQ',
|
||||||
|
'ERI' => 'ER',
|
||||||
|
'EST' => 'EE',
|
||||||
|
'SWZ' => 'SZ',
|
||||||
|
'ETH' => 'ET',
|
||||||
|
'FLK' => 'FK',
|
||||||
|
'FRO' => 'FO',
|
||||||
|
'FJI' => 'FJ',
|
||||||
|
'FIN' => 'FI',
|
||||||
|
'FRA' => 'FR',
|
||||||
|
'GUF' => 'GF',
|
||||||
|
'PYF' => 'PF',
|
||||||
|
'ATF' => 'TF',
|
||||||
|
'GAB' => 'GA',
|
||||||
|
'GMB' => 'GM',
|
||||||
|
'GEO' => 'GE',
|
||||||
|
'DEU' => 'DE',
|
||||||
|
'GHA' => 'GH',
|
||||||
|
'GIB' => 'GI',
|
||||||
|
'GRC' => 'GR',
|
||||||
|
'GRL' => 'GL',
|
||||||
|
'GRD' => 'GD',
|
||||||
|
'GLP' => 'GP',
|
||||||
|
'GUM' => 'GU',
|
||||||
|
'GTM' => 'GT',
|
||||||
|
'GGY' => 'GG',
|
||||||
|
'GIN' => 'GN',
|
||||||
|
'GNB' => 'GW',
|
||||||
|
'GUY' => 'GY',
|
||||||
|
'HTI' => 'HT',
|
||||||
|
'HMD' => 'HM',
|
||||||
|
'VAT' => 'VA',
|
||||||
|
'HND' => 'HN',
|
||||||
|
'HKG' => 'HK',
|
||||||
|
'HUN' => 'HU',
|
||||||
|
'ISL' => 'IS',
|
||||||
|
'IND' => 'IN',
|
||||||
|
'IDN' => 'ID',
|
||||||
|
'IRN' => 'IR',
|
||||||
|
'IRQ' => 'IQ',
|
||||||
|
'IRL' => 'IE',
|
||||||
|
'IMN' => 'IM',
|
||||||
|
'ISR' => 'IL',
|
||||||
|
'ITA' => 'IT',
|
||||||
|
'JAM' => 'JM',
|
||||||
|
'JPN' => 'JP',
|
||||||
|
'JEY' => 'JE',
|
||||||
|
'JOR' => 'JO',
|
||||||
|
'KAZ' => 'KZ',
|
||||||
|
'KEN' => 'KE',
|
||||||
|
'KIR' => 'KI',
|
||||||
|
'PRK' => 'KP',
|
||||||
|
'KOR' => 'KR',
|
||||||
|
'KWT' => 'KW',
|
||||||
|
'KGZ' => 'KG',
|
||||||
|
'LAO' => 'LA',
|
||||||
|
'LVA' => 'LV',
|
||||||
|
'LBN' => 'LB',
|
||||||
|
'LSO' => 'LS',
|
||||||
|
'LBR' => 'LR',
|
||||||
|
'LBY' => 'LY',
|
||||||
|
'LIE' => 'LI',
|
||||||
|
'LTU' => 'LT',
|
||||||
|
'LUX' => 'LU',
|
||||||
|
'MAC' => 'MO',
|
||||||
|
'MDG' => 'MG',
|
||||||
|
'MWI' => 'MW',
|
||||||
|
'MYS' => 'MY',
|
||||||
|
'MDV' => 'MV',
|
||||||
|
'MLI' => 'ML',
|
||||||
|
'MLT' => 'MT',
|
||||||
|
'MHL' => 'MH',
|
||||||
|
'MTQ' => 'MQ',
|
||||||
|
'MRT' => 'MR',
|
||||||
|
'MUS' => 'MU',
|
||||||
|
'MYT' => 'YT',
|
||||||
|
'MEX' => 'MX',
|
||||||
|
'FSM' => 'FM',
|
||||||
|
'MDA' => 'MD',
|
||||||
|
'MCO' => 'MC',
|
||||||
|
'MNG' => 'MN',
|
||||||
|
'MNE' => 'ME',
|
||||||
|
'MSR' => 'MS',
|
||||||
|
'MAR' => 'MA',
|
||||||
|
'MOZ' => 'MZ',
|
||||||
|
'MMR' => 'MM',
|
||||||
|
'NAM' => 'NA',
|
||||||
|
'NRU' => 'NR',
|
||||||
|
'NPL' => 'NP',
|
||||||
|
'NLD' => 'NL',
|
||||||
|
'NCL' => 'NC',
|
||||||
|
'NZL' => 'NZ',
|
||||||
|
'NIC' => 'NI',
|
||||||
|
'NER' => 'NE',
|
||||||
|
'NGA' => 'NG',
|
||||||
|
'NIU' => 'NU',
|
||||||
|
'NFK' => 'NF',
|
||||||
|
'MKD' => 'MK',
|
||||||
|
'MNP' => 'MP',
|
||||||
|
'NOR' => 'NO',
|
||||||
|
'OMN' => 'OM',
|
||||||
|
'PAK' => 'PK',
|
||||||
|
'PLW' => 'PW',
|
||||||
|
'PSE' => 'PS',
|
||||||
|
'PAN' => 'PA',
|
||||||
|
'PNG' => 'PG',
|
||||||
|
'PRY' => 'PY',
|
||||||
|
'PER' => 'PE',
|
||||||
|
'PHL' => 'PH',
|
||||||
|
'PCN' => 'PN',
|
||||||
|
'POL' => 'PL',
|
||||||
|
'PRT' => 'PT',
|
||||||
|
'PRI' => 'PR',
|
||||||
|
'QAT' => 'QA',
|
||||||
|
'REU' => 'RE',
|
||||||
|
'ROU' => 'RO',
|
||||||
|
'RUS' => 'RU',
|
||||||
|
'RWA' => 'RW',
|
||||||
|
'BLM' => 'BL',
|
||||||
|
'SHN' => 'SH',
|
||||||
|
'KNA' => 'KN',
|
||||||
|
'LCA' => 'LC',
|
||||||
|
'MAF' => 'MF',
|
||||||
|
'SPM' => 'PM',
|
||||||
|
'VCT' => 'VC',
|
||||||
|
'WSM' => 'WS',
|
||||||
|
'SMR' => 'SM',
|
||||||
|
'STP' => 'ST',
|
||||||
|
'SAU' => 'SA',
|
||||||
|
'SEN' => 'SN',
|
||||||
|
'SRB' => 'RS',
|
||||||
|
'SYC' => 'SC',
|
||||||
|
'SLE' => 'SL',
|
||||||
|
'SGP' => 'SG',
|
||||||
|
'SXM' => 'SX',
|
||||||
|
'SVK' => 'SK',
|
||||||
|
'SVN' => 'SI',
|
||||||
|
'SLB' => 'SB',
|
||||||
|
'SOM' => 'SO',
|
||||||
|
'ZAF' => 'ZA',
|
||||||
|
'SGS' => 'GS',
|
||||||
|
'SSD' => 'SS',
|
||||||
|
'ESP' => 'ES',
|
||||||
|
'LKA' => 'LK',
|
||||||
|
'SDN' => 'SD',
|
||||||
|
'SUR' => 'SR',
|
||||||
|
'SJM' => 'SJ',
|
||||||
|
'SWE' => 'SE',
|
||||||
|
'CHE' => 'CH',
|
||||||
|
'SYR' => 'SY',
|
||||||
|
'TWN' => 'TW',
|
||||||
|
'TJK' => 'TJ',
|
||||||
|
'TZA' => 'TZ',
|
||||||
|
'THA' => 'TH',
|
||||||
|
'TLS' => 'TL',
|
||||||
|
'TGO' => 'TG',
|
||||||
|
'TKL' => 'TK',
|
||||||
|
'TON' => 'TO',
|
||||||
|
'TTO' => 'TT',
|
||||||
|
'TUN' => 'TN',
|
||||||
|
'TUR' => 'TR',
|
||||||
|
'TKM' => 'TM',
|
||||||
|
'TCA' => 'TC',
|
||||||
|
'TUV' => 'TV',
|
||||||
|
'UGA' => 'UG',
|
||||||
|
'UKR' => 'UA',
|
||||||
|
'ARE' => 'AE',
|
||||||
|
'GBR' => 'GB',
|
||||||
|
'USA' => 'US',
|
||||||
|
'UMI' => 'UM',
|
||||||
|
'URY' => 'UY',
|
||||||
|
'UZB' => 'UZ',
|
||||||
|
'VUT' => 'VU',
|
||||||
|
'VEN' => 'VE',
|
||||||
|
'VNM' => 'VN',
|
||||||
|
'VGB' => 'VG',
|
||||||
|
'VIR' => 'VI',
|
||||||
|
'WLF' => 'WF',
|
||||||
|
'ESH' => 'EH',
|
||||||
|
'YEM' => 'YE',
|
||||||
|
'ZMB' => 'ZM',
|
||||||
|
'ZWE' => 'ZW',
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -63,6 +63,10 @@ class InstallSchema
|
|||||||
`accept_language_raw` text DEFAULT NULL COMMENT 'Accept-Language 完整值',
|
`accept_language_raw` text DEFAULT NULL COMMENT 'Accept-Language 完整值',
|
||||||
`judge_timing` text DEFAULT NULL COMMENT '判定用时 JSON',
|
`judge_timing` text DEFAULT NULL COMMENT '判定用时 JSON',
|
||||||
`fp_hdata` text DEFAULT NULL COMMENT '指纹 hdata 解码 JSON',
|
`fp_hdata` text DEFAULT NULL COMMENT '指纹 hdata 解码 JSON',
|
||||||
|
`api_by_request` text DEFAULT NULL COMMENT 'byApi 请求参数 JSON',
|
||||||
|
`api_by_response` text DEFAULT NULL COMMENT 'byApi 响应 JSON',
|
||||||
|
`api_risk_request` text DEFAULT NULL COMMENT 'byApiRisk 请求参数 JSON',
|
||||||
|
`api_risk_response` text DEFAULT NULL COMMENT 'byApiRisk 响应 JSON',
|
||||||
`add_time` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '日志记录时间',
|
`add_time` timestamp NOT NULL DEFAULT current_timestamp() COMMENT '日志记录时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `IP` (`IP`),
|
KEY `IP` (`IP`),
|
||||||
|
|||||||
@@ -23,8 +23,11 @@ require_once __DIR__ . '/Cloak/PipelineTimer.php';
|
|||||||
require_once __DIR__ . '/Cloak/ClientIpResolver.php';
|
require_once __DIR__ . '/Cloak/ClientIpResolver.php';
|
||||||
require_once __DIR__ . '/Cloak/GeoIpCountryCache.php';
|
require_once __DIR__ . '/Cloak/GeoIpCountryCache.php';
|
||||||
require_once __DIR__ . '/Cloak/GeoIpCountryResolver.php';
|
require_once __DIR__ . '/Cloak/GeoIpCountryResolver.php';
|
||||||
|
require_once __DIR__ . '/Cloak/CountryCodeNormalizer.php';
|
||||||
require_once __DIR__ . '/Cloak/CountryAllowlist.php';
|
require_once __DIR__ . '/Cloak/CountryAllowlist.php';
|
||||||
|
require_once __DIR__ . '/Cloak/RiskSecondaryGate.php';
|
||||||
require_once __DIR__ . '/Cloak/VisitorLogSchema.php';
|
require_once __DIR__ . '/Cloak/VisitorLogSchema.php';
|
||||||
|
require_once __DIR__ . '/Cloak/VisitorApiAudit.php';
|
||||||
require_once __DIR__ . '/Cloak/VisitorLogQueue.php';
|
require_once __DIR__ . '/Cloak/VisitorLogQueue.php';
|
||||||
require_once __DIR__ . '/Cloak/VisitorLogWorker.php';
|
require_once __DIR__ . '/Cloak/VisitorLogWorker.php';
|
||||||
require_once __DIR__ . '/Cloak/Pipeline/CheckPipeline.php';
|
require_once __DIR__ . '/Cloak/Pipeline/CheckPipeline.php';
|
||||||
|
|||||||
Binary file not shown.
@@ -183,8 +183,32 @@ if(empty($_SESSION['password']) || $_SESSION['password'] != LOGIN_PASSWORD) {
|
|||||||
.cell-copy-link:hover { color: var(--log-primary-dark); }
|
.cell-copy-link:hover { color: var(--log-primary-dark); }
|
||||||
.cell-ip { font-family: 'SF Mono', 'Consolas', monospace; font-size: 12px; }
|
.cell-ip { font-family: 'SF Mono', 'Consolas', monospace; font-size: 12px; }
|
||||||
.text-muted { color: #adb5bd; }
|
.text-muted { color: #adb5bd; }
|
||||||
.fp-hdata-cell { display: flex; flex-direction: column; gap: 4px; max-width: 100%; }
|
.fp-hdata-cell {
|
||||||
.fp-hdata-summary { font-size: 11.5px; color: #475569; line-height: 1.35; }
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px 8px;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 24px;
|
||||||
|
}
|
||||||
|
.fp-hdata-summary {
|
||||||
|
flex: 1 1 140px;
|
||||||
|
min-width: 0;
|
||||||
|
font-size: 11.5px;
|
||||||
|
color: #475569;
|
||||||
|
line-height: 1.35;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.fp-hdata-actions {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
.fp-hdata-btn {
|
.fp-hdata-btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 1px 8px;
|
padding: 1px 8px;
|
||||||
@@ -197,6 +221,28 @@ if(empty($_SESSION['password']) || $_SESSION['password'] != LOGIN_PASSWORD) {
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
.fp-hdata-btn:hover { background: #d1ecf1; }
|
.fp-hdata-btn:hover { background: #d1ecf1; }
|
||||||
|
/* 指纹 / API 审计列:解除 layui 固定行高,避免「判定详情」等按钮被裁切 */
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="fp_hdata_text"],
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="api_by_request_text"],
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="api_by_response_text"],
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="api_risk_request_text"],
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="api_risk_response_text"] {
|
||||||
|
height: auto !important;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="fp_hdata_text"] > .layui-table-cell,
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="api_by_request_text"] > .layui-table-cell,
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="api_by_response_text"] > .layui-table-cell,
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="api_risk_request_text"] > .layui-table-cell,
|
||||||
|
.log-table-wrap .layui-table-body td[data-field="api_risk_response_text"] > .layui-table-cell {
|
||||||
|
height: auto !important;
|
||||||
|
max-height: none !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
line-height: 1.35;
|
||||||
|
padding-top: 7px !important;
|
||||||
|
padding-bottom: 7px !important;
|
||||||
|
}
|
||||||
.fp-risk-explain { font-size: 13px; line-height: 1.55; color: #2c3e50; }
|
.fp-risk-explain { font-size: 13px; line-height: 1.55; color: #2c3e50; }
|
||||||
.fp-risk-explain-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
|
.fp-risk-explain-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
|
||||||
.fp-risk-tag { font-size: 12px; padding: 2px 10px; border-radius: 999px; background: #eef2ff; color: #3730a3; font-weight: 600; }
|
.fp-risk-tag { font-size: 12px; padding: 2px 10px; border-radius: 999px; background: #eef2ff; color: #3730a3; font-weight: 600; }
|
||||||
@@ -455,6 +501,26 @@ layui.use(function(){
|
|||||||
return '<span class="cloak-badge ' + cls + ' reason-badge" title="' + esc + '">' + short + '</span>';
|
return '<span class="cloak-badge ' + cls + ' reason-badge" title="' + esc + '">' + short + '</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatJsonPretty(str) {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(JSON.parse(str), null, 2);
|
||||||
|
} catch (e) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cellApiAudit(val, title) {
|
||||||
|
if (val == null || val === '' || val === '-') {
|
||||||
|
return '<span class="text-muted">—</span>';
|
||||||
|
}
|
||||||
|
var text = String(val);
|
||||||
|
var short = text.length > 40 ? text.slice(0, 40) + '…' : text;
|
||||||
|
return '<div class="fp-hdata-cell">'
|
||||||
|
+ '<span class="fp-hdata-summary" title="' + escapeAttr(text) + '">' + escapeHtml(short) + '</span>'
|
||||||
|
+ '<span class="fp-hdata-actions"><span class="fp-hdata-btn fp-hdata-btn-json" data-full="' + escapeAttr(text) + '" data-title="' + escapeAttr(title || 'API 数据') + '">查看 JSON</span></span>'
|
||||||
|
+ '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
function cellFpHdata(d) {
|
function cellFpHdata(d) {
|
||||||
if (!d.fp_hdata && !d.fp_hdata_text) {
|
if (!d.fp_hdata && !d.fp_hdata_text) {
|
||||||
return '<span class="text-muted">—</span>';
|
return '<span class="text-muted">—</span>';
|
||||||
@@ -465,14 +531,16 @@ layui.use(function(){
|
|||||||
if (summary) {
|
if (summary) {
|
||||||
html += '<span class="fp-hdata-summary" title="' + escapeAttr(summary) + '">' + escapeHtml(summary.length > 48 ? summary.slice(0, 48) + '…' : summary) + '</span>';
|
html += '<span class="fp-hdata-summary" title="' + escapeAttr(summary) + '">' + escapeHtml(summary.length > 48 ? summary.slice(0, 48) + '…' : summary) + '</span>';
|
||||||
}
|
}
|
||||||
|
html += '<span class="fp-hdata-actions">';
|
||||||
if (d.fp_hdata_explain_html) {
|
if (d.fp_hdata_explain_html) {
|
||||||
html += '<span class="fp-hdata-btn" data-explain-id="' + escapeAttr(String(d.id)) + '">判定详情</span>';
|
html += '<span class="fp-hdata-btn" data-explain-id="' + escapeAttr(String(d.id)) + '">判定详情</span>';
|
||||||
window.__fpExplainCache = window.__fpExplainCache || {};
|
window.__fpExplainCache = window.__fpExplainCache || {};
|
||||||
window.__fpExplainCache[d.id] = d.fp_hdata_explain_html;
|
window.__fpExplainCache[d.id] = d.fp_hdata_explain_html;
|
||||||
} else if (d.fp_hdata || d.fp_hdata_text) {
|
}
|
||||||
|
if (d.fp_hdata || d.fp_hdata_text) {
|
||||||
html += '<span class="fp-hdata-btn fp-hdata-btn-raw" data-full="' + escapeAttr(d.fp_hdata || d.fp_hdata_text) + '">原始 hdata</span>';
|
html += '<span class="fp-hdata-btn fp-hdata-btn-raw" data-full="' + escapeAttr(d.fp_hdata || d.fp_hdata_text) + '">原始 hdata</span>';
|
||||||
}
|
}
|
||||||
html += '</div>';
|
html += '</span></div>';
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,6 +570,10 @@ layui.use(function(){
|
|||||||
{ field: 'http_referer', title: 'HTTP-Referer', width: 260, defaultVisible: false, templet: function(d){ return cellLinkEllipsis(d.http_referer, LINK_CELL_MAX); } },
|
{ field: 'http_referer', title: 'HTTP-Referer', width: 260, defaultVisible: false, templet: function(d){ return cellLinkEllipsis(d.http_referer, LINK_CELL_MAX); } },
|
||||||
{ field: 'accept_language_raw', title: 'Accept-Language', width: 220, defaultVisible: false, templet: function(d){ return cellEllipsis(d.accept_language_raw, 32); } },
|
{ field: 'accept_language_raw', title: 'Accept-Language', width: 220, defaultVisible: false, templet: function(d){ return cellEllipsis(d.accept_language_raw, 32); } },
|
||||||
{ field: 'fp_hdata_text', title: '指纹 hdata', width: 360, defaultVisible: false, templet: function(d){ return cellFpHdata(d); } },
|
{ field: 'fp_hdata_text', title: '指纹 hdata', width: 360, defaultVisible: false, templet: function(d){ return cellFpHdata(d); } },
|
||||||
|
{ field: 'api_by_request_text', title: 'byApi 请求参数', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_by_request_text, 'byApi 请求参数 #' + d.id); } },
|
||||||
|
{ field: 'api_by_response_text', title: 'byApi 返回值', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_by_response_text, 'byApi 返回值 #' + d.id); } },
|
||||||
|
{ field: 'api_risk_request_text', title: 'byApiRisk 请求参数', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_risk_request_text, 'byApiRisk 请求参数 #' + d.id); } },
|
||||||
|
{ field: 'api_risk_response_text', title: 'byApiRisk 返回值', width: 300, defaultVisible: false, templet: function(d){ return cellApiAudit(d.api_risk_response_text, 'byApiRisk 返回值 #' + d.id); } },
|
||||||
];
|
];
|
||||||
|
|
||||||
var COLUMN_DEF_MAP = {};
|
var COLUMN_DEF_MAP = {};
|
||||||
@@ -598,8 +670,27 @@ layui.use(function(){
|
|||||||
return [cols];
|
return [cols];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fixRichTableCellHeights() {
|
||||||
|
var fields = ['fp_hdata_text', 'api_by_request_text', 'api_by_response_text', 'api_risk_request_text', 'api_risk_response_text'];
|
||||||
|
fields.forEach(function(field) {
|
||||||
|
$('.log-table-wrap .layui-table-body td[data-field="' + field + '"]').each(function() {
|
||||||
|
var $cell = $(this).children('.layui-table-cell');
|
||||||
|
$cell.css({ height: 'auto', whiteSpace: 'normal', overflow: 'visible' });
|
||||||
|
$(this).css('height', 'auto');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function reloadTableCols() {
|
function reloadTableCols() {
|
||||||
table.reload(TABLE_ID, { cols: buildCols(colConfig) });
|
table.reload(TABLE_ID, {
|
||||||
|
cols: buildCols(colConfig),
|
||||||
|
done: function(res, curr, count) {
|
||||||
|
if (typeof count === 'number') {
|
||||||
|
$("#total_nums").text(count);
|
||||||
|
}
|
||||||
|
fixRichTableCellHeights();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderColSettingsList() {
|
function renderColSettingsList() {
|
||||||
@@ -736,8 +827,15 @@ layui.use(function(){
|
|||||||
return 'color:#856404;background-color:#fff8e6;';
|
return 'color:#856404;background-color:#fff8e6;';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
lineStyle: function(row) {
|
||||||
|
if (row.fp_hdata || row.fp_hdata_text || row.fp_hdata_explain_html) {
|
||||||
|
return 'height:auto;';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
done: function(res, curr, count, origin){
|
done: function(res, curr, count, origin){
|
||||||
$("#total_nums").text(count);
|
$("#total_nums").text(count);
|
||||||
|
fixRichTableCellHeights();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -758,12 +856,16 @@ layui.use(function(){
|
|||||||
}
|
}
|
||||||
var full = this.getAttribute('data-full') || '';
|
var full = this.getAttribute('data-full') || '';
|
||||||
if (full) {
|
if (full) {
|
||||||
|
var popupTitle = this.getAttribute('data-title') || '指纹 hdata';
|
||||||
|
var body = this.classList.contains('fp-hdata-btn-json')
|
||||||
|
? escapeHtml(formatJsonPretty(full))
|
||||||
|
: escapeHtml(full);
|
||||||
layer.open({
|
layer.open({
|
||||||
type: 1,
|
type: 1,
|
||||||
title: '指纹 hdata',
|
title: popupTitle,
|
||||||
area: ['680px', '70vh'],
|
area: ['680px', '70vh'],
|
||||||
shadeClose: true,
|
shadeClose: true,
|
||||||
content: '<pre style="padding:14px;margin:0;max-height:calc(70vh - 50px);overflow:auto;font-size:12px;white-space:pre-wrap;word-break:break-all;">' + escapeHtml(full) + '</pre>'
|
content: '<pre style="padding:14px;margin:0;max-height:calc(70vh - 50px);overflow:auto;font-size:12px;white-space:pre-wrap;word-break:break-all;">' + body + '</pre>'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,6 +51,13 @@ if ($tests['resolver_available']) {
|
|||||||
if (!$tests['allowlist_us'] || !$tests['allowlist_cn']) {
|
if (!$tests['allowlist_us'] || !$tests['allowlist_cn']) {
|
||||||
$ok = false;
|
$ok = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$tests['normalize_iso2'] = CountryCodeNormalizer::toAlpha2('us') === 'US';
|
||||||
|
$tests['normalize_name'] = CountryCodeNormalizer::toAlpha2('United States') === 'US';
|
||||||
|
$tests['allowlist_name'] = CountryAllowlist::isAllowed('United Kingdom', CountryAllowlist::parse('GB,US'));
|
||||||
|
if (!$tests['normalize_iso2'] || !$tests['normalize_name'] || !$tests['allowlist_name']) {
|
||||||
|
$ok = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
|
|||||||
Reference in New Issue
Block a user