diff --git a/README.md b/README.md index 0165a773..6dddacf6 100755 --- a/README.md +++ b/README.md @@ -1,59 +1,94 @@ -

Laravel Logo

+key_indexnow = 8f3a9d2b1c4e7f0a8f3a9d2b1c4e7f0a -

-Build Status -Total Downloads -Latest Stable Version -License -

+NASA_API_SECRET=wocaonide # 接收端key -## About Laravel +url_ad_fu = fuuu.cloud -Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: +token_base = wocaonide +is_loc = false +tg_send_enable = true +url_master_base = master.diaodu.us +site_code = clashfreenode.com +site_code_short = cfn +page_limit = 20 +app_title = SSR/V2ray/Clash免费节点每天更新 +app_desc = "最新免费 Clash、V2ray 及 SSR 节点订阅地址每日更新。提供 Shadowrocket 与 Singbox、Hiddify 高速节点一键复制导入,保障稳定连通与高速体验。" +app_keywords = "Clash免费节点, V2ray订阅链接, SSR节点, 免费节点更新" +url_resource_base = resource.gemcook.net +url_github_href = https://github.com/clashfreenodecom/clashfreenode.com -- [Simple, fast routing engine](https://laravel.com/docs/routing). -- [Powerful dependency injection container](https://laravel.com/docs/container). -- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. -- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). -- Database agnostic [schema migrations](https://laravel.com/docs/migrations). -- [Robust background job processing](https://laravel.com/docs/queues). -- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). +MEILISEARCH_KEY=wocaonide +SCOUT_DRIVER=meilisearch +MEILISEARCH_HOST=http://127.0.0.1:7700 +SCOUT_QUEUE=false -Laravel is accessible, powerful, and provides tools required for large, robust applications. +VITE_DEV_SERVER_URL=http://127.0.0.1:5173 -## Learning Laravel +APP_NAME=clashfreenode +APP_ENV=local +APP_KEY=base64:VkMoebPURG4A1ker22M5nfFV+tcjph+eYvQrJYANq0Y= +APP_DEBUG=true +APP_URL=https://clashfreenode.com -Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out [Laravel Learn](https://laravel.com/learn), where you will be guided through building a modern Laravel application. +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US -If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database -## Laravel Sponsors +# PHP_CLI_SERVER_WORKERS=4 -We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). +BCRYPT_ROUNDS=12 -### Premium Partners +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug -- **[Vehikl](https://vehikl.com)** -- **[Tighten Co.](https://tighten.co)** -- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** -- **[64 Robots](https://64robots.com)** -- **[Curotec](https://www.curotec.com/services/technologies/laravel)** -- **[DevSquad](https://devsquad.com/hire-laravel-developers)** -- **[Redberry](https://redberry.international/laravel-development)** -- **[Active Logic](https://activelogic.com)** +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=clashfreenode-new-com +DB_USERNAME=hotdog +DB_PASSWORD=hotshotgg -## Contributing +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null -Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database -## Code of Conduct +RESPONSE_CACHE_ENABLED=false +CACHE_STORE=file +RESPONSE_CACHE_STORE=file +RESPONSE_CACHE_DRIVER=file +# CACHE_PREFIX= -In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). +MEMCACHED_HOST=127.0.0.1 -## Security Vulnerabilities +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 -If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. +MAIL_MAILER=log +MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" -## License +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false -The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). +VITE_APP_NAME="${APP_NAME}" diff --git a/app/Http/Controllers/Api/v1/ArticlesController.php b/app/Http/Controllers/Api/v1/ArticlesController.php index 5302d925..2ead58d0 100755 --- a/app/Http/Controllers/Api/v1/ArticlesController.php +++ b/app/Http/Controllers/Api/v1/ArticlesController.php @@ -12,6 +12,7 @@ use App\Models\ArticleThumbNum; use App\Services\TomTool\HttpV2; use App\Services\TagService; +use App\Services\IndexNowService; class ArticlesController extends Controller { @@ -83,15 +84,10 @@ public function receive(Request $oRequest) $oArticle->save(); (new TagService())->syncArticleTagsByName($oArticle, $aOptTag); - -// $aArticle = $oArticle->toArray(); -// unset($aArticle["sContent"]); -// unset($aArticle["sTitleSub"]); -// $jArticle = json_encode($aArticle, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); - + $sMsg = "文章put成功:{$oArticle->sSlug}"; TeleSlave::notify()->send($sMsg); - + return [ "isDone" => true, ]; @@ -155,20 +151,6 @@ public function receiveShadowrocketids(Request $oRequest) ['sSlug' => $sSlug], $aAttributes ); - -// $oArticle = Articles::updateOrCreate( -// ['sSlug' => $sSlug], -// [ -// 'sThumb' => $sThumbPath, -// 'iCategoryId' => $oCategories->id, -// 'sTitle' => $sTitle, -// 'sTitleSub' => $sTitleSub, -// 'sContent' => $sArticle, -// 'i7like' => $i7like, -// 'iStatus' => 1, -// 'iUserId' => 1, -// ], -// ); (new TagService())->syncArticleTagsByName($oArticle, $aArticleTags); @@ -176,7 +158,10 @@ public function receiveShadowrocketids(Request $oRequest) $sArticleUrl = config("app.url")."/shadowrocketids/".$sSlug; - TeleSlave::notify()->send($sArticleUrl." 发布成功"); + $oIndexNow = new IndexNowService(); + $bIndexR = $oIndexNow->pushUrls([$sArticleUrl.".html"]); + + TeleSlave::notify()->send($sArticleUrl." 发布成功 | "."indexNow推送:".$bIndexR); return response()->json($oFlow->done()); } @@ -241,27 +226,16 @@ public function receiveFreenode(Request $oRequest) $aAttributes ); -// $oArticle = Articles::updateOrCreate( -// ['sSlug' => $sSlug], -// [ -// 'sThumb' => $sThumbPath, -// 'iCategoryId' => $oCategories->id, -// 'sTitle' => $sArticleTitle, -// 'sTitleSub' => $sArticleTitleSub, -// 'sContent' => $sArticleContent, -// 'i7like' => $i7like, -// 'iStatus' => 1, -// 'iUserId' => 1, -// ], -// ); // 我希望,如果数据已经有了,i7like就不再更新 - (new TagService())->syncArticleTagsByName($oArticle, $aArticleTags); ArticleFreenodeThumbNum::where("author_code", $sSubKey)->update(["num" => $sThumbNumTrue]); $sArticleUrl = config("app.url")."/feenode/".$sSlug; - TeleSlave::notify()->send($sArticleUrl." 发布成功"); + $oIndexNow = new IndexNowService(); + $bIndexR = $oIndexNow->pushUrls([$sArticleUrl.".html"]); + + TeleSlave::notify()->send($sArticleUrl." 发布成功 | "."indexNow推送:".$bIndexR); return response()->json($oFlow->done()); } diff --git a/app/Http/Controllers/TestController.php b/app/Http/Controllers/TestController.php index c1281ba4..0981c650 100755 --- a/app/Http/Controllers/TestController.php +++ b/app/Http/Controllers/TestController.php @@ -3,11 +3,24 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; +use App\Services\IndexNowService; -class TestController extends Controller +class TestController { public function hello() { tt("hello"); } + + public function test1() + { + $oIndexNow = new IndexNowService(); + + + $a = ["https://clashfreenode.com/a/fn20260910.html"]; // 这个页面实际是存在的,但是跳过了? + + $r = $oIndexNow->pushUrls($a); + + tt($r); + } } diff --git a/app/Models/Categories.php b/app/Models/Categories.php index 07deddb9..f47c7202 100755 --- a/app/Models/Categories.php +++ b/app/Models/Categories.php @@ -14,6 +14,7 @@ class Categories extends Model public $timestamps = false; protected $casts = [ 'aBanner' => 'array', + 'aOpt' => 'array' ]; protected $touches = ['oArticles']; diff --git a/app/Services/IndexNowService.php b/app/Services/IndexNowService.php new file mode 100644 index 00000000..2368a4fc --- /dev/null +++ b/app/Services/IndexNowService.php @@ -0,0 +1,226 @@ + $aUrlList + * @return bool + */ + public function pushUrls(array $aUrlList): bool + { + if (empty($aUrlList)) { + return false; + } + + $sIndexNowKey = config('key.indexnow'); + $sHostDomain = config('app.site_code', 'clashfreenode.com'); + + // TEST 时可以写死,但正式环境建议删除这一行 + $sHostDomain = 'clashfreenode.com'; + + if (empty($sIndexNowKey) || empty($sHostDomain)) { + Log::error('IndexNow 配置缺失。'); + + return false; + } + + /** + * --------------------------------------------------------- + * 1. 清理 URL + * --------------------------------------------------------- + */ + $aUrlList = array_values(array_unique(array_filter( + $aUrlList, + static function ($sUrl) { + return is_string($sUrl) + && filter_var($sUrl, FILTER_VALIDATE_URL); + } + ))); + + if (empty($aUrlList)) { + return false; + } + + /** + * --------------------------------------------------------- + * 2. 检查 URL 是否真实存在 + * --------------------------------------------------------- + */ + $aValidUrlList = []; + + foreach ($aUrlList as $sUrl) { + + // 检查域名 + $sUrlHost = parse_url($sUrl, PHP_URL_HOST); + + if ($sUrlHost !== $sHostDomain) { + Log::warning('IndexNow URL 域名不匹配', [ + 'url' => $sUrl, + 'host' => $sHostDomain, + ]); + + continue; + } + + // 检查页面是否真实存在 + if (!$this->urlExists($sUrl)) { + Log::warning('IndexNow 跳过不存在的 URL', [ + 'url' => $sUrl, + ]); + + continue; + } + + $aValidUrlList[] = $sUrl; + } + + /** + * 一个 URL 都不存在 + */ + if (empty($aValidUrlList)) { + Log::warning('IndexNow 没有可推送的有效 URL', [ + 'count' => count($aUrlList), + ]); + + return false; + } + + /** + * --------------------------------------------------------- + * 3. 构造 IndexNow Payload + * --------------------------------------------------------- + */ + $aPayloadData = [ + 'host' => $sHostDomain, + 'key' => $sIndexNowKey, + 'keyLocation' => "https://{$sHostDomain}/{$sIndexNowKey}.txt", + 'urlList' => $aValidUrlList, + ]; + + /** + * --------------------------------------------------------- + * 4. 推送 IndexNow + * --------------------------------------------------------- + */ + try { + + $oResponse = Http::timeout(10) + ->acceptJson() + ->withHeaders([ + 'Content-Type' => 'application/json; charset=utf-8', + ]) + ->post( + 'https://api.indexnow.org/indexnow', + $aPayloadData + ); + + $iStatusCode = $oResponse->status(); + + /** + * 200 = 成功处理 + * 202 = 已接受,等待处理 + */ + if (in_array($iStatusCode, [200, 202], true)) { + + Log::info('IndexNow 推送成功', [ + 'status' => $iStatusCode, + 'count' => count($aValidUrlList), + 'host' => $sHostDomain, + 'urls' => $aValidUrlList, + ]); + + return true; + } + + Log::error('IndexNow 推送失败', [ + 'status' => $iStatusCode, + 'body' => $oResponse->body(), + 'count' => count($aValidUrlList), + 'host' => $sHostDomain, + 'urls' => $aValidUrlList, + ]); + + return false; + + } catch (Throwable $e) { + + Log::error('IndexNow 请求异常', [ + 'message' => $e->getMessage(), + 'count' => count($aValidUrlList), + 'host' => $sHostDomain, + ]); + + return false; + } + } + + + /** + * 判断 URL 页面是否真实存在。 + * + * 使用 GET 检查,跟搜索引擎实际访问页面的方式更接近。 + * + * @param string $sUrl + * @return bool + */ + private function urlExists(string $sUrl): bool + { + try { + + $oResponse = Http::timeout(10) + ->withOptions([ + 'allow_redirects' => true, + ]) + ->get($sUrl); + + $iStatusCode = $oResponse->status(); + + Log::info('URL 页面检查', [ + 'url' => $sUrl, + 'status' => $iStatusCode, + ]); + + /** + * 200 ~ 299 + * 认为页面正常存在 + */ + if ($oResponse->successful()) { + return true; + } + + Log::warning('URL 页面不存在或无法访问', [ + 'url' => $sUrl, + 'status' => $iStatusCode, + ]); + + return false; + + } catch (Throwable $e) { + + Log::warning('URL 页面检查异常', [ + 'url' => $sUrl, + 'message' => $e->getMessage(), + ]); + + return false; + } + } + + +} diff --git a/config/key.php b/config/key.php new file mode 100755 index 00000000..60b15848 --- /dev/null +++ b/config/key.php @@ -0,0 +1,7 @@ + env('key_indexnow'), + +]; diff --git a/public/8f3a9d2b1c4e7f0a8f3a9d2b1c4e7f0a.txt b/public/8f3a9d2b1c4e7f0a8f3a9d2b1c4e7f0a.txt new file mode 100644 index 00000000..374afa5a --- /dev/null +++ b/public/8f3a9d2b1c4e7f0a8f3a9d2b1c4e7f0a.txt @@ -0,0 +1 @@ +8f3a9d2b1c4e7f0a8f3a9d2b1c4e7f0a diff --git a/resources/views/components/app-layout.blade.php b/resources/views/components/app-layout.blade.php index 3dfbf917..673e8d76 100755 --- a/resources/views/components/app-layout.blade.php +++ b/resources/views/components/app-layout.blade.php @@ -1,5 +1,3 @@ - - @props([ 'sTitle' => config('app.title').' - '.config('app.name'), 'sDesc' => config('app.desc'), @@ -8,7 +6,7 @@ 'sOgType' => 'website', 'sOgImg' => '/sites/'.config("app.site_code_short").'/siteimg.jpg', ]) - + @@ -16,11 +14,15 @@ - + + +@php + $sTodayDate = now()->format('Y年m月d日'); //有问题? +@endphp {{-- SEO 三杰 --}} {{ $sTitle }} - + diff --git a/resources/views/pages/articles/default/show.blade.php b/resources/views/pages/articles/default/show.blade.php index 9c2686fe..e1ce9921 100755 --- a/resources/views/pages/articles/default/show.blade.php +++ b/resources/views/pages/articles/default/show.blade.php @@ -1,14 +1,23 @@ +@php + $iNoIndexDays = (int) ($oArticle->oCategory->aOpt['noindexday'] ?? 0); + $bIsExpired = $iNoIndexDays > 0 && $oArticle->created_at->addDays($iNoIndexDays)->isPast(); + $sRobotsStatus = $bIsExpired ? 'noindex, follow' : 'index, follow'; +@endphp + -@section('sTitle', $oArticle->sTitle . ' - ' . $oArticle->oCategory->sName) -@section('sDesc', $oArticle->sTitleSub) -@section('sOgType', 'article') +{{-- + @section('sTitle', $oArticle->sTitle . ' - ' . ($oArticle->oCategory->sName ?? '')) + @section('sDesc', $oArticle->sTitleSub ?? '') + @section('sOgType', 'article') +--}}
diff --git a/routes/web.php b/routes/web.php index 959f6047..357663cc 100755 --- a/routes/web.php +++ b/routes/web.php @@ -10,6 +10,11 @@ use App\Http\Controllers\Api\v1\ArticlesController as ApiV1ArticlesController; use App\Http\Controllers\Api\v1\FreenodeController as ApiV1FreenodeController; +Route::get('/test/hello', [TestController::class, 'hello']); // 找个控制器为什么无效了 +Route::get('/test/test1', [TestController::class, 'test1']); +Route::get('/test/test2', [TestController::class, 'test2']); +Route::get('/test/test3', [TestController::class, 'test3']); + Route::post('/api/article/receive/shadowrocketids', [ApiV1ArticlesController::class, 'receiveShadowrocketids']); Route::post('/api/article/receive/freenode', [ApiV1ArticlesController::class, 'receiveFreenode']); Route::post('/api/article/receive', [ApiV1ArticlesController::class, 'receive']); @@ -42,7 +47,7 @@ //Route::get('/ajax/{sSlug1}/{sSlug2?}', [ArticlesController::class, 'listText']) // ->name('articles.list.text'); -Route::get('/hello', [TestController::class, 'hello']); + //Route::post('/ajax-handler', function () { // return response()->json(['sStatus' => 'success']);