diff --git a/apps/core/src/modules/configs/configs.default.ts b/apps/core/src/modules/configs/configs.default.ts index 09bba0d0..31ecf0a9 100644 --- a/apps/core/src/modules/configs/configs.default.ts +++ b/apps/core/src/modules/configs/configs.default.ts @@ -40,7 +40,7 @@ export const generateDefaultConfig: () => IConfig = () => ({ enableComment: true, enableThrottleGuard: false, }, - friendLinkOptions: { allowApply: true }, + friendLinkOptions: { allowApply: true, allowSubPath: false }, backupOptions: { enable: DEMO_MODE ? false : true, endpoint: null!, diff --git a/apps/core/src/modules/configs/configs.dto.ts b/apps/core/src/modules/configs/configs.dto.ts index 6cace61a..5d0e2411 100644 --- a/apps/core/src/modules/configs/configs.dto.ts +++ b/apps/core/src/modules/configs/configs.dto.ts @@ -3,8 +3,8 @@ import { ArrayUnique, IsBoolean, IsEmail, - IsIP, IsInt, + IsIP, IsNotEmpty, IsOptional, IsString, @@ -18,6 +18,7 @@ import { IsAllowedUrl } from '~/decorators/dto/isAllowedUrl' import { OpenAiSupportedModels } from '../ai/ai.constants' import { Encrypt } from './configs.encrypt.util' import { + halfFieldOption, JSONSchemaArrayField, JSONSchemaHalfGirdPlainField, JSONSchemaNumberField, @@ -25,11 +26,10 @@ import { JSONSchemaPlainField, JSONSchemaTextAreaField, JSONSchemaToggleField, - halfFieldOption, } from './configs.jsonschema.decorator' import type { ChatModel } from 'openai/resources' -const SecretField = (target: Object, propertyKey: string | symbol) => { +const SecretField = (target: object, propertyKey: string | symbol) => { Encrypt(target, propertyKey) Exclude({ toPlainOnly: true })(target, propertyKey) } @@ -261,6 +261,11 @@ export class FriendLinkOptionsDto { @IsOptional() @JSONSchemaToggleField('允许申请友链') allowApply: boolean + + @IsBoolean() + @IsOptional() + @JSONSchemaToggleField('允许子路径友链', { description: '例如 /blog 子路径' }) + allowSubPath: boolean } @JSONSchema({ title: '文本设定' }) diff --git a/apps/core/src/modules/link/link.controller.ts b/apps/core/src/modules/link/link.controller.ts index ecfbaf1c..727ca60c 100644 --- a/apps/core/src/modules/link/link.controller.ts +++ b/apps/core/src/modules/link/link.controller.ts @@ -99,6 +99,7 @@ export class LinkController { if (!(await this.linkService.canApplyLink())) { throw new ForbiddenException('主人目前不允许申请友链了!') } + await this.linkService.applyForLink(body) scheduleManager.schedule(async () => { await this.linkService.sendToMaster(body.author, body) diff --git a/apps/core/src/modules/link/link.model.ts b/apps/core/src/modules/link/link.model.ts index d868d7fb..ff574dca 100644 --- a/apps/core/src/modules/link/link.model.ts +++ b/apps/core/src/modules/link/link.model.ts @@ -1,5 +1,6 @@ -import { URL } from 'node:url' +import { modelOptions, prop } from '@typegoose/typegoose' import { Transform } from 'class-transformer' + import { IsEmail, IsEnum, @@ -9,8 +10,6 @@ import { MaxLength, } from 'class-validator' -import { modelOptions, prop } from '@typegoose/typegoose' - import { BaseModel } from '~/shared/model/base.model' export enum LinkType { @@ -51,9 +50,6 @@ export class LinkModel extends BaseModel { required: true, trim: true, unique: true, - set(val) { - return new URL(val).origin - }, }) @IsUrl( { require_protocol: true, protocols: ['https'] }, diff --git a/apps/core/src/modules/link/link.service.ts b/apps/core/src/modules/link/link.service.ts index f26ea5b7..3ecfbf3a 100644 --- a/apps/core/src/modules/link/link.service.ts +++ b/apps/core/src/modules/link/link.service.ts @@ -1,18 +1,20 @@ +import { URL } from 'node:url' + import { BadRequestException, Injectable, Logger, NotFoundException, + UnprocessableEntityException, } from '@nestjs/common' - import { BusinessEvents, EventScope } from '~/constants/business-event.constant' import { isDev } from '~/global/env.global' import { EmailService } from '~/processors/helper/helper.email.service' import { EventManagerService } from '~/processors/helper/helper.event.service' import { HttpService } from '~/processors/helper/helper.http.service' import { InjectModel } from '~/transformers/model.transformer' -import { scheduleManager } from '~/utils' +import { scheduleManager } from '~/utils' import { ConfigsService } from '../configs/configs.service' import { UserService } from '../user/user.service' import { LinkApplyEmailType } from './link-mail.enum' @@ -36,6 +38,8 @@ export class LinkService { return this.linkModel } async applyForLink(model: LinkModel) { + const { allowSubPath } = await this.configsService.get('friendLinkOptions') + const existedDoc = await this.model .findOne({ $or: [{ url: model.url }, { name: model.name }], @@ -66,8 +70,16 @@ export class LinkService { .lean() } } else { + const url = new URL(model.url) + const pathname = url.pathname + + if (pathname !== '/' && !allowSubPath) { + throw new UnprocessableEntityException('管理员当前禁用了子路径友链申请') + } + nextModel = await this.model.create({ ...model, + url: allowSubPath ? `${url.origin}${url.pathname}` : url.origin, type: LinkType.Friend, state: LinkState.Audit, }) @@ -134,7 +146,7 @@ export class LinkService { } const { enable } = await this.configs.get('mailOptions') if (!enable || isDev) { - console.log(` + console.info(` To: ${model.email} 你的友链已通过 站点标题:${model.name} @@ -152,7 +164,7 @@ export class LinkService { async sendToMaster(authorName: string, model: LinkModel) { const enable = (await this.configs.get('mailOptions')).enable if (!enable || isDev) { - console.log(`来自 ${authorName} 的友链请求: + console.info(`来自 ${authorName} 的友链请求: 站点标题:${model.name} 站点网站:${model.url} 站点描述:${model.description}`)