feat!: add xlog fn and builtIn on snippets model

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2023-04-05 12:47:36 +08:00
parent 4fb557548d
commit ae379a4559
12 changed files with 118 additions and 25 deletions

View File

@@ -26,7 +26,7 @@ module.exports = {
},
overrides: [
{
files: ['src/migration/**/*.ts'],
files: ['src/migration/**/*.ts', 'src/modules/serverless/pack/**/*.ts'],
rules: {
'import/no-default-export': 'off',
},

View File

@@ -66,4 +66,7 @@ export const generateDefaultConfig: () => IConfig = () => ({
featureList: {
emailSubscribe: false,
},
thirdPartyServiceIntegration: {
xLogSiteId: '',
},
})

View File

@@ -336,3 +336,16 @@ export class FeatureListDto {
@IsOptional()
emailSubscribe: boolean
}
/**
* 第三方服务集成
*/
@JSONSchema({ title: '第三方服务集成' })
export class ThirdPartyServiceIntegrationDto {
@JSONSchemaPlainField('xLog SiteId', {
description: '文章发布同步到 [xLog](https://xlog.app)',
})
@IsOptional()
@IsString()
xLogSiteId?: string
}

View File

@@ -15,6 +15,7 @@ import {
SeoDto,
TerminalOptionsDto,
TextOptionsDto,
ThirdPartyServiceIntegrationDto,
UrlDto,
} from './configs.dto'
@@ -72,6 +73,10 @@ export abstract class IConfig {
@Type(() => FeatureListDto)
@ValidateNested()
featureList: Required<FeatureListDto>
@Type(() => ThirdPartyServiceIntegrationDto)
@ValidateNested()
thirdPartyServiceIntegration: Required<ThirdPartyServiceIntegrationDto>
}
export type IConfigKeys = keyof IConfig

View File

@@ -15,4 +15,8 @@ export interface BuiltInFunctionObject {
path: string
method: string
code: string
reference: string
}
export const defineBuiltInSnippetConfig = (config: BuiltInFunctionObject) =>
config

View File

@@ -0,0 +1,4 @@
import { builtInSnippets } from './built-in'
import { xLogPackSnippets } from './xlog'
export const allBuiltInSnippetPack = [...xLogPackSnippets, ...builtInSnippets]

View File

@@ -0,0 +1,20 @@
import { defineBuiltInSnippetConfig } from '../../function.types'
export default defineBuiltInSnippetConfig({
name: 'getPageId',
method: 'GET',
code: `import axios from 'axios';
export default async function handler(ctx: Context) {
const { req } = ctx
const { query } = req
const { slug, handle } = query
return axios.get('https://xlog.app/api/slug2id', {
params: {
slug, handle
}
}).then(data => data.data).catch(err => ({ err }))
}`,
path: 'get_page_id',
reference: 'xlog',
})

View File

@@ -0,0 +1,3 @@
import get_page_id from './get_page_id'
export const xLogPackSnippets = [get_page_id]

View File

@@ -118,11 +118,12 @@ export class ServerlessController {
@Delete('/reset/:id')
@Auth()
async resetBuiltInFunction(@Param('id') id: string) {
const isBuiltin = await this.serverlessService.isBuiltInFunction(id)
if (!isBuiltin) {
const builtIn = await this.serverlessService.isBuiltInFunction(id)
if (!builtIn) {
throw new BadRequestException('can not reset a non-builtin function')
}
await this.serverlessService.resetBuiltInFunction(isBuiltin)
await this.serverlessService.resetBuiltInFunction(builtIn)
return
}
}

View File

@@ -41,7 +41,7 @@ import {
FunctionContextRequest,
FunctionContextResponse,
} from './function.types'
import { builtInSnippets } from './pack/built-in'
import { allBuiltInSnippetPack as builtInSnippets } from './pack'
import { ServerlessStorageCollectionName } from './serverless.model'
import { complieTypeScriptBabelOptions, hashStable } from './serverless.util'
@@ -671,41 +671,54 @@ export class ServerlessService implements OnModuleInit {
private async pourBuiltInFunctions() {
const paths = [] as string[]
const references = new Set<string>()
const pathCodeMap = new Map<string, BuiltInFunctionObject>()
for (const s of builtInSnippets) {
paths.push(s.path)
pathCodeMap.set(s.path, s)
if (s.reference) {
references.add(s.reference)
}
}
// 0. get built-in functions is exist in db
const result = await this.model
.find({
name: {
$in: paths,
},
reference: 'built-in',
type: SnippetType.Function,
})
.lean()
const result = await this.model.find({
name: {
$in: paths,
},
// FIXME reference not only `built-in` now
reference: {
$in: ['built-in'].concat(Array.from(references.values())),
},
type: SnippetType.Function,
})
// 1. filter is exist
const migrationTasks = [] as Promise<any>[]
for (const doc of result) {
const path = doc.name
pathCodeMap.delete(path)
// migration, add builtIn set to `true`
if (!doc.builtIn) {
migrationTasks.push(doc.updateOne({ builtIn: true }))
}
}
await Promise.all(migrationTasks)
// 2. pour
for (const [path, { code, method, name }] of pathCodeMap) {
for (const [path, { code, method, name, reference }] of pathCodeMap) {
this.logger.log(`pour built-in function: ${name}`)
await this.model.create({
type: SnippetType.Function,
name: path,
reference: 'built-in',
reference: reference || 'built-in',
raw: code,
method: method || 'get',
enable: true,
private: false,
builtIn: true,
})
}
}
@@ -717,14 +730,21 @@ export class ServerlessService implements OnModuleInit {
})
.lean()
if (!document) return false
const isBuiltin =
document.type == SnippetType.Function && document.reference == 'built-in'
return isBuiltin ? document.name : false
const isBuiltin = document.type == SnippetType.Function && document.builtIn
return isBuiltin
? {
name: document.name,
reference: document.reference || 'built-in',
}
: false
}
async resetBuiltInFunction(name: string) {
const builtIn = builtInSnippets.find((s) => s.path == name)
if (!builtIn) {
async resetBuiltInFunction(model: { name: string; reference: string }) {
const { name, reference } = model
const builtInSnippet = builtInSnippets.find(
(s) => s.path === name && s.reference === reference,
)
if (!builtInSnippet) {
throw new InternalServerErrorException('built-in function not found')
}
@@ -732,7 +752,7 @@ export class ServerlessService implements OnModuleInit {
{
name,
},
{ raw: builtIn.code },
{ raw: builtInSnippet.code },
)
}
}

View File

@@ -115,4 +115,9 @@ export class SnippetModel extends BaseModel {
enable?: boolean
updated?: string
@prop({
default: false,
})
builtIn?: boolean
}

View File

@@ -4,7 +4,12 @@ import { render } from 'ejs'
import { createTransport } from 'nodemailer'
import Mail from 'nodemailer/lib/mailer'
import { Injectable, Logger, OnModuleInit } from '@nestjs/common'
import {
Injectable,
Logger,
OnModuleDestroy,
OnModuleInit,
} from '@nestjs/common'
import { OnEvent } from '@nestjs/event-emitter'
import { BizException } from '~/common/exceptions/biz.exception'
@@ -29,7 +34,7 @@ export enum LinkApplyEmailType {
}
@Injectable()
export class EmailService implements OnModuleInit {
export class EmailService implements OnModuleInit, OnModuleDestroy {
private instance: ReturnType<typeof createTransport>
private logger: Logger
constructor(
@@ -49,6 +54,10 @@ export class EmailService implements OnModuleInit {
}
}
onModuleDestroy() {
this.teardown()
}
async readTemplate(
type: ReplyMailType | NewsletterMailType,
): Promise<string> {
@@ -116,10 +125,16 @@ export class EmailService implements OnModuleInit {
break
}
}
teardown() {
this.instance?.close?.()
}
@OnEvent(EventBusEvents.EmailInit)
init() {
this.getConfigFromConfigService()
.then((config) => {
this.teardown()
this.instance = createTransport({
...config,
secure: true,