@@ -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!,
|
||||
|
||||
@@ -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: '文本设定' })
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'] },
|
||||
|
||||
@@ -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}`)
|
||||
|
||||
Reference in New Issue
Block a user