<?php /** * Created by PhpStorm. * User: zhl * Date: 2023/09/02 * Time: 10:36 */ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Models\SyncSubmitTask\SyncSubmitTask; use Illuminate\Http\Exceptions\HttpResponseException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Validator; use ZipArchive; /** * Class NoticeController * @package App\Http\Controllers\Api */ class NoticeController extends Controller { const SUCCESS = 200; const ERROR = 400; const SERVERERROR = 500; const TYPEVISIT = "visit"; const TYPEINQUIRY = "inquiry"; const TRAFFICZERO = 0; const TRAFFICONE = 1; const DEVICE_PORT_ONE = 1; const DEVICE_PORT_TWO = 2; protected $header = [];//设置请求头参数 /** * @param array $data * @param string $message * @param int $status * @return string */ protected function success($data = [], $message = 'success', $status = 200) { $array = compact('status', 'message', 'data'); return json_encode($array, JSON_UNESCAPED_UNICODE); } /** * @param int $status * @param string $message * @param array $data * @return string */ protected function error($message = 'error', $status = 400, $data = []) { $array = compact('status', 'message', 'data'); return json_encode($array, JSON_UNESCAPED_UNICODE); } /** * 响应 * @param null $msg * @param int $code * @param array $data * @param int $result_code * @param string $type * @return void */ public function response($msg = null, $code = self::SUCCESS, $data = [], $result_code = 200, $type = 'application/json') { $result = [ 'msg' => $msg, 'code' => $code, 'data' => $data, ]; $this->header['Content-Type'] = $type; $response = response($result, $result_code, $this->header); throw new HttpResponseException($response); } /** * GET请求 * @param $url * @return mixed */ public function curlGet($url) { $ch1 = curl_init(); $timeout = 0; curl_setopt($ch1, CURLOPT_URL, $url); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch1, CURLOPT_ENCODING, ''); curl_setopt($ch1, CURLOPT_MAXREDIRS, 10); curl_setopt($ch1, CURLOPT_HTTPHEADER, array()); curl_setopt($ch1, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($ch1, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch1, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch1, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch1, CURLOPT_CUSTOMREQUEST, "GET"); curl_setopt($ch1, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); $access_txt = curl_exec($ch1); curl_close($ch1); return json_decode($access_txt, true); } /** * 发送http post请求 * @param $url * @param $data * @param array $header * @param bool $is_json * @return mixed|string */ function httpPost($url, $data, $header = [], $is_json = true) { if (empty($header)) { $header = array( "Accept: application/json", "Content-Type:application/json;charset=utf-8", "token:" . env("SECRET_TOKEN"), "pid:" . env("MERCHANT_NUMBER") ); } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)'); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $res = curl_exec($ch); if (curl_errno($ch)) { $error_message = curl_error($ch); @file_put_contents(storage_path('logs/error.log'), var_export($error_message, true) . PHP_EOL, FILE_APPEND); } curl_close($ch); if ($is_json) { return json_decode($res, true); } return trim($res); } /** * A端上传验证代码 * @param Request $request * @return string */ public function uploadVerifyFile(Request $request) { $domain = $request->getHost(); $files = $request->allFiles(); $file_name = ''; foreach ($files as $file) { $destinationPath = public_path($domain); $file->move($destinationPath, $file->getClientOriginalName()); $filePath = $destinationPath . '/' . $file->getClientOriginalName(); @file_put_contents(storage_path('logs/upload_file.log'), date('Y-m-d H:i:s') . " " . $filePath . PHP_EOL, FILE_APPEND); $file_name = $file->getClientOriginalName(); } if ($file_name) { $transmitUrl = env("TRANSMIT_URL"); $this->httpPost($transmitUrl . "api/selfSiteVerify/", json_encode(['file_name' => $file_name, 'type' => 1])); } return $this->success(); } /** * A端上传amp站验证代码 * @param Request $request * @return string */ public function uploadAmpVerifyFile(Request $request) { $domain = $request->getHost(); $domain_array = parse_url($domain); $host = $domain_array['host'] ? $domain_array['path'] : ""; $host_array = explode('.', $host); if (count($host_array) <= 2) { array_unshift($host_array, 'm'); } else { $host_array[0] = 'm'; } $amp_domain = implode('.', $host_array); $files = $request->allFiles(); $file_name = ''; foreach ($files as $file) { $destinationPath = public_path($amp_domain); $file->move($destinationPath, $file->getClientOriginalName()); $filePath = $destinationPath . '/' . $file->getClientOriginalName(); @file_put_contents(storage_path('logs/upload_file.log'), date('Y-m-d H:i:s') . " " . $filePath . PHP_EOL, FILE_APPEND); $file_name = $file->getClientOriginalName(); } if ($file_name) { $transmitUrl = env("TRANSMIT_URL"); $this->httpPost($transmitUrl . "api/selfSiteVerify/", json_encode(['file_name' => $file_name, 'type' => 2])); } return $this->success(); } /** * 获取需要下载的文件url * @param Request $request * @return string */ public function websiteHtml(Request $request) { $domain = $request->getHost(); $site_token = $request->input('site_token'); $zip_count = $request->input('zip_count'); $token = env("SECRET_TOKEN"); $pid = env("MERCHANT_NUMBER"); $apiUrl = env("API_URL"); if (empty($site_token)) { return $this->error('请求无效'); } if ($site_token != $token) { return $this->error('请求无效'); } $requestUrl = $apiUrl . "api/get_url_verify_token/?pid=" . $pid . "&token=" . $token . "&domain=" . $domain; @file_put_contents(storage_path('logs/notify_get_url.log'), date('Y-m-d H:i:s') . "接收到通知:" . $requestUrl . PHP_EOL, FILE_APPEND); try { $res = $this->curlGet($requestUrl); if ($res["status"] != self::SUCCESS) { $msg = isset($res["message"]) && !empty($res["message"]) ? $res["message"] : "请求失败!"; return $this->error($msg); } } catch (\Exception $e) { return $this->error($e->getMessage()); } $info = [ "domain" => $domain, "zip_count" => $zip_count ]; $info = json_encode($info); Redis::set('handle_html', $info); return $this->success(); } /** * 网站html解压 * @param $zip_count * @param $domain */ public function websiteHtmlHandle($zip_count, $domain) { $api_url = env('API_URL'); for ($i = 0; $i <= $zip_count; $i++) { $targetFile = $this->downLoadFile($api_url . $domain . '_part' . $i . '.zip'); if ($targetFile == "") { $this->output('文件 ' . $targetFile . ' 不存在'); continue; } $zip = new ZipArchive(); if ($zip->open($targetFile) === TRUE) { $outputFolder = public_path($domain); if (!is_dir($outputFolder)) { mkdir($outputFolder, 0777, true); } // 解压缩文件,保留原文件结构 $zip->extractTo($outputFolder); $zip->close(); $this->deleteDirectory($targetFile); } else { // 处理打开压缩文件失败的情况 $this->output('解压文件 ' . $targetFile . ' 失败'); continue; } } $transmitUrl = env("TRANSMIT_URL"); $this->httpPost($transmitUrl . "api/selfSiteNotify/", json_encode(['domain' => $domain])); } /** * 下载文件(下载到public目录下) * @param $url * @return string */ public function downLoadFile($url) { $savePath = public_path(); if (!file_exists($savePath)) { mkdir($savePath, 0777, true); } $targetFile = $savePath . '/' . basename($url); if (!file_exists($targetFile)) { $file = fopen($targetFile, 'w'); fclose($file); chmod($targetFile, 0755); } $remoteFile = fopen($url, 'rb'); $localFile = fopen($targetFile, 'wb'); if ($remoteFile && $localFile) { while (!feof($remoteFile)) { fwrite($localFile, fread($remoteFile, 1024 * 8), 1024 * 8); } fclose($remoteFile); fclose($localFile); return $targetFile; } else { return ""; } // $context = stream_context_create([ // 'http' => [ // 'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", // ], // ]); // $fileContent = @file_get_contents($url, false, $context); // if ($fileContent !== false) { // file_put_contents($targetFile, $fileContent); // return $targetFile; // } else { // return ""; // } } /** * 删除文件 * @param $path * @return bool */ public function deleteDirectory($path) { if (!is_dir($path)) { try { unlink($path); } catch (\Exception $e) { return true; } return true; } $files = array_diff(scandir($path), array('.', '..')); foreach ($files as $file) { $filePath = $path . '/' . $file; if (is_dir($filePath)) { $this->deleteDirectory($filePath); } else { try { unlink($filePath); } catch (\Exception $e) { return true; } } } rmdir($path); return true; } /** * 提交 * @param $request * @param string $type * @param int $traffic * @return string */ public function transmit($request, $type = self::TYPEVISIT, $traffic = self::TRAFFICZERO) { if ($request->getClientIp() == "127.0.0.1") { return $this->success(); } //判断是否是爬虫 $isReptile = $this->isReptile($request); if ($isReptile) { return $this->success(); } $data = $request->all(); if (empty($data)) { return $this->success(); } $data["device_port"] = $this->userAgentHandle($request->userAgent(), $data["device_port"] ?? self::DEVICE_PORT_ONE); $req["data"] = $data; $referrer_url = $data["referrer_url"] ?? $request->header('Referer'); if ($type == self::TYPEVISIT) { $referrer_url = $this->visitInfoHandle($referrer_url); } $req["referer"] = $referrer_url; $req["domain"] = $request->getHost(); $req["ip"] = $request->getClientIp(); $req["user_agent"] = $request->userAgent(); $req["files"] = isset($data["files"]) ? $data["files"] : null; $req["type"] = $type; $req["traffic"] = $traffic; $req["timestamp"] = time(); //转发接口 $transmitUrl = env("TRANSMIT_URL"); $resp = $this->httpPost($transmitUrl . "api/selfSiteApi/", json_encode($req)); if ($resp["status"] != self::SUCCESS) { $dataJson = json_encode($req); if (!file_exists(storage_path('logs/fail_req'))) { mkdir(storage_path('logs/fail_req', 0777, true)); } @file_put_contents(storage_path('logs/fail_req/' . date('Y-m-d') . '.log'), var_export($dataJson, true) . PHP_EOL, FILE_APPEND); } return $this->success(); } /** * 处理user_agent * @param $user_agent * @param $device_port * @return int */ public function userAgentHandle($user_agent, $device_port) { if (!in_array($device_port, [self::DEVICE_PORT_ONE, self::DEVICE_PORT_TWO])) { $device_port = self::DEVICE_PORT_ONE; } // 头信息中带有这些信息, 代表是手机端, 重置设备类型 if (preg_match('/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|wap|windowsce|ucweb/', $user_agent)) { $device_port = self::DEVICE_PORT_TWO; } return $device_port; } /** * 客户访问埋点接口 * @param Request $request * @return string */ public function customerVisit(Request $request) { return $this->transmit($request); } /** * 埋点信息处理 * @param $referrerUrl * @return string */ public function visitInfoHandle($referrerUrl) { if (preg_match('/google|facebook|bing|yahoo|youtobe|linkedin|messefrankfurt|yandex|tiktok|twitter|instagram|reddit|telegram|pinterest|tumblr/', $referrerUrl)) { } else if ($referrerUrl == null) { //直访用户 $referrerUrl = ""; } else { $referrerUrl = "https://www.google.com/"; } return $referrerUrl; } /** * 是否是爬虫访问 * @param $request * @return bool */ public function isReptile($request) { $agent = $request->header('User-Agent'); if (!empty($agent)) { $spiderSite = array( "TencentTraveler", "Baiduspider+", "BaiduGame", "Googlebot", "msnbot", "Sosospider+", "Sogou web spider", "ia_archiver", "Yahoo! Slurp", "YoudaoBot", "Yahoo Slurp", "MSNBot", "Java (Often spam bot)", "BaiDuSpider", "Voila", "Yandex bot", "BSpider", "twiceler", "Sogou Spider", "Speedy Spider", "Google AdSense", "Heritrix", "Python-urllib", "Alexa (IA Archiver)", "Ask", "Exabot", "Custo", "OutfoxBot/YodaoBot", "yacy", "SurveyBot", "legs", "lwp-trivial", "Nutch", "StackRambler", "The web archive (IA Archiver)", "Perl tool", "MJ12bot", "Netcraft", "MSIECrawler", "WGet tools", "larbin", "Fish search", "yandex.com/bots", "google.com/bot", "bingbot", "YandexMobileBot", "BingPreview", "AhrefsBot", "bot" ); $flag = 0; foreach ($spiderSite as $val) { $str = strtolower($val); if (strpos($agent, $str) !== false) { $flag = 1; } } if ($flag == 1) { return true; } else { return false; } } else { return false; } } /** * 接口 * @param Request $request * @return string */ public function trafficVisit(Request $request) { return $this->transmit($request, self::TYPEVISIT, self::TRAFFICONE); } /** * C端表单提交 询盘信息 * @param Request $request * @return string */ public function inquiry(Request $request) { $data = $request->all(); $black_ips = ['203.86.233.27', '37.19.221.202']; if (in_array(request()->getClientIp(), $black_ips)) { $this->success(); } //同一ip 5秒内超过5次 限制1小时 $ip = $request->ip(); $lock_key = 'lock_ip_' . $ip; $rate_key = 'rate_limit:' . $ip; if (Cache::has($lock_key)) { $this->success(); } if (Cache::has($rate_key)) { $count = Cache::get($rate_key); if ($count >= 5) { Cache::put($lock_key, 1, 3600); $this->success(); } Cache::increment($rate_key); } else { Cache::put($rate_key, 1, 5); } $data["files"] = $request->allFiles(); return $this->transmit($request, self::TYPEINQUIRY, self::TRAFFICZERO); } /** * 收集邮箱或者手机号等其他信息 * @param Request $request * @return string */ public function inquiryOtherInfo(Request $request) { return $this->transmit($request, self::TYPEINQUIRY); } /** * 起点表单询盘 * @param Request $request * @return string */ public function inquiryQd(Request $request) { return $this->transmit($request, self::TYPEINQUIRY); } /** * 搜索 * @param Request $request * @return mixed|string */ public function search(Request $request) { $data = $request->all(); //获取搜索参数 $data["search_content"] = ''; if (isset($data['s'])) { $req["search_content"] = $data['s']; } if (isset($data['search'])) { $req["search_content"] = $data['search']; } //分页 $req["page"] = isset($data['page']) && (int)$data['page'] > 1 ? (int)$data['page'] : 1; $req["domain"] = $request->getHost(); //转发data $transmitUrl = env("API_URL"); $resp = $this->httpPost($transmitUrl . "api/get_search_content/", json_encode($req)); return $resp["data"]["html"]; } /** * 输出处理日志 * @param $message * @return bool */ public function output($message) { echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL; return true; } }