debug
This commit is contained in:
@@ -43,8 +43,8 @@ if ($strTmpCode == 'videoGpt1') {
|
||||
return view('rss/baidu.xml')->contentType('text/xml');
|
||||
})->ext('xml');
|
||||
|
||||
$routeGetHead('rss/so', function () {
|
||||
return view('rss/so.xml')->contentType('text/xml');
|
||||
$routeGetHead('rss/so', function (\think\Request $Request, SiteContext $SiteContext) {
|
||||
return $SiteContext->getSoSitemapResponse();
|
||||
})->ext('xml');
|
||||
|
||||
$routeGetHead('/sitemap_index', function (\think\Request $Request, SiteContext $SiteContext) {
|
||||
@@ -413,9 +413,8 @@ if ($strTmpCode == 'videoGpt1') {
|
||||
/**
|
||||
* /rss/so
|
||||
*/
|
||||
Route::get('rss/so', function () {
|
||||
return view('rss/so.xml')
|
||||
->contentType('text/xml');
|
||||
Route::get('rss/so', function (\think\Request $Request, SiteContext $SiteContext) {
|
||||
return $SiteContext->getSoSitemapResponse();
|
||||
})->ext('xml');
|
||||
|
||||
/**
|
||||
|
||||
@@ -1017,9 +1017,11 @@ class DomainModel extends BaseModel
|
||||
}
|
||||
|
||||
if (!is_array($this->t_cfg) || !array_key_exists($strKey, $this->t_cfg)) {
|
||||
$strFallbackValue = $this->resolveLegacyTemplateFallback((string)$strKey);
|
||||
if ($strFallbackValue !== null) {
|
||||
return $strFallbackValue;
|
||||
}
|
||||
return '';
|
||||
$strError = sprintf("t_cfg [%s] not found! %s ", $strKey , $strDomain . '-' . $strController . '-' . $strAction. '-' . $strDiff. '-' . $boolJoin.'-'.request()->url);
|
||||
throw new \Exception($strError);
|
||||
}
|
||||
|
||||
$intSfgId = $this->t_cfg[$strKey]['sfg_id'];
|
||||
@@ -1070,13 +1072,83 @@ class DomainModel extends BaseModel
|
||||
$strError = sprintf("subject fomart [%s] not found !", $strKey);
|
||||
throw new \Exception($strError);
|
||||
}
|
||||
|
||||
return $SubjectFomartModel->sf_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
|
||||
return $SubjectFomartModel->sf_val;
|
||||
}
|
||||
|
||||
protected function resolveLegacyTemplateFallback(string $strKey): ?string
|
||||
{
|
||||
if (!is_array($this->t_cfg) || !isset($this->t_cfg['pages'], $this->t_cfg['url_family'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$arrSeoKeyMap = [
|
||||
'VIDEO@INDEX@INDEX@TITLE' => ['title', 'home'],
|
||||
'VIDEO@INDEX@INDEX@KEYWORDS' => ['keywords', 'home'],
|
||||
'VIDEO@INDEX@INDEX@DESCRIPTION' => ['description', 'home'],
|
||||
'VIDEO@GETCATEGORYINDEX@TITLE' => ['title', 'category_index'],
|
||||
'VIDEO@GETCATEGORYINDEX@KEYWORDS' => ['keywords', 'category_index'],
|
||||
'VIDEO@GETCATEGORYINDEX@DESCRIPTION' => ['description', 'category_index'],
|
||||
'VIDEO@GETCATEGORY@TITLE' => ['title', 'category_list'],
|
||||
'VIDEO@GETCATEGORY@KEYWORDS' => ['keywords', 'category_list'],
|
||||
'VIDEO@GETCATEGORY@DESCRIPTION' => ['description', 'category_list'],
|
||||
'VIDEO@GETSEARCHVIDEO@TITLE' => ['title', 'search'],
|
||||
'VIDEO@GETSEARCHVIDEO@KEYWORDS' => ['keywords', 'search'],
|
||||
'VIDEO@GETSEARCHVIDEO@DESCRIPTION' => ['description', 'search'],
|
||||
'VIDEO@GETVIDEORANKINDEX@TITLE' => ['title', 'rank_index'],
|
||||
'VIDEO@GETVIDEORANKINDEX@KEYWORDS' => ['keywords', 'rank_index'],
|
||||
'VIDEO@GETVIDEORANKINDEX@DESCRIPTION' => ['description', 'rank_index'],
|
||||
'VIDEO@GETVIDEORANKLIST@TITLE' => ['title', 'rank_list'],
|
||||
'VIDEO@GETVIDEORANKLIST@KEYWORDS' => ['keywords', 'rank_list'],
|
||||
'VIDEO@GETVIDEORANKLIST@DESCRIPTION' => ['description', 'rank_list'],
|
||||
'VIDEO@GETVIDEOINFO@TITLE' => ['title', 'detail'],
|
||||
'VIDEO@GETVIDEOINFO@KEYWORDS' => ['keywords', 'detail'],
|
||||
'VIDEO@GETVIDEOINFO@DESCRIPTION' => ['description', 'detail'],
|
||||
'VIDEO@GETVIDEOPLAY@TITLE' => ['title', 'play'],
|
||||
'VIDEO@GETVIDEOPLAY@KEYWORDS' => ['keywords', 'play'],
|
||||
'VIDEO@GETVIDEOPLAY@DESCRIPTION' => ['description', 'play'],
|
||||
];
|
||||
|
||||
if (isset($arrSeoKeyMap[$strKey])) {
|
||||
[$strSeoCode, $strSeoPage] = $arrSeoKeyMap[$strKey];
|
||||
|
||||
try {
|
||||
$SiteContext = \think\Container::getInstance()->make(\app\services\SiteContext::class);
|
||||
return (string)$SiteContext->getSeoTkd($strSeoCode, $strSeoPage);
|
||||
} catch (\Throwable $Throwable) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$arrRuntimeStyle = \app\common\helper\SiteStyle::getConfig($this, (string)($this->d_domain ?? ''));
|
||||
$UrlBuilder = new \app\common\helper\UrlBuilder($arrRuntimeStyle);
|
||||
} catch (\Throwable $Throwable) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$strTemplateCode = (string)(request()->TemplatesModel->t_code ?? '');
|
||||
if ($strTemplateCode !== '' && $strTemplateCode !== 'videoGpt1') {
|
||||
return match ($strKey) {
|
||||
'VIDEO_RANK_INDEX_URL' => '/phb-index',
|
||||
'VIDEO_SEARCH_LIST_URL' => '/query.html',
|
||||
'VIDEO_HISTORY_LIST_URL' => '/history.html',
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
return match ($strKey) {
|
||||
'VIDEO_CATEGORY_INDEX_URL' => $UrlBuilder->categoryHome(),
|
||||
'VIDEO_RANK_INDEX_URL' => $UrlBuilder->rankIndex(),
|
||||
'VIDEO_SEARCH_LIST_URL' => $UrlBuilder->searchEntry(),
|
||||
'VIDEO_HISTORY_LIST_URL' => $UrlBuilder->history(),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $strKey
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -172,6 +172,53 @@ class VideoModel extends MongoModel
|
||||
return $this->findManyWithCache($arrFilter, $intCount, $arrSort, $arrOptions, $strKey, $intLifeTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 轻量级 sitemap / rss 列表,只取生成 URL 需要的字段,避免大对象缓存撑爆内存。
|
||||
*/
|
||||
public function findSitemapItemsWithCache(
|
||||
array $arrFilter,
|
||||
int $intCount = 1000,
|
||||
array $arrSort = [],
|
||||
string $strKey = '',
|
||||
int $intLifeTime = 24 * 3600
|
||||
): array {
|
||||
try {
|
||||
$arrResult = $this->checkCacheByKey($strKey);
|
||||
if ($arrResult !== NULL) {
|
||||
return is_array($arrResult) ? $arrResult : [];
|
||||
}
|
||||
|
||||
$arrOptions = self::$arrOptions;
|
||||
$arrOptions['projection'] = [
|
||||
'_id' => 0,
|
||||
'v_id' => 1,
|
||||
'v_name_en' => 1,
|
||||
'v_publish_date' => 1,
|
||||
'created_at' => 1,
|
||||
'updated_at' => 1,
|
||||
];
|
||||
$arrOptions['limit'] = max(1, $intCount);
|
||||
|
||||
if (!empty($arrSort)) {
|
||||
$arrOptions['sort'] = $arrSort;
|
||||
}
|
||||
|
||||
$Cursor = $this->getCol()->find($arrFilter, $arrOptions);
|
||||
$arrResult = iterator_to_array($Cursor);
|
||||
|
||||
foreach ($arrResult as &$arrItem) {
|
||||
applyToKeys($arrItem, ['created_at', 'updated_at'], 'formatMongoDate');
|
||||
}
|
||||
unset($arrItem);
|
||||
|
||||
$this->setCacheByKey($strKey, $arrResult, $intLifeTime);
|
||||
|
||||
return $arrResult;
|
||||
} catch (\Throwable $Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* find one Video
|
||||
*
|
||||
|
||||
@@ -13,6 +13,7 @@ use app\model\ChapterModel;
|
||||
use app\model\SystemConfigModel;
|
||||
use app\model\NovelClicksModel;
|
||||
use app\model\NovelModel;
|
||||
use app\model\VideoModel;
|
||||
use app\common\helper\SiteStyle;
|
||||
use app\common\helper\CssBuilder;
|
||||
use app\common\helper\JsBuilder;
|
||||
@@ -566,6 +567,72 @@ class SiteContext
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容旧站的 /rss/so.xml。
|
||||
* 避免走模板标签 video:list 一次性读取/缓存大量完整视频文档导致内存溢出。
|
||||
*/
|
||||
public function getSoSitemapResponse(): Response
|
||||
{
|
||||
$strDomain = trim((string)($this->DomainModel->d_domain ?? ''));
|
||||
$strHost = $strDomain === '' ? trim((string)$this->Request->host()) : $strDomain;
|
||||
$strCacheKey = sprintf('SiteContext:getSoSitemapResponse:%s', $strHost);
|
||||
|
||||
$arrRows = VideoModel::getInstance()->findSitemapItemsWithCache(
|
||||
[],
|
||||
1000,
|
||||
['created_at' => -1, 'v_id' => -1],
|
||||
$strCacheKey,
|
||||
86400
|
||||
);
|
||||
|
||||
$arrXml = [
|
||||
'<?xml version="1.0" encoding="UTF-8"?>',
|
||||
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
||||
];
|
||||
|
||||
$VideoService = app(VideoService::class);
|
||||
foreach ($arrRows as $arrVideo) {
|
||||
$intVId = (int)($arrVideo['v_id'] ?? 0);
|
||||
$strSlug = trim((string)($arrVideo['v_name_en'] ?? ''));
|
||||
if ($intVId <= 0 || $strSlug === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$strPath = $VideoService->getVideoInfoUrl($intVId, $strSlug);
|
||||
if ($strPath === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$strLoc = 'https://www.' . $strHost . $strPath;
|
||||
$strLastmod = trim((string)($arrVideo['v_publish_date'] ?? ''));
|
||||
if ($strLastmod === '') {
|
||||
$strLastmod = trim((string)($arrVideo['updated_at'] ?? ''));
|
||||
}
|
||||
if ($strLastmod === '') {
|
||||
$strLastmod = trim((string)($arrVideo['created_at'] ?? ''));
|
||||
}
|
||||
if ($strLastmod !== '') {
|
||||
$intTs = strtotime($strLastmod);
|
||||
if ($intTs !== false) {
|
||||
$strLastmod = date('Y-m-d', $intTs);
|
||||
}
|
||||
}
|
||||
|
||||
$arrXml[] = '<url>';
|
||||
$arrXml[] = '<loc>' . htmlspecialchars($strLoc, ENT_QUOTES | ENT_XML1, 'UTF-8') . '</loc>';
|
||||
if ($strLastmod !== '') {
|
||||
$arrXml[] = '<lastmod>' . htmlspecialchars($strLastmod, ENT_QUOTES | ENT_XML1, 'UTF-8') . '</lastmod>';
|
||||
}
|
||||
$arrXml[] = '</url>';
|
||||
}
|
||||
|
||||
$arrXml[] = '</urlset>';
|
||||
|
||||
return response(implode("\n", $arrXml), 200, [
|
||||
'Content-Type' => 'application/xml; charset=UTF-8',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 限速访问
|
||||
*
|
||||
@@ -817,6 +884,11 @@ class SiteContext
|
||||
return '';
|
||||
}
|
||||
|
||||
$arrTemplateCfg = is_array($this->DomainModel->t_cfg ?? null) ? $this->DomainModel->t_cfg : [];
|
||||
if (!array_key_exists($strLegacyKey, $arrTemplateCfg)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->normalizeSeoText($this->converterTemplate($strLegacyKey));
|
||||
} catch (\Throwable $Throwable) {
|
||||
|
||||
13
code/extend/copyright/Auth.php
Normal file
13
code/extend/copyright/Auth.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace copyright;
|
||||
|
||||
class Auth
|
||||
{
|
||||
public static function checkCopyright(): null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
187
code/extend/db/mongo/MongoBase.php
Normal file
187
code/extend/db/mongo/MongoBase.php
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace db\mongo;
|
||||
|
||||
use MongoDB\Client;
|
||||
use MongoDB\Collection;
|
||||
use MongoDB\Database;
|
||||
use MongoDB\Driver\Exception\Exception as MongoDriverException;
|
||||
use MongoDB\Operation\FindOneAndUpdate;
|
||||
|
||||
class MongoBase
|
||||
{
|
||||
private static ?self $instance = null;
|
||||
|
||||
private Client $client;
|
||||
|
||||
private Database $db;
|
||||
|
||||
public static function getInstance(): self
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$config = $this->getMongoConfig();
|
||||
$this->client = $this->buildClient($config);
|
||||
$this->db = $this->client->selectDatabase((string) $config['database']);
|
||||
}
|
||||
|
||||
public function getDb(): Database
|
||||
{
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
public function getNextSequence(string $strCollection, string $strSequenceName): int
|
||||
{
|
||||
$result = $this->getCollection($strCollection)->findOneAndUpdate(
|
||||
['_id' => $strSequenceName],
|
||||
['$inc' => ['sequence_value' => 1]],
|
||||
[
|
||||
'upsert' => true,
|
||||
'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER,
|
||||
] + $this->getTypeMapOptions()
|
||||
);
|
||||
|
||||
return (int) ($result['sequence_value'] ?? 1);
|
||||
}
|
||||
|
||||
public function insertOne(string $strCollection, array $arrData, array $arrOptions = []): mixed
|
||||
{
|
||||
$result = $this->getCollection($strCollection)->insertOne($arrData, $arrOptions);
|
||||
return $result->getInsertedId();
|
||||
}
|
||||
|
||||
public function insertMany(string $strCollection, array $arrData, array $arrOptions = []): int
|
||||
{
|
||||
$result = $this->getCollection($strCollection)->insertMany($arrData, $arrOptions);
|
||||
return $result->getInsertedCount();
|
||||
}
|
||||
|
||||
public function findOne(string $strCollection, array $arrFilter = [], array $arrOptions = []): ?array
|
||||
{
|
||||
$result = $this->getCollection($strCollection)->findOne(
|
||||
$arrFilter,
|
||||
$arrOptions + $this->getTypeMapOptions()
|
||||
);
|
||||
|
||||
return $result === null ? null : (array) $result;
|
||||
}
|
||||
|
||||
public function findMany(
|
||||
string $strCollection,
|
||||
array $arrFilter = [],
|
||||
int $intCount = 0,
|
||||
array $arrSort = [],
|
||||
array $arrOptions = []
|
||||
): array {
|
||||
$options = $arrOptions + $this->getTypeMapOptions();
|
||||
if ($intCount > 0 && !isset($options['limit'])) {
|
||||
$options['limit'] = $intCount;
|
||||
}
|
||||
if ($arrSort !== [] && !isset($options['sort'])) {
|
||||
$options['sort'] = $arrSort;
|
||||
}
|
||||
|
||||
$cursor = $this->getCollection($strCollection)->find($arrFilter, $options);
|
||||
$arrResult = [];
|
||||
foreach ($cursor as $item) {
|
||||
$arrResult[] = (array) $item;
|
||||
}
|
||||
|
||||
return $arrResult;
|
||||
}
|
||||
|
||||
public function updateOne(string $strCollection, array $arrFilter, array $arrUpdate, array $arrOptions = []): int
|
||||
{
|
||||
$result = $this->getCollection($strCollection)->updateOne($arrFilter, $arrUpdate, $arrOptions);
|
||||
return $result->getModifiedCount();
|
||||
}
|
||||
|
||||
public function delete(string $strCollection, array $arrFilter, array $arrOptions = []): int
|
||||
{
|
||||
$result = $this->getCollection($strCollection)->deleteMany($arrFilter, $arrOptions);
|
||||
return $result->getDeletedCount();
|
||||
}
|
||||
|
||||
private function getCollection(string $strCollection): Collection
|
||||
{
|
||||
return $this->db->selectCollection($strCollection);
|
||||
}
|
||||
|
||||
private function getMongoConfig(): array
|
||||
{
|
||||
$config = \config('mongodb');
|
||||
if (!is_array($config)) {
|
||||
throw new \RuntimeException('MongoDB config is missing.');
|
||||
}
|
||||
|
||||
$config['hostname'] = (string) ($config['hostname'] ?? '127.0.0.1');
|
||||
$config['hostport'] = (string) ($config['hostport'] ?? '27017');
|
||||
$config['username'] = (string) ($config['username'] ?? '');
|
||||
$config['password'] = (string) ($config['password'] ?? '');
|
||||
$config['database'] = (string) ($config['database'] ?? '');
|
||||
|
||||
if ($config['database'] === '') {
|
||||
throw new \RuntimeException('MongoDB database is not configured.');
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function buildClient(array $config): Client
|
||||
{
|
||||
$uri = sprintf('mongodb://%s:%s', $config['hostname'], $config['hostport']);
|
||||
$options = [];
|
||||
|
||||
if ($config['username'] !== '') {
|
||||
$options['username'] = $config['username'];
|
||||
$options['password'] = $config['password'];
|
||||
foreach ($this->getAuthSources($config['database']) as $authSource) {
|
||||
try {
|
||||
$client = new Client($uri, $options + ['authSource' => $authSource]);
|
||||
foreach ($client->selectDatabase($config['database'])->listCollections([], ['maxTimeMS' => 3000]) as $_) {
|
||||
break;
|
||||
}
|
||||
return $client;
|
||||
} catch (MongoDriverException) {
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException('MongoDB authentication failed for all configured authSource values.');
|
||||
}
|
||||
|
||||
return new Client($uri, $options);
|
||||
}
|
||||
|
||||
private function getAuthSources(string $database): array
|
||||
{
|
||||
$sources = [];
|
||||
$envSource = \env('MONGO_AUTH_DB', '');
|
||||
if (is_string($envSource) && $envSource !== '') {
|
||||
$sources[] = $envSource;
|
||||
}
|
||||
$sources[] = 'admin';
|
||||
$sources[] = $database;
|
||||
|
||||
return array_values(array_unique($sources));
|
||||
}
|
||||
|
||||
private function getTypeMapOptions(): array
|
||||
{
|
||||
return [
|
||||
'typeMap' => [
|
||||
'root' => 'array',
|
||||
'document' => 'array',
|
||||
'array' => 'array',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,11 @@ use ReflectionException;
|
||||
|
||||
class Init
|
||||
{
|
||||
private const REPLACED_DDL_MAP = [
|
||||
'1.ddl' => \copyright\Auth::class,
|
||||
'10.ddl' => \db\mongo\MongoBase::class,
|
||||
];
|
||||
|
||||
public static function run()
|
||||
{
|
||||
self::loadDDL(__DIR__ . '/ext');
|
||||
@@ -17,6 +22,10 @@ class Init
|
||||
public static function loadDDL($strDir)
|
||||
{
|
||||
foreach (self::scanDdlFiles($strDir) as $strDDL) {
|
||||
$strFilename = basename($strDDL);
|
||||
if (isset(self::REPLACED_DDL_MAP[$strFilename]) && class_exists(self::REPLACED_DDL_MAP[$strFilename])) {
|
||||
continue;
|
||||
}
|
||||
load_module_file($strDDL);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user