feat: 添加推送到Bing支持 (#2379)
* feat(core): 添加 Bing 搜索推送功能 - 在 errorCode.constant.ts 中添加 Bing 相关的错误代码 - 在 configs.dto.ts 中添加 BingSearchOptionDto 类用于配置 Bing 推送选项 - 在 helper.cron.service.ts 中实现 pushToBingSearch 方法进行 Bing 搜索推送 * feat(core): 添加必应搜索推送配置并优化相关功能 - 在默认配置中添加 bingSearchOptions 项 - 更新 BingSearchOptionDto 类,统一字段命名 - 在 IConfig 接口中添加 bingSearchOptions 字段 - 修改 CronService 中的 pushToBingSearch 方法,适配新配置 * build: 更新下载资源链接 - 将 CDN 下载 URL 从 `https://mirror.ghproxy.com/` 更改为 `https://ghfast.top/` * feat(api-client): 添加 Bing 搜索配置模型 - 新增 BingSearchOptionsModel 类,用于 Bing 搜索的配置选项 - 该模型包括 enable 和 token 两个属性,与 BaiduSearchOptionsModel 类似 * refactor(configs): 统一配置类命名规则 - 将 BingSearchOptionDto 重命名为 BingSearchOptionsDto,与 BaiduSearchOptionsDto 保持一致 - 更新相关引用和配置字段类型 * feat(core): 添加每日凌晨1点执行的 Bing 搜索推送任务 - 在 CronService 类中添加了 pushToBingSearch 方法 - 使用 @CronOnce 装饰器设置任务执行时间为每天凌晨1点 - 任务名称为 'pushToBingSearch' - 方法描述为 '推送到Bing' * chore: 添加.eslintcache到.gitignore - 在.gitignore文件中添加.eslintcache,避免eslint缓存文件被版本控制 * feat(docs): 更新readme * refactor(core): 优化 Bing 站长提交日志输出 - 修改了 Bing 站长提交结果的日志输出格式 - 当提交成功时,输出简短的成功日志 - 当提交失败时,仍输出详细的错误信息 * Update apps/core/src/processors/helper/helper.cron.service.ts Co-authored-by: Innei <tukon479@gmail.com> Signed-off-by: Teror Fox <i@trfox.top> * chore: 删除废弃的服务器部署脚本 * feat(core): 添加 Bing API 域名无效错误码 - 在 ErrorCodeEnum 枚举中添加 BingDomainInvalid 错误码 - 在 ErrorCode 对象中添加对应的错误信息和状态码 * refactor(core): 修复 Bing 推送异常时返回值问题 --------- Signed-off-by: Teror Fox <i@trfox.top> Co-authored-by: Innei <tukon479@gmail.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -51,3 +51,5 @@ bin/process-reporter
|
||||
|
||||
dist
|
||||
dev/
|
||||
|
||||
.eslintcache
|
||||
@@ -166,6 +166,7 @@ ResponseInterceptor -> ResponseFilterInterceptor -> JSONTransformInterceptor ->
|
||||
1. [CronService] 维护管理计划任务
|
||||
- 自动备份
|
||||
- 推送百度搜索
|
||||
- 推送Bing搜索
|
||||
- 清除缓存
|
||||
- etc.
|
||||
1. [EmailService] 送信服务
|
||||
|
||||
@@ -25,6 +25,11 @@ export enum ErrorCodeEnum {
|
||||
// system
|
||||
MasterLost = 99998,
|
||||
BanInDemo = 999999,
|
||||
|
||||
//Bing
|
||||
BingAPIFailed = 300002,
|
||||
BingKeyInvalid = 300003,
|
||||
BingDomainInvalid = 300004,
|
||||
}
|
||||
|
||||
export const ErrorCode = Object.freeze<Record<ErrorCodeEnum, [string, number]>>(
|
||||
@@ -53,5 +58,9 @@ export const ErrorCode = Object.freeze<Record<ErrorCodeEnum, [string, number]>>(
|
||||
[ErrorCodeEnum.AIResultParsingError]: ['AI 结果解析错误', 500],
|
||||
|
||||
[ErrorCodeEnum.EmailTemplateNotFound]: ['邮件模板不存在', 400],
|
||||
|
||||
[ErrorCodeEnum.BingAPIFailed]: ['Bing API请求失败', 503],
|
||||
[ErrorCodeEnum.BingKeyInvalid]: ['Bing API密钥无效', 401],
|
||||
[ErrorCodeEnum.BingDomainInvalid]: ['Bing API域名无效', 400],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -51,6 +51,7 @@ export const generateDefaultConfig: () => IConfig = () => ({
|
||||
secretKey: null!,
|
||||
},
|
||||
baiduSearchOptions: { enable: false, token: null! },
|
||||
bingSearchOptions: { enable: false, token: null! },
|
||||
algoliaSearchOptions: {
|
||||
enable: false,
|
||||
apiKey: '',
|
||||
|
||||
@@ -3,11 +3,9 @@ import {
|
||||
ArrayUnique,
|
||||
IsBoolean,
|
||||
IsEmail,
|
||||
isInt,
|
||||
IsInt,
|
||||
IsIP,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
@@ -210,6 +208,20 @@ export class BaiduSearchOptionsDto {
|
||||
token?: string
|
||||
}
|
||||
|
||||
@JSONSchema({ title: 'Bing推送设定' })
|
||||
export class BingSearchOptionsDto {
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@JSONSchemaToggleField('开启推送')
|
||||
enable?: boolean
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@SecretField
|
||||
@JSONSchemaPasswordField('Bing API密钥')
|
||||
token?: string
|
||||
}
|
||||
|
||||
@JSONSchema({ title: 'Algolia Search' })
|
||||
export class AlgoliaSearchOptionsDto {
|
||||
@IsBoolean()
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
BackupOptionsDto,
|
||||
BaiduSearchOptionsDto,
|
||||
BarkOptionsDto,
|
||||
BingSearchOptionsDto,
|
||||
CommentOptionsDto,
|
||||
FeatureListDto,
|
||||
FriendLinkOptionsDto,
|
||||
@@ -67,6 +68,8 @@ export abstract class IConfig {
|
||||
backupOptions: Required<BackupOptionsDto>
|
||||
@ConfigField(() => BaiduSearchOptionsDto)
|
||||
baiduSearchOptions: Required<BaiduSearchOptionsDto>
|
||||
@ConfigField(() => BingSearchOptionsDto)
|
||||
bingSearchOptions: Required<BingSearchOptionsDto>
|
||||
@ConfigField(() => AlgoliaSearchOptionsDto)
|
||||
algoliaSearchOptions: Required<AlgoliaSearchOptionsDto>
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export class UpdateService {
|
||||
return
|
||||
}
|
||||
|
||||
const cdnDownloadUrl = `https://mirror.ghproxy.com/${downloadUrl}`
|
||||
const cdnDownloadUrl = `https://ghfast.top/${downloadUrl}`
|
||||
// const cdnDownloadUrl = downloadUrl
|
||||
|
||||
subscriber.next(
|
||||
|
||||
@@ -21,7 +21,6 @@ import { ConfigsService } from '~/modules/configs/configs.service'
|
||||
import { InjectModel } from '~/transformers/model.transformer'
|
||||
import { getRedisKey } from '~/utils/redis.util'
|
||||
|
||||
import { CacheService } from '../redis/cache.service'
|
||||
import { RedisService } from '../redis/redis.service'
|
||||
import { HttpService } from './helper.http.service'
|
||||
import { JWTService } from './helper.jwt.service'
|
||||
@@ -160,6 +159,52 @@ export class CronService {
|
||||
return null
|
||||
}
|
||||
|
||||
@CronOnce(CronExpression.EVERY_DAY_AT_1AM, { name: 'pushToBingSearch' })
|
||||
@CronDescription('推送到Bing')
|
||||
async pushToBingSearch() {
|
||||
const {
|
||||
url: { webUrl },
|
||||
bingSearchOptions: configs,
|
||||
} = await this.configs.waitForConfigReady()
|
||||
|
||||
if (!configs.enable) {
|
||||
return
|
||||
}
|
||||
const apiKey = configs.token
|
||||
if (!apiKey) {
|
||||
this.logger.error('[BingSearchPushTask] API key 为空')
|
||||
return
|
||||
}
|
||||
|
||||
const pushUrls = await this.aggregateService.getSiteMapContent()
|
||||
const urls = pushUrls.map((item) => item.url)
|
||||
|
||||
try {
|
||||
const res = await this.http.axiosRef.post(
|
||||
`https://ssl.bing.com/webmaster/api.svc/json/SubmitUrlbatch?apikey=${apiKey}`,
|
||||
{
|
||||
siteUrl: webUrl,
|
||||
urlList: urls,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
charset: 'utf-8',
|
||||
},
|
||||
},
|
||||
)
|
||||
if (res?.data?.d === null) {
|
||||
this.logger.log('Bing站长提交成功')
|
||||
} else {
|
||||
this.logger.log(`Bing站长提交结果:${JSON.stringify(res.data)}`)
|
||||
}
|
||||
return res.data
|
||||
} catch (error) {
|
||||
this.logger.error(`Bing推送错误:${error.message}`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@CronDescription('扫表:删除过期 JWT')
|
||||
@CronOnce(CronExpression.EVERY_DAY_AT_1AM, {
|
||||
name: 'deleteExpiredJWT',
|
||||
|
||||
@@ -37,6 +37,11 @@ export declare class BaiduSearchOptionsModel {
|
||||
enable: boolean
|
||||
token?: string
|
||||
}
|
||||
export declare class BingSearchOptionsModel {
|
||||
enable: boolean
|
||||
token?: string
|
||||
}
|
||||
|
||||
export declare class AlgoliaSearchOptionsModel {
|
||||
enable: boolean
|
||||
apiKey?: string
|
||||
|
||||
@@ -16,7 +16,7 @@ const { homedir } = os
|
||||
const { repository } = require('../package.json')
|
||||
|
||||
const argv = process.argv.slice(2)
|
||||
const scpPath = av['scp_path']
|
||||
const scpPath = av.scp_path
|
||||
function getOsBuildAssetName() {
|
||||
return `release-linux.zip`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user