feat: add secret for function (#881)
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { isURL } from 'class-validator'
|
||||
import fs, { mkdir, stat } from 'fs/promises'
|
||||
import { isPlainObject } from 'lodash'
|
||||
import LRUCache from 'lru-cache'
|
||||
import { createRequire } from 'module'
|
||||
import { mongo } from 'mongoose'
|
||||
import path, { resolve } from 'path'
|
||||
import { nextTick } from 'process'
|
||||
import qs from 'qs'
|
||||
|
||||
import { TransformOptions, parseAsync, transformAsync } from '@babel/core'
|
||||
import BabelPluginTransformCommonJS from '@babel/plugin-transform-modules-commonjs'
|
||||
@@ -235,6 +237,14 @@ export class ServerlessService {
|
||||
const { raw: functionString } = model
|
||||
const logger = new Logger(`fx:${model.reference}/${model.name}`)
|
||||
const document = await this.model.findById(model.id)
|
||||
const secretObj = model.secret ? qs.parse(model.secret) : {}
|
||||
|
||||
if (!isPlainObject(secretObj)) {
|
||||
throw new InternalServerErrorException(
|
||||
`secret parsing error, must be object, got ${typeof secretObj}`,
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const self = this
|
||||
const globalContext = {
|
||||
@@ -257,6 +267,8 @@ export class ServerlessService {
|
||||
},
|
||||
},
|
||||
|
||||
secret: secretObj,
|
||||
|
||||
model,
|
||||
document,
|
||||
name: model.name,
|
||||
|
||||
@@ -8,7 +8,9 @@ import {
|
||||
Matches,
|
||||
MaxLength,
|
||||
} from 'class-validator'
|
||||
import { isNil } from 'lodash'
|
||||
import aggregatePaginate from 'mongoose-aggregate-paginate-v2'
|
||||
import { stringify } from 'qs'
|
||||
|
||||
import { index, modelOptions, plugin, prop } from '@typegoose/typegoose'
|
||||
|
||||
@@ -84,12 +86,20 @@ export class SnippetModel extends BaseModel {
|
||||
@IsOptional()
|
||||
schema?: string
|
||||
|
||||
// for function
|
||||
// for function start
|
||||
@prop()
|
||||
@IsEnum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'])
|
||||
@IsOptional()
|
||||
method?: string
|
||||
|
||||
@prop()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => (isNil(value) ? value : stringify(value)))
|
||||
// username=123&password=123
|
||||
secret?: string
|
||||
// for function end
|
||||
|
||||
@prop()
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { load } from 'js-yaml'
|
||||
import JSON5 from 'json5'
|
||||
import { AggregatePaginateModel, Document } from 'mongoose'
|
||||
import qs from 'qs'
|
||||
|
||||
import {
|
||||
BadRequestException,
|
||||
@@ -52,9 +53,9 @@ export class SnippetService {
|
||||
return await this.model.create({ ...model, created: new Date() })
|
||||
}
|
||||
|
||||
async update(id: string, model: SnippetModel) {
|
||||
await this.validateTypeAndCleanup(model)
|
||||
delete model.created
|
||||
async update(id: string, newModel: SnippetModel) {
|
||||
await this.validateTypeAndCleanup(newModel)
|
||||
delete newModel.created
|
||||
const old = await this.model.findById(id).lean()
|
||||
|
||||
if (!old) {
|
||||
@@ -63,19 +64,48 @@ export class SnippetService {
|
||||
|
||||
if (
|
||||
old.type === SnippetType.Function &&
|
||||
model.type !== SnippetType.Function
|
||||
newModel.type !== SnippetType.Function
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
'`type` is not allowed to change if this snippet set to Function type.',
|
||||
)
|
||||
}
|
||||
|
||||
// merge secret
|
||||
if (old.secret && newModel.secret) {
|
||||
const oldSecret = qs.parse(old.secret)
|
||||
const newSecret = qs.parse(newModel.secret)
|
||||
|
||||
// first delete key if newer secret not provide
|
||||
for (const key in oldSecret) {
|
||||
if (!(key in newSecret)) {
|
||||
delete oldSecret[key]
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in newSecret) {
|
||||
// if newSecret has same key, but value is empty, remove it
|
||||
|
||||
if (newSecret[key] === '' && oldSecret[key] !== '') {
|
||||
delete newSecret[key]
|
||||
}
|
||||
}
|
||||
|
||||
newModel.secret = qs.stringify({ ...oldSecret, ...newSecret })
|
||||
}
|
||||
|
||||
await this.deleteCachedSnippet(old.reference, old.name)
|
||||
return await this.model.findByIdAndUpdate(
|
||||
const newerDoc = await this.model.findByIdAndUpdate(
|
||||
id,
|
||||
{ ...model, modified: new Date() },
|
||||
{ ...newModel, modified: new Date() },
|
||||
{ new: true },
|
||||
)
|
||||
if (newerDoc) {
|
||||
const nextSnippet = this.transformLeanSnippetModel(newerDoc.toObject())
|
||||
|
||||
return nextSnippet
|
||||
}
|
||||
return newerDoc
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
@@ -134,7 +164,7 @@ export class SnippetService {
|
||||
// TODO refactor
|
||||
// cleanup
|
||||
if (model.type !== SnippetType.Function) {
|
||||
const deleteKeys: (keyof SnippetModel)[] = ['enable', 'method']
|
||||
const deleteKeys: (keyof SnippetModel)[] = ['enable', 'method', 'secret']
|
||||
deleteKeys.forEach((key) => {
|
||||
Reflect.deleteProperty(model, key)
|
||||
})
|
||||
@@ -146,7 +176,29 @@ export class SnippetService {
|
||||
if (!doc) {
|
||||
throw new NotFoundException()
|
||||
}
|
||||
return doc
|
||||
|
||||
// transform sth.
|
||||
const nextSnippet = this.transformLeanSnippetModel(doc)
|
||||
|
||||
return nextSnippet
|
||||
}
|
||||
|
||||
private transformLeanSnippetModel(snippet: SnippetModel) {
|
||||
const nextSnippet = { ...snippet }
|
||||
// transform sth.
|
||||
if (snippet.type === SnippetType.Function) {
|
||||
if (snippet.secret) {
|
||||
const secretObj = qs.parse(snippet.secret)
|
||||
|
||||
for (const key in secretObj) {
|
||||
// remove secret value, only keep key
|
||||
secretObj[key] = ''
|
||||
}
|
||||
nextSnippet.secret = secretObj as any
|
||||
}
|
||||
}
|
||||
|
||||
return nextSnippet
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user