From c293241e9b5c26a3e2fcd230c5cc17741e92772e Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Thu, 8 Jun 2023 14:05:19 +0000 Subject: [PATCH 1/8] feat(server): add function template market --- server/src/app.module.ts | 2 + server/src/application/application.module.ts | 7 +- server/src/dependency/dependency.module.ts | 1 + server/src/dependency/dependency.service.ts | 6 +- .../dto/create-function-template.dto.ts | 80 ++ .../dto/star-function-template.dto.ts | 11 + .../dto/update-function-template.dto.ts | 58 ++ .../dto/use-function-template.dto.ts | 11 + .../entities/function-template.ts | 99 +++ .../entities/swagger-help.ts | 146 ++++ .../function-template.controller.ts | 392 +++++++++ .../function-template.module.ts | 21 + .../function-template.service.ts | 811 ++++++++++++++++++ server/tsconfig.json | 2 +- 14 files changed, 1642 insertions(+), 5 deletions(-) create mode 100644 server/src/function-template/dto/create-function-template.dto.ts create mode 100644 server/src/function-template/dto/star-function-template.dto.ts create mode 100644 server/src/function-template/dto/update-function-template.dto.ts create mode 100644 server/src/function-template/dto/use-function-template.dto.ts create mode 100644 server/src/function-template/entities/function-template.ts create mode 100644 server/src/function-template/entities/swagger-help.ts create mode 100644 server/src/function-template/function-template.controller.ts create mode 100644 server/src/function-template/function-template.module.ts create mode 100644 server/src/function-template/function-template.service.ts diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 1169fcecd8..83726798b1 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -22,6 +22,7 @@ import * as path from 'path' import { AcceptLanguageResolver, I18nModule, QueryResolver } from 'nestjs-i18n' import { BillingModule } from './billing/billing.module' import { AuthenticationModule } from './authentication/authentication.module' +import { FunctionTemplateModule } from './function-template/function-template.module' @Module({ imports: [ @@ -62,6 +63,7 @@ import { AuthenticationModule } from './authentication/authentication.module' ), }), BillingModule, + FunctionTemplateModule, ], controllers: [AppController], providers: [AppService], diff --git a/server/src/application/application.module.ts b/server/src/application/application.module.ts index cfc1c723c9..8d9dd8d301 100644 --- a/server/src/application/application.module.ts +++ b/server/src/application/application.module.ts @@ -33,6 +33,11 @@ import { ResourceService } from 'src/billing/resource.service' BundleService, ResourceService, ], - exports: [ApplicationService, BundleService], + exports: [ + ApplicationService, + ApplicationConfigurationService, + EnvironmentVariableService, + BundleService, + ], }) export class ApplicationModule {} diff --git a/server/src/dependency/dependency.module.ts b/server/src/dependency/dependency.module.ts index 06d79104b3..97dbe7f4b9 100644 --- a/server/src/dependency/dependency.module.ts +++ b/server/src/dependency/dependency.module.ts @@ -7,5 +7,6 @@ import { DependencyService } from './dependency.service' imports: [ApplicationModule], controllers: [DependencyController], providers: [DependencyService], + exports: [DependencyService], }) export class DependencyModule {} diff --git a/server/src/dependency/dependency.service.ts b/server/src/dependency/dependency.service.ts index d47eeb5064..4bb983ddc6 100644 --- a/server/src/dependency/dependency.service.ts +++ b/server/src/dependency/dependency.service.ts @@ -125,7 +125,7 @@ export class DependencyService { * @param appid * @returns */ - private async getExtras(appid: string) { + async getExtras(appid: string) { const conf = await this.db .collection('ApplicationConfiguration') .findOne({ appid }) @@ -138,12 +138,12 @@ export class DependencyService { * Get the built-in dependencies in string array * @returns */ - private getBuiltins() { + getBuiltins() { const obj = RUNTIME_BUILTIN_DEPENDENCIES return Object.keys(obj).map((key) => `${key}@${obj[key]}`) } - private validate(dto: CreateDependencyDto) { + validate(dto: CreateDependencyDto) { try { npa.resolve(dto.name, dto.spec) return true diff --git a/server/src/function-template/dto/create-function-template.dto.ts b/server/src/function-template/dto/create-function-template.dto.ts new file mode 100644 index 0000000000..fec8ab7bdc --- /dev/null +++ b/server/src/function-template/dto/create-function-template.dto.ts @@ -0,0 +1,80 @@ +import { ObjectId } from 'mongodb' +import { CreateEnvironmentDto } from '../../application/dto/create-env.dto' +import { CreateDependencyDto } from 'src/dependency/dto/create-dependency.dto' +import { CloudFunctionSource } from 'src/function/entities/cloud-function' +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' +import { + IsArray, + IsBoolean, + IsIn, + IsNotEmpty, + IsOptional, + IsString, + Matches, + MaxLength, + ValidateNested, +} from 'class-validator' +import { Type } from 'class-transformer' +import { HTTP_METHODS } from '../../constants' +import { HttpMethod } from '../../function/entities/cloud-function' +export class FunctionTemplateItemDto { + @ApiProperty({ + description: 'FunctionTemplate function name', + }) + @IsNotEmpty() + @Matches(/^[a-zA-Z0-9_.\-\/]{1,256}$/) + name: string + + @ApiPropertyOptional() + @MaxLength(256) + description?: string + + @ApiProperty({ type: [String], enum: HttpMethod }) + @IsIn(HTTP_METHODS, { each: true }) + methods: HttpMethod[] = [] + + @ApiProperty({ description: 'The source code of the function' }) + @IsNotEmpty() + @IsString() + @MaxLength(1024 * 512) + code: string + + validate() { + return null + } +} + +export class CreateFunctionTemplateDto { + @ApiProperty({ description: 'function template name' }) + @IsNotEmpty() + @IsString() + name: string + + @ApiProperty({ description: 'Dependencies', type: [CreateDependencyDto] }) + @IsNotEmpty() + @ValidateNested({ each: true }) + @Type(() => CreateDependencyDto) + dependencies: CreateDependencyDto[] + + @ApiProperty({ description: 'environments', type: [CreateEnvironmentDto] }) + @ValidateNested({ each: true }) + @Type(() => CreateEnvironmentDto) + environments: CreateEnvironmentDto[] + + @ApiProperty({ description: 'Private flag' }) + @IsBoolean() + private: boolean + + @ApiPropertyOptional({ description: 'function template description' }) + @IsString() + description?: string + + @ApiProperty({ + description: 'items of the function template', + type: [FunctionTemplateItemDto], + }) + @IsNotEmpty() + @ValidateNested({ each: true }) + @Type(() => FunctionTemplateItemDto) + items: FunctionTemplateItemDto[] +} diff --git a/server/src/function-template/dto/star-function-template.dto.ts b/server/src/function-template/dto/star-function-template.dto.ts new file mode 100644 index 0000000000..000652dc7f --- /dev/null +++ b/server/src/function-template/dto/star-function-template.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger' +import { IsNotEmpty } from 'class-validator' +import { ObjectId } from 'mongodb' + +export class StarFunctionTemplateDto { + @ApiProperty({ + description: 'The ObjectId of function template', + }) + @IsNotEmpty() + functionTemplateId: ObjectId +} diff --git a/server/src/function-template/dto/update-function-template.dto.ts b/server/src/function-template/dto/update-function-template.dto.ts new file mode 100644 index 0000000000..972ebfeb18 --- /dev/null +++ b/server/src/function-template/dto/update-function-template.dto.ts @@ -0,0 +1,58 @@ +import { ObjectId } from 'mongodb' +import { CreateEnvironmentDto } from '../../application/dto/create-env.dto' +import { CreateDependencyDto } from 'src/dependency/dto/create-dependency.dto' +import { CloudFunctionSource } from 'src/function/entities/cloud-function' +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' +import { + IsArray, + IsBoolean, + IsIn, + IsNotEmpty, + IsOptional, + IsString, + Matches, + MaxLength, + ValidateNested, +} from 'class-validator' +import { Type } from 'class-transformer' +import { HTTP_METHODS } from '../../constants' +import { HttpMethod } from '../../function/entities/cloud-function' +import { FunctionTemplateItemDto } from './create-function-template.dto' + +export class UpdateFunctionTemplateDto { + @ApiProperty({ description: 'Function template id' }) + functionTemplateId: ObjectId + + @ApiProperty({ description: 'Template name' }) + @IsNotEmpty() + @IsString() + name: string + + @ApiProperty({ description: 'Dependencies', type: [CreateDependencyDto] }) + @IsNotEmpty() + @ValidateNested({ each: true }) + @Type(() => CreateDependencyDto) + dependencies: CreateDependencyDto[] + + @ApiProperty({ description: 'Environments', type: [CreateEnvironmentDto] }) + @ValidateNested({ each: true }) + @Type(() => CreateEnvironmentDto) + environments: CreateEnvironmentDto[] + + @ApiProperty({ description: 'Private flag' }) + @IsBoolean() + private: boolean + + @ApiPropertyOptional({ description: 'Template description' }) + @IsString() + description?: string + + @ApiPropertyOptional({ + description: 'Template items', + type: [FunctionTemplateItemDto], + }) + @IsNotEmpty() + @ValidateNested({ each: true }) + @Type(() => FunctionTemplateItemDto) + items: FunctionTemplateItemDto[] +} diff --git a/server/src/function-template/dto/use-function-template.dto.ts b/server/src/function-template/dto/use-function-template.dto.ts new file mode 100644 index 0000000000..8b8c611f20 --- /dev/null +++ b/server/src/function-template/dto/use-function-template.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger' +import { IsNotEmpty } from 'class-validator' +import { ObjectId } from 'mongodb' + +export class UseFunctionTemplateDto { + @ApiProperty({ + description: 'The ObjectId of function template', + }) + @IsNotEmpty() + functionTemplateId: ObjectId +} diff --git a/server/src/function-template/entities/function-template.ts b/server/src/function-template/entities/function-template.ts new file mode 100644 index 0000000000..c8269daddd --- /dev/null +++ b/server/src/function-template/entities/function-template.ts @@ -0,0 +1,99 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' +import { + CloudFunction, + CloudFunctionSource, + HttpMethod, +} from 'src/function/entities/cloud-function' +import { EnvironmentVariable } from 'src/application/entities/application-configuration' +import { ObjectId } from 'mongodb' + +export class FunctionTemplate { + @ApiProperty({ type: String }) + _id?: ObjectId + + @ApiProperty({ type: String }) + uid: ObjectId + + @ApiProperty() + name: string + + @ApiProperty() + dependencies: string[] + + @ApiProperty({ isArray: true, type: EnvironmentVariable }) + environments: EnvironmentVariable[] + + @ApiProperty() + private: boolean + + @ApiProperty() + isRecommended: boolean + + @ApiProperty() + description: string + + @ApiProperty() + star: number + + @ApiProperty() + createdAt: Date + + @ApiProperty() + updatedAt: Date + + @ApiPropertyOptional() + tags?: string[] + + @ApiPropertyOptional() + category?: string +} + +export class FunctionTemplateItem { + @ApiProperty({ type: String }) + _id?: ObjectId + + @ApiProperty({ type: String }) + templateId: ObjectId + + @ApiProperty() + name: string + + @ApiProperty() + desc: string + + @ApiProperty() + source: Partial + + @ApiProperty({ type: [String], enum: HttpMethod }) + methods: HttpMethod[] + + createdAt: Date + + updatedAt: Date +} + +export class FunctionTemplateStarRelation { + @ApiProperty({ type: String }) + _id?: ObjectId + @ApiProperty({ type: String }) + uid: ObjectId + @ApiProperty({ type: String }) + templateId: ObjectId + @ApiProperty() + createdAt: Date + @ApiProperty() + updatedAt: Date +} + +export class FunctionTemplateUseRelation { + @ApiProperty({ type: String }) + _id?: ObjectId + @ApiProperty({ type: String }) + uid: ObjectId + @ApiProperty({ type: String }) + templateId: ObjectId + @ApiProperty() + createdAt: Date + @ApiProperty() + updatedAt: Date +} diff --git a/server/src/function-template/entities/swagger-help.ts b/server/src/function-template/entities/swagger-help.ts new file mode 100644 index 0000000000..fcf36a3905 --- /dev/null +++ b/server/src/function-template/entities/swagger-help.ts @@ -0,0 +1,146 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' +import { + CloudFunction, + CloudFunctionSource, + HttpMethod, +} from 'src/function/entities/cloud-function' +import { EnvironmentVariable } from 'src/application/entities/application-configuration' +import { ObjectId } from 'mongodb' +import { User } from 'src/user/entities/user' +import { FunctionTemplate, FunctionTemplateItem } from './function-template' + +class FunctionTemplateItemSourceSwagger { + @ApiProperty({ description: 'The source code of the function' }) + code: string +} + +class FunctionTemplateItemSwagger { + @ApiProperty({ type: String }) + _id?: ObjectId + + @ApiProperty({ type: String }) + templateId: ObjectId + + @ApiProperty() + name: string + + @ApiProperty() + desc: string + + @ApiProperty() + source: FunctionTemplateItemSourceSwagger + + @ApiProperty({ type: [String], enum: HttpMethod }) + methods: HttpMethod[] + + @ApiProperty() + createdAt: Date + + @ApiProperty() + updatedAt: Date +} +export class FunctionTemplateSwagger { + @ApiProperty({ type: String }) + _id?: ObjectId + + @ApiProperty({ type: String }) + uid: ObjectId + + @ApiProperty() + name: string + + @ApiProperty() + dependencies: string[] + + @ApiProperty({ isArray: true, type: EnvironmentVariable }) + environments: EnvironmentVariable[] + + @ApiProperty() + private: boolean + + @ApiProperty() + isRecommended: boolean + + @ApiProperty() + description: string + + @ApiProperty() + star: number + + @ApiProperty() + createdAt: Date + + @ApiProperty() + updatedAt: Date + + @ApiPropertyOptional({ type: [FunctionTemplateItemSwagger] }) + items: FunctionTemplateItemSwagger[] +} + +export class GetFunctionTemplateUsedByItemSwagger { + @ApiProperty({ type: String }) + _id: ObjectId + + @ApiProperty({ type: String }) + uid: ObjectId + + @ApiProperty({ type: String }) + templateId: ObjectId + + @ApiProperty() + createdAt: Date + + @ApiProperty() + updatedAt: Date + + @ApiProperty({ + type: [User], + }) + users: User[] +} + +export class GetMyStaredFunctionTemplateSwagger { + @ApiProperty({ type: String }) + _id: ObjectId + + @ApiProperty({ type: String }) + uid: ObjectId + + @ApiProperty({ type: String }) + templateId: ObjectId + + @ApiProperty() + createdAt: Date + + @ApiProperty() + updatedAt: Date + + @ApiProperty({ type: [FunctionTemplate] }) + functionTemplate: FunctionTemplate[] + + @ApiProperty({ type: [FunctionTemplateItemSwagger] }) + items: FunctionTemplateItemSwagger[] +} + +export class GetMyRecentUseFunctionTemplateSwagger { + @ApiProperty({ type: String }) + _id: ObjectId + + @ApiProperty({ type: String }) + uid: ObjectId + + @ApiProperty({ type: String }) + templateId: ObjectId + + @ApiProperty() + createdAt: Date + + @ApiProperty() + updatedAt: Date + + @ApiProperty({ type: [FunctionTemplate] }) + functionTemplate: FunctionTemplate[] + + @ApiProperty({ type: [FunctionTemplateItemSwagger] }) + items: FunctionTemplateItemSwagger[] +} diff --git a/server/src/function-template/function-template.controller.ts b/server/src/function-template/function-template.controller.ts new file mode 100644 index 0000000000..6b55bdd112 --- /dev/null +++ b/server/src/function-template/function-template.controller.ts @@ -0,0 +1,392 @@ +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + UseGuards, + Req, + Query, +} from '@nestjs/common' +import { FunctionTemplateService } from './function-template.service' +import { CreateFunctionTemplateDto } from './dto/create-function-template.dto' +import { StarFunctionTemplateDto } from './dto/star-function-template.dto' +import { UseFunctionTemplateDto } from './dto/use-function-template.dto' +import { UpdateFunctionTemplateDto } from './dto/update-function-template.dto' +import * as assert from 'node:assert' +import { IRequest } from '../utils/interface' +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger' +import { + ResponseUtil, + ApiResponseString, + ApiResponseArray, + ApiResponsePagination, +} from 'src/utils/response' +import { JwtAuthGuard } from 'src/auth/jwt.auth.guard' +import { ApplicationAuthGuard } from 'src/auth/application.auth.guard' +import { ObjectId } from 'mongodb' +import { FunctionService } from 'src/function/function.service' +import { BundleService } from 'src/application/bundle.service' +import { DependencyService } from 'src/dependency/dependency.service' +import { + FunctionTemplateSwagger, + GetFunctionTemplateUsedByItemSwagger, + GetMyStaredFunctionTemplateSwagger, + GetMyRecentUseFunctionTemplateSwagger, +} from './entities/swagger-help' + +@ApiTags('FunctionTemplate') +@ApiBearerAuth('Authorization') +@Controller('function-template/:appid') +export class FunctionTemplateController { + constructor( + private readonly functionsService: FunctionService, + private readonly bundleService: BundleService, + private readonly dependencyService: DependencyService, + private readonly functionTemplateService: FunctionTemplateService, + ) {} + + /** + * Create a new function-template + * @param dto + * @returns + */ + @ApiOperation({ summary: 'create a function template' }) + @ApiResponseArray(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Post('create') + async createFunctiomTemplate( + @Body() dto: CreateFunctionTemplateDto, + @Req() req: IRequest, + ) { + // validate dependencies + const valid = dto.dependencies.every((dep) => + this.dependencyService.validate(dep), + ) + if (!valid) { + return ResponseUtil.error('function template dependencies is invalid') + } + const res = await this.functionTemplateService.createFunctionTemplate( + req.user._id, + dto, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'use a function template' }) + @ApiResponseArray(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Post('use') + async useFunctiomTemplate( + @Param('appid') appid: string, + @Body() dto: UseFunctionTemplateDto, + @Req() req: IRequest, + ) { + dto.functionTemplateId = new ObjectId(dto.functionTemplateId) + + // check if the function template is exist + const found = await this.functionTemplateService.findOneFunctionTemplate( + dto.functionTemplateId, + ) + if (!found) { + return ResponseUtil.error('function template not found') + } + + // function check + const functionTemplateItems = + await this.functionTemplateService.findFunctionTemplateitems( + dto.functionTemplateId, + ) + if (functionTemplateItems.length === 0 || !functionTemplateItems) { + return ResponseUtil.error('function template items not found') + } + + for (const functionTemplateItem of functionTemplateItems) { + // check name is unique + const found = await this.functionsService.findOne( + appid, + functionTemplateItem.name, + ) + if (found) { + return ResponseUtil.error('function name is not unique') + } + // check if meet the count limit + const bundle = await this.bundleService.findOne(appid) + const MAX_FUNCTION_COUNT = + bundle?.resource?.limitCountOfCloudFunction || 0 + const count = await this.functionsService.count(appid) + if (count >= MAX_FUNCTION_COUNT) { + return ResponseUtil.error('exceed the count limit') + } + } + + const res = await this.functionTemplateService.useFunctionTemplate( + req.user._id, + appid, + dto, + ) + + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'update a function template' }) + @ApiResponseArray(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Patch('update') + async updateFunctiomTemplate( + @Body() dto: UpdateFunctionTemplateDto, + @Req() req: IRequest, + ) { + dto.functionTemplateId = new ObjectId(dto.functionTemplateId) + + // check if the function template is exist + const found = await this.functionTemplateService.findOneFunctionTemplate( + dto.functionTemplateId, + req.user._id, + ) + if (!found) { + return ResponseUtil.error('function template not found') + } + + const res = await this.functionTemplateService.updateFunctionTemplate( + req.user._id, + dto.functionTemplateId, + dto, + ) + + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'delete a function template' }) + @ApiResponseArray(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Delete('delete') + async deleteFunctiomTemplate( + @Query('id') templateId: string, + @Req() req: IRequest, + ) { + const found = await this.functionTemplateService.findOneFunctionTemplate( + new ObjectId(templateId), + req.user._id, + ) + if (!found) { + return ResponseUtil.error('function template not found') + } + const res = await this.functionTemplateService.deleteFunctionTemplate( + new ObjectId(templateId), + req.user._id, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'star a function template' }) + @ApiResponseString() + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Post('star') + async starFunctiomTemplate( + @Body() dto: StarFunctionTemplateDto, + @Req() req: IRequest, + ) { + dto.functionTemplateId = new ObjectId(dto.functionTemplateId) + const found = await this.functionTemplateService.findOneFunctionTemplate( + dto.functionTemplateId, + ) + if (!found) { + return ResponseUtil.error('function template not found') + } + const res = await this.functionTemplateService.starFunctionTemplate( + dto.functionTemplateId, + req.user._id, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get function template user star state' }) + @ApiResponseString() + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Get('star') + async getUserFunctionTemplateStarState( + @Query('id') templateId: string, + @Req() req: IRequest, + ) { + const res = + await this.functionTemplateService.functionTemplateUserStarState( + new ObjectId(templateId), + req.user._id, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get people who use this function template' }) + @ApiResponsePagination(GetFunctionTemplateUsedByItemSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Get('used-by') + async getFunctionTemplateUsedBy( + @Query('id') templateId: string, + @Query('recent') recent: number, + @Query('page') page: number, + @Query('pageSize') pageSize: number, + ) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + const found = await this.functionTemplateService.findOneFunctionTemplate( + new ObjectId(templateId), + ) + if (!found) { + return ResponseUtil.error('function template not found') + } + const res = await this.functionTemplateService.functionTemplateUsedBy( + new ObjectId(templateId), + recent, + page, + pageSize, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get one function template' }) + @ApiResponseArray(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Get('one') + async getOneFunctionTemplate( + @Query('id') templateId: string, + @Req() req: IRequest, + ) { + const template = await this.functionTemplateService.findOneFunctionTemplate( + new ObjectId(templateId), + ) + + try { + assert( + template.private === false || + template.uid.toString() === req.user._id.toString(), + 'private function template can only be inspect by the owner', + ) + } catch (error) { + return ResponseUtil.error(error.message) + } + + const res = await this.functionTemplateService.findOne( + new ObjectId(templateId), + ) + + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get all function template' }) + @ApiResponsePagination(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Get('all') + async getAllFunctionTemplate( + @Query('recent') recent: number, + @Query('page') page: number, + @Query('pageSize') pageSize: number, + ) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = await this.functionTemplateService.findFunctionTemplates( + recent, + page, + pageSize, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get most star function template' }) + @ApiResponsePagination(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Get('hot') + async getHotFunctionTemplate( + @Query('starAsc') starAsc: number, + @Query('page') page: number, + @Query('pageSize') pageSize: number, + ) { + starAsc = starAsc === 0 ? Number(starAsc) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findMostStarFunctionTemplates( + starAsc, + page, + pageSize, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get most star function template' }) + @ApiResponsePagination(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Get('my') + async getMyFunctionTemplate( + @Query('recent') recent: number, + @Query('page') page: number, + @Query('pageSize') pageSize: number, + @Req() req: IRequest, + ) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = await this.functionTemplateService.findMyFunctionTemplates( + recent, + page, + pageSize, + req.user._id, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get my star function template' }) + @ApiResponsePagination(GetMyStaredFunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Get('my-star') + async getMyStaredFunctionTemplate( + @Query('recent') recent: number, + @Query('page') page: number, + @Query('pageSize') pageSize: number, + @Req() req: IRequest, + ) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findMyStaredFunctionTemplates( + recent, + page, + pageSize, + req.user._id, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get my recent used function template' }) + @ApiResponsePagination(GetMyRecentUseFunctionTemplateSwagger) + @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @Get('my-recent') + async getMyRecentUseFunctionTemplate( + @Query('recent') recent: number, + @Query('page') page: number, + @Query('pageSize') pageSize: number, + @Req() req: IRequest, + ) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findMyRecentUseFunctionTemplates( + recent, + page, + pageSize, + req.user._id, + ) + return ResponseUtil.ok(res) + } +} diff --git a/server/src/function-template/function-template.module.ts b/server/src/function-template/function-template.module.ts new file mode 100644 index 0000000000..7a7205657c --- /dev/null +++ b/server/src/function-template/function-template.module.ts @@ -0,0 +1,21 @@ +import { Module } from '@nestjs/common' +import { FunctionTemplateService } from './function-template.service' +import { FunctionTemplateController } from './function-template.controller' +// import { EnvironmentVariableService } from '../application/environment.service' +import { ApplicationModule } from 'src/application/application.module' +import { DatabaseModule } from 'src/database/database.module' +import { FunctionModule } from 'src/function/function.module' +import { DependencyModule } from '../dependency/dependency.module' + +@Module({ + imports: [ + ApplicationModule, + DatabaseModule, + FunctionModule, + DependencyModule, + ], + controllers: [FunctionTemplateController], + // providers: [FunctionTemplateService, EnvironmentVariableService], + providers: [FunctionTemplateService], +}) +export class FunctionTemplateModule {} diff --git a/server/src/function-template/function-template.service.ts b/server/src/function-template/function-template.service.ts new file mode 100644 index 0000000000..87ef7ccd94 --- /dev/null +++ b/server/src/function-template/function-template.service.ts @@ -0,0 +1,811 @@ +import { Injectable, Logger } from '@nestjs/common' +import { CreateFunctionTemplateDto } from './dto/create-function-template.dto' +import { UseFunctionTemplateDto } from './dto/use-function-template.dto' +import { UpdateFunctionTemplateDto } from './dto/update-function-template.dto' +import { SystemDatabase } from 'src/system-database' +import { + FunctionTemplate, + FunctionTemplateItem, + FunctionTemplateUseRelation, + FunctionTemplateStarRelation, +} from './entities/function-template' +import { ObjectId } from 'mongodb' +import { EnvironmentVariableService } from 'src/application/environment.service' +import { ApplicationConfiguration } from 'src/application/entities/application-configuration' +import * as assert from 'node:assert' +import * as npa from 'npm-package-arg' +import { CloudFunction } from 'src/function/entities/cloud-function' +import { compileTs2js } from '../utils/lang' +import { CN_PUBLISHED_CONF, CN_PUBLISHED_FUNCTIONS } from 'src/constants' +import { DatabaseService } from 'src/database/database.service' +import { DependencyService } from 'src/dependency/dependency.service' + +@Injectable() +export class FunctionTemplateService { + constructor( + private readonly environmentVariableService: EnvironmentVariableService, + private readonly databaseService: DatabaseService, + private readonly dependencyService: DependencyService, + ) {} + + private readonly logger = new Logger(FunctionTemplateService.name) + private readonly db = SystemDatabase.db + + async createFunctionTemplate( + userid: ObjectId, + dto: CreateFunctionTemplateDto, + ) { + const client = SystemDatabase.client + const session = client.startSession() + + try { + session.startTransaction() + + const insertedTemplate = await this.db + .collection('FunctionTemplate') + .insertOne( + { + uid: userid, + name: dto.name, + dependencies: dto.dependencies.map( + (dep) => `${dep.name}@${dep.spec}`, + ), + environments: dto.environments, + private: dto.private, + isRecommended: false, + description: dto.description || '', + star: 0, + createdAt: new Date(), + updatedAt: new Date(), + }, + { session }, + ) + + const functionTemplateItems = dto.items.map((item) => ({ + templateId: insertedTemplate.insertedId, + name: item.name, + desc: item.description || '', + source: { code: item.code }, + methods: item.methods, + createdAt: new Date(), + updatedAt: new Date(), + })) + + await this.db + .collection('FunctionTemplateItem') + .insertMany(functionTemplateItems, { session }) + + await session.commitTransaction() + + const pipe = [ + { $match: { _id: insertedTemplate.insertedId } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + ] + const template = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() + return template + } catch (error) { + await session.abortTransaction() + this.logger.error(error) + throw error + } finally { + await session.endSession() + } + } + + async updateFunctionTemplate( + userid: ObjectId, + templateId: ObjectId, + dto: UpdateFunctionTemplateDto, + ) { + const client = SystemDatabase.client + const session = client.startSession() + + try { + session.startTransaction() + + const found = await this.db + .collection('FunctionTemplate') + .findOne({ _id: templateId, uid: userid }) + + // If a template transitions from public to private, the star relationships and use relationships of other users with that template should also be removed. + if (found.private === false && dto.private === true) { + await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .deleteMany( + { templateId: templateId, uid: { $ne: userid } }, + { session }, + ) + await this.db + .collection( + 'FunctionTemplateUseRelation', + ) + .deleteMany( + { templateId: templateId, uid: { $ne: userid } }, + { session }, + ) + const star = await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .findOne({ uid: userid, templateId: templateId }, { session }) + if (star) { + await this.db + .collection('FunctionTemplate') + .updateOne( + { _id: templateId, uid: userid }, + { $set: { star: 1 } }, + { session }, + ) + } else { + await this.db + .collection('FunctionTemplate') + .updateOne( + { _id: templateId, uid: userid }, + { $set: { star: 0 } }, + { session }, + ) + } + } + + const res = await this.db + .collection('FunctionTemplate') + .findOneAndUpdate( + { _id: templateId, uid: userid }, + { + $set: { + name: dto.name, + dependencies: dto.dependencies.map( + (dep) => `${dep.name}@${dep.spec}`, + ), + environments: dto.environments, + private: dto.private, + isRecommended: false, + description: dto.description || '', + }, + $currentDate: { updatedAt: true }, + }, + { + session: session, + returnDocument: 'after', + }, + ) + if (res.lastErrorObject.updatedExisting) { + const functionTemplateItems = dto.items.map((item) => ({ + templateId: res.value._id, + name: item.name, + desc: item.description || '', + source: { code: item.code }, + methods: item.methods, + createdAt: new Date(), + updatedAt: new Date(), + })) + await this.db + .collection('FunctionTemplateItem') + .deleteMany({ templateId: templateId }, { session }) + await this.db + .collection('FunctionTemplateItem') + .insertMany(functionTemplateItems, { session }) + } + await session.commitTransaction() + + const pipe = [ + { $match: { _id: templateId } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + ] + const template = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() + return template + } catch (error) { + await session.abortTransaction() + this.logger.error(error) + throw error + } finally { + await session.endSession() + } + } + + // use function template + async useFunctionTemplate( + userid: ObjectId, + appid: string, + dto: UseFunctionTemplateDto, + ) { + const client = SystemDatabase.client + const appDataBaseInfo = await this.databaseService.findOne(appid) + const appDataBase = client.db(appDataBaseInfo.name) + + const session = client.startSession() + + try { + session.startTransaction() + + const functionTemplate = await this.findOneFunctionTemplate( + dto.functionTemplateId, + ) + assert( + functionTemplate.private === false || + functionTemplate.uid.toString() === userid.toString(), + 'private function template can only be used by the owner', + ) + // add function template dependencies to application configuration + // + const extraDenpendencies = await this.dependencyService.getExtras(appid) + const builtinDependencies = this.dependencyService.getBuiltins() + const all = extraDenpendencies.concat(builtinDependencies) + const allnames = all.map((item) => npa(item).name) + + // If the dependency already exists, use the original dependency. + const filtered = functionTemplate.dependencies.filter((item) => { + const name = npa(item).name + return !allnames.includes(name) + }) + const deps = extraDenpendencies.concat(filtered) + + await this.db + .collection('ApplicationConfiguration') + .updateOne( + { appid }, + { + $set: { dependencies: deps }, + $currentDate: { updatedAt: true }, + }, + { session }, + ) + + // add function template Env to application configuration + // + const orginEnv = await this.environmentVariableService.findAll(appid) + const mergedEnv = [ + ...orginEnv, + ...functionTemplate.environments.filter( + (item2) => !orginEnv.some((item1) => item1.name === item2.name), + ), + ] + + const applicationConf = await this.db + .collection('ApplicationConfiguration') + .findOneAndUpdate( + { appid }, + { + $set: { environments: mergedEnv }, + $currentDate: { updatedAt: true }, + }, + { returnDocument: 'after', session }, + ) + assert(applicationConf?.value, 'application configuration not found') + + // publish application configuration to app database + const coll = appDataBase.collection(CN_PUBLISHED_CONF) + await coll.deleteOne({ appid: applicationConf.value.appid }, { session }) + await coll.insertOne(applicationConf.value, { session }) + + // publish function template items to CloudFunction and app database + // + const functionTemplateItems = await this.findFunctionTemplateitems( + dto.functionTemplateId, + ) + + for (const functionTemplateItem of functionTemplateItems) { + await this.db.collection('CloudFunction').insertOne( + { + appid, + name: functionTemplateItem.name, + source: { + code: functionTemplateItem.source.code, + compiled: compileTs2js(functionTemplateItem.source.code), + version: 0, + }, + desc: functionTemplateItem.desc || '', + createdBy: userid, + methods: functionTemplateItem.methods, + tags: [], + createdAt: new Date(), + updatedAt: new Date(), + }, + { session }, + ) + } + + // add function template items to app database + for (const functionTemplateItem of functionTemplateItems) { + const fn = await this.db + .collection('CloudFunction') + .findOne( + { appid: appid, name: functionTemplateItem.name }, + { session }, + ) + + const coll = appDataBase.collection(CN_PUBLISHED_FUNCTIONS) + await coll.deleteOne({ name: fn.name }, { session }) + await coll.insertOne(fn, { session }) + } + + // user use relation + const res = await this.db + .collection('FunctionTemplateUseRelation') + .findOneAndUpdate( + { uid: userid, templateId: dto.functionTemplateId }, + { + $currentDate: { updatedAt: true }, + }, + { session }, + ) + if (!res.lastErrorObject.updatedExisting) { + await this.db + .collection( + 'FunctionTemplateUseRelation', + ) + .insertOne( + { + uid: userid, + templateId: dto.functionTemplateId, + createdAt: new Date(), + updatedAt: new Date(), + }, + { session }, + ) + } + + await session.commitTransaction() + + const pipe = [ + { $match: { _id: dto.functionTemplateId } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + ] + const result = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() + return result + } catch (error) { + await session.abortTransaction() + this.logger.error(error) + throw error + } finally { + await session.endSession() + } + } + + async deleteFunctionTemplate(templateId: ObjectId, userid: ObjectId) { + const client = SystemDatabase.client + const session = client.startSession() + try { + session.startTransaction() + + const pipe = [ + { $match: { _id: templateId } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + ] + + const deletedFunctionTemplate = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() + + await this.db + .collection('FunctionTemplate') + .deleteOne({ _id: templateId, uid: userid }, { session }) + await this.db + .collection('FunctionTemplateItem') + .deleteMany({ templateId: templateId }, { session }) + + await session.commitTransaction() + + return deletedFunctionTemplate + } catch (error) { + await session.abortTransaction() + throw error + } finally { + await session.endSession() + } + } + async starFunctionTemplate(templateId: ObjectId, userid: ObjectId) { + const client = SystemDatabase.client + const session = client.startSession() + try { + session.startTransaction() + const functionTemplate = await this.findOneFunctionTemplate(templateId) + assert( + functionTemplate.private === false || + functionTemplate.uid.toString() === userid.toString(), + 'private function template can only be stared by the owner', + ) + + const found = await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .findOne({ uid: userid, templateId: templateId }) + + if (found) { + await this.db + .collection('FunctionTemplate') + .updateOne( + { _id: templateId }, + { $inc: { star: -1 }, $currentDate: { updatedAt: true } }, + { session }, + ) + await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .deleteOne({ uid: userid, templateId: templateId }, { session }) + await session.commitTransaction() + return 'unstar' + } + + await this.db + .collection('FunctionTemplate') + .updateOne( + { _id: templateId }, + { $inc: { star: 1 }, $currentDate: { updatedAt: true } }, + { session }, + ) + + await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .insertOne( + { + uid: userid, + templateId: templateId, + createdAt: new Date(), + updatedAt: new Date(), + }, + { session }, + ) + + await session.commitTransaction() + return 'stared' + } catch (error) { + await session.abortTransaction() + throw error + } finally { + await session.endSession + } + } + + async functionTemplateUserStarState(templateId: ObjectId, userid: ObjectId) { + const res = await this.db + .collection('FunctionTemplateStarRelation') + .findOne({ uid: userid, templateId: templateId }) + if (res) { + return 'stared' + } else { + return 'unstar' + } + } + + async functionTemplateUsedBy( + templateId: ObjectId, + recent: number, + page: number, + pageSize: number, + ) { + const pipe = [ + { $match: { templateId: templateId } }, + { + $lookup: { + from: 'User', + localField: 'uid', + foreignField: '_id', + as: 'users', + }, + }, + { $sort: { updatedAt: recent === 0 ? 1 : -1 } }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + const usedBy = await this.db + .collection('FunctionTemplateUseRelation') + .aggregate(pipe) + .toArray() + + const total = await this.db + .collection('FunctionTemplateUseRelation') + .countDocuments({ templateId }) + + const res = { + list: usedBy, + total: total, + page, + pageSize, + } + + return res + } + + async findOne(templateId: ObjectId) { + const pipe = [ + { $match: { _id: templateId } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + ] + const functionTemplate = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() + + return functionTemplate + } + + async findFunctionTemplates(recent: number, page: number, pageSize: number) { + const pipe = [ + { $match: { private: false } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const total = await this.db + .collection('FunctionTemplate') + .countDocuments({ private: false }) + + const functionTemplate = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() + + const res = { + list: functionTemplate, + total: total, + page, + pageSize, + } + + return res + } + + async findMostStarFunctionTemplates( + starAsc: number, + page: number, + pageSize: number, + ) { + const pipe = [ + { $match: { private: false } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + { + $sort: { + star: starAsc === 0 ? 1 : -1, + }, + }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const total = await this.db + .collection('FunctionTemplate') + .countDocuments({ private: false }) + + const templates = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() + + const res = { + list: templates, + total: total, + page, + pageSize, + } + + return res + } + + async findMyFunctionTemplates( + recent: number, + page: number, + pageSize: number, + userid: ObjectId, + ) { + const pipe = [ + { $match: { uid: userid } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const total = await this.db + .collection('FunctionTemplate') + .countDocuments({ uid: userid }) + + const myTemplate = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() + + const res = { + list: myTemplate, + total: total, + page, + pageSize, + } + + return res + } + + async findMyStaredFunctionTemplates( + recent: number, + page: number, + pageSize: number, + userid: ObjectId, + ) { + const pipe = [ + { $match: { uid: userid } }, + { + $lookup: { + from: 'FunctionTemplate', + localField: 'templateId', + foreignField: '_id', + as: 'functionTemplate', + }, + }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: 'templateId', + foreignField: 'templateId', + as: 'items', + }, + }, + { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const total = await this.db + .collection('FunctionTemplateStarRelation') + .countDocuments({ uid: userid }) + + const myStarTemplates = await this.db + .collection('FunctionTemplateStarRelation') + .aggregate(pipe) + .toArray() + + const res = { + list: myStarTemplates, + total: total, + page, + pageSize, + } + + return res + } + + async findMyRecentUseFunctionTemplates( + recent: number, + page: number, + pageSize: number, + userid: ObjectId, + ) { + const pipe = [ + { $match: { uid: userid } }, + { + $lookup: { + from: 'FunctionTemplate', + localField: 'templateId', + foreignField: '_id', + as: 'functionTemplates', + }, + }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: 'templateId', + foreignField: 'templateId', + as: 'items', + }, + }, + { $sort: { updatedAt: recent === 0 ? 1 : -1 } }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const recentUseFunctionTemplates = await this.db + .collection('FunctionTemplateUseRelation') + .aggregate(pipe) + .toArray() + + const total = await this.db + .collection('FunctionTemplateUseRelation') + .countDocuments({ uid: userid }) + + const res = { + list: recentUseFunctionTemplates, + total: total, + page, + pageSize, + } + + return res + } + + async findOneFunctionTemplate(templateId: ObjectId, userid?: ObjectId) { + if (userid) { + // 查找一个 function template + const res = await this.db + .collection('FunctionTemplate') + .findOne({ _id: templateId, uid: userid }) + return res + } + + const res = await this.db + .collection('FunctionTemplate') + .findOne({ _id: templateId }) + return res + } + + async findFunctionTemplateitems(templateId: ObjectId) { + const functionTemplateItems = await this.db + .collection('FunctionTemplateItem') + .find({ templateId: templateId }) + .toArray() + + return functionTemplateItems + } +} diff --git a/server/tsconfig.json b/server/tsconfig.json index adb614cab7..12cebf4b35 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -18,4 +18,4 @@ "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false } -} +} \ No newline at end of file From 98d05ac31c58f2e67dffa8bb616172de2f991016 Mon Sep 17 00:00:00 2001 From: maslow Date: Wed, 7 Jun 2023 15:59:08 +0800 Subject: [PATCH 2/8] refactor(server): refactor auth api(profile/pat) (#1226) --- server/src/app.module.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 83726798b1..d91ae0e55c 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -23,6 +23,7 @@ import { AcceptLanguageResolver, I18nModule, QueryResolver } from 'nestjs-i18n' import { BillingModule } from './billing/billing.module' import { AuthenticationModule } from './authentication/authentication.module' import { FunctionTemplateModule } from './function-template/function-template.module' +import { AuthenticationModule } from './authentication/authentication.module' @Module({ imports: [ From bb33ed71ff73f914f98260e4888a9b8f9bec3cea Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Fri, 9 Jun 2023 02:22:56 +0000 Subject: [PATCH 3/8] feat(server): add function template market --- server/src/app.module.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/app.module.ts b/server/src/app.module.ts index d91ae0e55c..f65746e9cd 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -24,6 +24,7 @@ import { BillingModule } from './billing/billing.module' import { AuthenticationModule } from './authentication/authentication.module' import { FunctionTemplateModule } from './function-template/function-template.module' import { AuthenticationModule } from './authentication/authentication.module' +import { FunctionTemplateModule } from './function-template/function-template.module' @Module({ imports: [ From 2592319d4075f9669acb1a9d438eaea203c934bd Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Tue, 13 Jun 2023 02:39:18 +0000 Subject: [PATCH 4/8] chore(server: Interface aggregation) --- server/src/app.module.ts | 2 - .../dto/star-function-template.dto.ts | 1 + .../dto/use-function-template.dto.ts | 4 + .../entities/function-template.ts | 1 - .../function-template.controller.ts | 391 +++++++++++++----- .../function-template.service.ts | 279 ++++++++++++- 6 files changed, 564 insertions(+), 114 deletions(-) diff --git a/server/src/app.module.ts b/server/src/app.module.ts index f65746e9cd..83726798b1 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -23,8 +23,6 @@ import { AcceptLanguageResolver, I18nModule, QueryResolver } from 'nestjs-i18n' import { BillingModule } from './billing/billing.module' import { AuthenticationModule } from './authentication/authentication.module' import { FunctionTemplateModule } from './function-template/function-template.module' -import { AuthenticationModule } from './authentication/authentication.module' -import { FunctionTemplateModule } from './function-template/function-template.module' @Module({ imports: [ diff --git a/server/src/function-template/dto/star-function-template.dto.ts b/server/src/function-template/dto/star-function-template.dto.ts index 000652dc7f..f9995dab98 100644 --- a/server/src/function-template/dto/star-function-template.dto.ts +++ b/server/src/function-template/dto/star-function-template.dto.ts @@ -5,6 +5,7 @@ import { ObjectId } from 'mongodb' export class StarFunctionTemplateDto { @ApiProperty({ description: 'The ObjectId of function template', + type: 'string', }) @IsNotEmpty() functionTemplateId: ObjectId diff --git a/server/src/function-template/dto/use-function-template.dto.ts b/server/src/function-template/dto/use-function-template.dto.ts index 8b8c611f20..58574e49d7 100644 --- a/server/src/function-template/dto/use-function-template.dto.ts +++ b/server/src/function-template/dto/use-function-template.dto.ts @@ -5,7 +5,11 @@ import { ObjectId } from 'mongodb' export class UseFunctionTemplateDto { @ApiProperty({ description: 'The ObjectId of function template', + type: 'string', }) @IsNotEmpty() functionTemplateId: ObjectId + + @ApiProperty() + appid: string } diff --git a/server/src/function-template/entities/function-template.ts b/server/src/function-template/entities/function-template.ts index c8269daddd..6d96f82a7b 100644 --- a/server/src/function-template/entities/function-template.ts +++ b/server/src/function-template/entities/function-template.ts @@ -1,6 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' import { - CloudFunction, CloudFunctionSource, HttpMethod, } from 'src/function/entities/cloud-function' diff --git a/server/src/function-template/function-template.controller.ts b/server/src/function-template/function-template.controller.ts index 6b55bdd112..80dbe3e0f0 100644 --- a/server/src/function-template/function-template.controller.ts +++ b/server/src/function-template/function-template.controller.ts @@ -4,11 +4,11 @@ import { Post, Body, Patch, - Param, Delete, UseGuards, Req, Query, + Param, } from '@nestjs/common' import { FunctionTemplateService } from './function-template.service' import { CreateFunctionTemplateDto } from './dto/create-function-template.dto' @@ -24,8 +24,7 @@ import { ApiResponseArray, ApiResponsePagination, } from 'src/utils/response' -import { JwtAuthGuard } from 'src/auth/jwt.auth.guard' -import { ApplicationAuthGuard } from 'src/auth/application.auth.guard' +import { JwtAuthGuard } from 'src/authentication/jwt.auth.guard' import { ObjectId } from 'mongodb' import { FunctionService } from 'src/function/function.service' import { BundleService } from 'src/application/bundle.service' @@ -39,7 +38,7 @@ import { @ApiTags('FunctionTemplate') @ApiBearerAuth('Authorization') -@Controller('function-template/:appid') +@Controller('function-template') export class FunctionTemplateController { constructor( private readonly functionsService: FunctionService, @@ -55,9 +54,9 @@ export class FunctionTemplateController { */ @ApiOperation({ summary: 'create a function template' }) @ApiResponseArray(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @UseGuards(JwtAuthGuard) @Post('create') - async createFunctiomTemplate( + async createFunctionTemplate( @Body() dto: CreateFunctionTemplateDto, @Req() req: IRequest, ) { @@ -77,15 +76,24 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'use a function template' }) @ApiResponseArray(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @UseGuards(JwtAuthGuard) @Post('use') - async useFunctiomTemplate( - @Param('appid') appid: string, + async useFunctionTemplate( @Body() dto: UseFunctionTemplateDto, @Req() req: IRequest, ) { dto.functionTemplateId = new ObjectId(dto.functionTemplateId) + // Check if appid is a valid resource + const valid = await this.functionTemplateService.applicationAuthGuard( + dto.appid, + req.user._id, + ) + + if (!valid) { + return ResponseUtil.error('invalid resource') + } + // check if the function template is exist const found = await this.functionTemplateService.findOneFunctionTemplate( dto.functionTemplateId, @@ -96,7 +104,7 @@ export class FunctionTemplateController { // function check const functionTemplateItems = - await this.functionTemplateService.findFunctionTemplateitems( + await this.functionTemplateService.findFunctionTemplateItems( dto.functionTemplateId, ) if (functionTemplateItems.length === 0 || !functionTemplateItems) { @@ -106,17 +114,17 @@ export class FunctionTemplateController { for (const functionTemplateItem of functionTemplateItems) { // check name is unique const found = await this.functionsService.findOne( - appid, + dto.appid, functionTemplateItem.name, ) if (found) { return ResponseUtil.error('function name is not unique') } // check if meet the count limit - const bundle = await this.bundleService.findOne(appid) + const bundle = await this.bundleService.findOne(dto.appid) const MAX_FUNCTION_COUNT = bundle?.resource?.limitCountOfCloudFunction || 0 - const count = await this.functionsService.count(appid) + const count = await this.functionsService.count(dto.appid) if (count >= MAX_FUNCTION_COUNT) { return ResponseUtil.error('exceed the count limit') } @@ -124,7 +132,7 @@ export class FunctionTemplateController { const res = await this.functionTemplateService.useFunctionTemplate( req.user._id, - appid, + dto.appid, dto, ) @@ -133,9 +141,9 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'update a function template' }) @ApiResponseArray(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @UseGuards(JwtAuthGuard) @Patch('update') - async updateFunctiomTemplate( + async updateFunctionTemplate( @Body() dto: UpdateFunctionTemplateDto, @Req() req: IRequest, ) { @@ -161,10 +169,10 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'delete a function template' }) @ApiResponseArray(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) - @Delete('delete') - async deleteFunctiomTemplate( - @Query('id') templateId: string, + @UseGuards(JwtAuthGuard) + @Delete(':id') + async deleteFunctionTemplate( + @Param('id') templateId: string, @Req() req: IRequest, ) { const found = await this.functionTemplateService.findOneFunctionTemplate( @@ -183,9 +191,9 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'star a function template' }) @ApiResponseString() - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @UseGuards(JwtAuthGuard) @Post('star') - async starFunctiomTemplate( + async starFunctionTemplate( @Body() dto: StarFunctionTemplateDto, @Req() req: IRequest, ) { @@ -205,10 +213,10 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'get function template user star state' }) @ApiResponseString() - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) - @Get('star') + @UseGuards(JwtAuthGuard) + @Get('star/:id') async getUserFunctionTemplateStarState( - @Query('id') templateId: string, + @Param('id') templateId: string, @Req() req: IRequest, ) { const res = @@ -221,10 +229,10 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'get people who use this function template' }) @ApiResponsePagination(GetFunctionTemplateUsedByItemSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) - @Get('used-by') + @UseGuards(JwtAuthGuard) + @Get('used-by/:id') async getFunctionTemplateUsedBy( - @Query('id') templateId: string, + @Param('id') templateId: string, @Query('recent') recent: number, @Query('page') page: number, @Query('pageSize') pageSize: number, @@ -249,10 +257,10 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'get one function template' }) @ApiResponseArray(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) - @Get('one') + @UseGuards(JwtAuthGuard) + @Get('one/:id') async getOneFunctionTemplate( - @Query('id') templateId: string, + @Param('id') templateId: string, @Req() req: IRequest, ) { const template = await this.functionTemplateService.findOneFunctionTemplate( @@ -278,13 +286,43 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'get all function template' }) @ApiResponsePagination(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @UseGuards(JwtAuthGuard) @Get('all') async getAllFunctionTemplate( @Query('recent') recent: number, @Query('page') page: number, @Query('pageSize') pageSize: number, + @Query('name') name: string, + @Query('starAsc') starAsc: number, + @Query('hot') hot: boolean, ) { + if (name) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findFunctionTemplatesByName( + recent, + page, + pageSize, + name, + ) + return ResponseUtil.ok(res) + } + if (hot) { + starAsc = starAsc === 0 ? Number(starAsc) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findMostStarFunctionTemplates( + starAsc, + page, + pageSize, + ) + return ResponseUtil.ok(res) + } recent = recent === 0 ? Number(recent) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 @@ -294,41 +332,172 @@ export class FunctionTemplateController { page, pageSize, ) + return ResponseUtil.ok(res) } - @ApiOperation({ summary: 'get most star function template' }) - @ApiResponsePagination(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) - @Get('hot') - async getHotFunctionTemplate( - @Query('starAsc') starAsc: number, - @Query('page') page: number, - @Query('pageSize') pageSize: number, - ) { - starAsc = starAsc === 0 ? Number(starAsc) : 1 - page = page ? Number(page) : 1 - pageSize = pageSize ? Number(pageSize) : 10 + // @ApiOperation({ summary: 'get all function template' }) + // @ApiResponsePagination(FunctionTemplateSwagger) + // @UseGuards(JwtAuthGuard) + // @Get('all-byname') + // async getAllFunctionTemplateByName( + // @Query('recent') recent: number, + // @Query('page') page: number, + // @Query('pageSize') pageSize: number, + // @Query('name') name: string, + // ) { + // recent = recent === 0 ? Number(recent) : 1 + // page = page ? Number(page) : 1 + // pageSize = pageSize ? Number(pageSize) : 10 - const res = - await this.functionTemplateService.findMostStarFunctionTemplates( - starAsc, - page, - pageSize, - ) - return ResponseUtil.ok(res) - } + // const res = await this.functionTemplateService.findFunctionTemplatesByName( + // recent, + // page, + // pageSize, + // name, + // ) + // return ResponseUtil.ok(res) + // } + + // @ApiOperation({ summary: 'get most star function template' }) + // @ApiResponsePagination(FunctionTemplateSwagger) + // @UseGuards(JwtAuthGuard) + // @Get('hot') + // async getHotFunctionTemplate( + // @Query('starAsc') starAsc: number, + // @Query('page') page: number, + // @Query('pageSize') pageSize: number, + // ) { + // starAsc = starAsc === 0 ? Number(starAsc) : 1 + // page = page ? Number(page) : 1 + // pageSize = pageSize ? Number(pageSize) : 10 + + // const res = + // await this.functionTemplateService.findMostStarFunctionTemplates( + // starAsc, + // page, + // pageSize, + // ) + // return ResponseUtil.ok(res) + // } + + // @ApiOperation({ summary: 'get my most star function template' }) + // @ApiResponsePagination(FunctionTemplateSwagger) + // @UseGuards(JwtAuthGuard) + // @Get('my-hot') + // async getMyHotFunctionTemplate( + // @Query('starAsc') starAsc: number, + // @Query('page') page: number, + // @Query('pageSize') pageSize: number, + // @Req() req: IRequest, + // ) { + // starAsc = starAsc === 0 ? Number(starAsc) : 1 + // page = page ? Number(page) : 1 + // pageSize = pageSize ? Number(pageSize) : 10 - @ApiOperation({ summary: 'get most star function template' }) + // const res = + // await this.functionTemplateService.findMyMostStarFunctionTemplates( + // starAsc, + // page, + // pageSize, + // req.user._id, + // ) + // return ResponseUtil.ok(res) + // } + + @ApiOperation({ summary: 'get my template function' }) @ApiResponsePagination(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) + @UseGuards(JwtAuthGuard) @Get('my') async getMyFunctionTemplate( @Query('recent') recent: number, @Query('page') page: number, @Query('pageSize') pageSize: number, + @Query('name') name: string, + @Query('starName') starName: string, + @Query('stared') stared: boolean, + @Query('recentUsed') recentUsed: boolean, + @Query('hot') hot: boolean, + @Query('starAsc') starAsc: number, @Req() req: IRequest, ) { + if (name) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findMyFunctionTemplatesByName( + recent, + page, + pageSize, + req.user._id, + name, + ) + return ResponseUtil.ok(res) + } + + if (starName) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findMyStaredFunctionTemplates( + recent, + page, + pageSize, + req.user._id, + starName, + ) + return ResponseUtil.ok(res) + } + + if (stared) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findMyStaredFunctionTemplates( + recent, + page, + pageSize, + req.user._id, + ) + return ResponseUtil.ok(res) + } + + if (recentUsed) { + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findMyRecentUseFunctionTemplates( + recent, + page, + pageSize, + req.user._id, + ) + return ResponseUtil.ok(res) + } + + if (hot) { + starAsc = starAsc === 0 ? Number(starAsc) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = + await this.functionTemplateService.findMyMostStarFunctionTemplates( + starAsc, + page, + pageSize, + req.user._id, + ) + return ResponseUtil.ok(res) + } + recent = recent === 0 ? Number(recent) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 @@ -342,51 +511,77 @@ export class FunctionTemplateController { return ResponseUtil.ok(res) } - @ApiOperation({ summary: 'get my star function template' }) - @ApiResponsePagination(GetMyStaredFunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) - @Get('my-star') - async getMyStaredFunctionTemplate( - @Query('recent') recent: number, - @Query('page') page: number, - @Query('pageSize') pageSize: number, - @Req() req: IRequest, - ) { - recent = recent === 0 ? Number(recent) : 1 - page = page ? Number(page) : 1 - pageSize = pageSize ? Number(pageSize) : 10 + // @ApiOperation({ summary: 'get my template function by name' }) + // @ApiResponsePagination(FunctionTemplateSwagger) + // @UseGuards(JwtAuthGuard) + // @Get('my-byname') + // async getMyFunctionTemplateByName( + // @Query('recent') recent: number, + // @Query('page') page: number, + // @Query('pageSize') pageSize: number, + // @Query('name') name: string, + // @Req() req: IRequest, + // ) { + // recent = recent === 0 ? Number(recent) : 1 + // page = page ? Number(page) : 1 + // pageSize = pageSize ? Number(pageSize) : 10 - const res = - await this.functionTemplateService.findMyStaredFunctionTemplates( - recent, - page, - pageSize, - req.user._id, - ) - return ResponseUtil.ok(res) - } + // const res = + // await this.functionTemplateService.findMyFunctionTemplatesByName( + // recent, + // page, + // pageSize, + // req.user._id, + // name, + // ) + // return ResponseUtil.ok(res) + // } - @ApiOperation({ summary: 'get my recent used function template' }) - @ApiResponsePagination(GetMyRecentUseFunctionTemplateSwagger) - @UseGuards(JwtAuthGuard, ApplicationAuthGuard) - @Get('my-recent') - async getMyRecentUseFunctionTemplate( - @Query('recent') recent: number, - @Query('page') page: number, - @Query('pageSize') pageSize: number, - @Req() req: IRequest, - ) { - recent = recent === 0 ? Number(recent) : 1 - page = page ? Number(page) : 1 - pageSize = pageSize ? Number(pageSize) : 10 + // @ApiOperation({ summary: 'get my star function template' }) + // @ApiResponsePagination(FunctionTemplateSwagger) + // @UseGuards(JwtAuthGuard) + // @Get('my-star') + // async getMyStaredFunctionTemplate( + // @Query('recent') recent: number, + // @Query('page') page: number, + // @Query('pageSize') pageSize: number, + // @Req() req: IRequest, + // ) { + // recent = recent === 0 ? Number(recent) : 1 + // page = page ? Number(page) : 1 + // pageSize = pageSize ? Number(pageSize) : 10 - const res = - await this.functionTemplateService.findMyRecentUseFunctionTemplates( - recent, - page, - pageSize, - req.user._id, - ) - return ResponseUtil.ok(res) - } + // const res = + // await this.functionTemplateService.findMyStaredFunctionTemplates( + // recent, + // page, + // pageSize, + // req.user._id, + // ) + // return ResponseUtil.ok(res) + // } + + // @ApiOperation({ summary: 'get my recent used function template' }) + // @ApiResponsePagination(FunctionTemplateSwagger) + // @UseGuards(JwtAuthGuard) + // @Get('my-recent') + // async getMyRecentUseFunctionTemplate( + // @Query('recent') recent: number, + // @Query('page') page: number, + // @Query('pageSize') pageSize: number, + // @Req() req: IRequest, + // ) { + // recent = recent === 0 ? Number(recent) : 1 + // page = page ? Number(page) : 1 + // pageSize = pageSize ? Number(pageSize) : 10 + + // const res = + // await this.functionTemplateService.findMyRecentUseFunctionTemplates( + // recent, + // page, + // pageSize, + // req.user._id, + // ) + // return ResponseUtil.ok(res) + // } } diff --git a/server/src/function-template/function-template.service.ts b/server/src/function-template/function-template.service.ts index 87ef7ccd94..bc4473ff10 100644 --- a/server/src/function-template/function-template.service.ts +++ b/server/src/function-template/function-template.service.ts @@ -19,6 +19,7 @@ import { compileTs2js } from '../utils/lang' import { CN_PUBLISHED_CONF, CN_PUBLISHED_FUNCTIONS } from 'src/constants' import { DatabaseService } from 'src/database/database.service' import { DependencyService } from 'src/dependency/dependency.service' +import { ApplicationService } from '../application/application.service' @Injectable() export class FunctionTemplateService { @@ -26,6 +27,7 @@ export class FunctionTemplateService { private readonly environmentVariableService: EnvironmentVariableService, private readonly databaseService: DatabaseService, private readonly dependencyService: DependencyService, + private readonly appService: ApplicationService, ) {} private readonly logger = new Logger(FunctionTemplateService.name) @@ -250,17 +252,17 @@ export class FunctionTemplateService { ) // add function template dependencies to application configuration // - const extraDenpendencies = await this.dependencyService.getExtras(appid) + const extraDependencies = await this.dependencyService.getExtras(appid) const builtinDependencies = this.dependencyService.getBuiltins() - const all = extraDenpendencies.concat(builtinDependencies) - const allnames = all.map((item) => npa(item).name) + const all = extraDependencies.concat(builtinDependencies) + const allNames = all.map((item) => npa(item).name) // If the dependency already exists, use the original dependency. const filtered = functionTemplate.dependencies.filter((item) => { const name = npa(item).name - return !allnames.includes(name) + return !allNames.includes(name) }) - const deps = extraDenpendencies.concat(filtered) + const deps = extraDependencies.concat(filtered) await this.db .collection('ApplicationConfiguration') @@ -275,11 +277,11 @@ export class FunctionTemplateService { // add function template Env to application configuration // - const orginEnv = await this.environmentVariableService.findAll(appid) + const originEnv = await this.environmentVariableService.findAll(appid) const mergedEnv = [ - ...orginEnv, + ...originEnv, ...functionTemplate.environments.filter( - (item2) => !orginEnv.some((item1) => item1.name === item2.name), + (item2) => !originEnv.some((item1) => item1.name === item2.name), ), ] @@ -302,7 +304,7 @@ export class FunctionTemplateService { // publish function template items to CloudFunction and app database // - const functionTemplateItems = await this.findFunctionTemplateitems( + const functionTemplateItems = await this.findFunctionTemplateItems( dto.functionTemplateId, ) @@ -649,6 +651,144 @@ export class FunctionTemplateService { return res } + async findFunctionTemplatesByName( + recent: number, + page: number, + pageSize: number, + name: string, + ) { + const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const reg = new RegExp(safeName, 'i') + const pipe = [ + { $match: { private: false, name: { $regex: reg } } }, + + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const total = await this.db + .collection('FunctionTemplate') + .countDocuments( + { private: false, name: { $regex: reg } }, + { maxTimeMS: 5000 }, + ) + + const functionTemplate = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .maxTimeMS(5000) + .toArray() + + const res = { + list: functionTemplate, + total: total, + page, + pageSize, + } + + return res + } + + async findMyMostStarFunctionTemplates( + starAsc: number, + page: number, + pageSize: number, + userid: ObjectId, + ) { + const pipe = [ + { $match: { private: false, uid: userid } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + { + $sort: { + star: starAsc === 0 ? 1 : -1, + }, + }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const total = await this.db + .collection('FunctionTemplate') + .countDocuments({ private: false, uid: userid }) + + const templates = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() + + const res = { + list: templates, + total: total, + page, + pageSize, + } + + return res + } + + async findMyFunctionTemplatesByName( + recent: number, + page: number, + pageSize: number, + userid: ObjectId, + name: string, + ) { + const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const reg = new RegExp(safeName, 'i') + const pipe = [ + { $match: { uid: userid, name: { $regex: reg } } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, + }, + { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const total = await this.db + .collection('FunctionTemplate') + .countDocuments( + { uid: userid, name: { $regex: reg } }, + { maxTimeMS: 5000 }, + ) + + const myTemplate = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .maxTimeMS(5000) + .toArray() + + const res = { + list: myTemplate, + total: total, + page, + pageSize, + } + + return res + } + async findMyFunctionTemplates( recent: number, page: number, @@ -694,7 +834,90 @@ export class FunctionTemplateService { page: number, pageSize: number, userid: ObjectId, + name?: string, ) { + if (name) { + const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const reg = new RegExp(safeName, 'i') + + const pipe = [ + { + $lookup: { + from: 'FunctionTemplate', + localField: 'templateId', + foreignField: '_id', + as: 'functionTemplate', + }, + }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: 'templateId', + foreignField: 'templateId', + as: 'items', + }, + }, + { $match: { uid: userid, 'functionTemplate.name': { $regex: reg } } }, + { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const pipe2 = [ + { + $lookup: { + from: 'FunctionTemplate', + localField: 'templateId', + foreignField: '_id', + as: 'functionTemplate', + }, + }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: 'templateId', + foreignField: 'templateId', + as: 'items', + }, + }, + { $match: { uid: userid, 'functionTemplate.name': { $regex: reg } } }, + { $group: { _id: null, count: { $sum: 1 } } }, + ] + + const total = await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .aggregate(pipe2) + .maxTimeMS(5000) + .toArray() + + const myStarTemplates = await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .aggregate(pipe) + .maxTimeMS(5000) + .toArray() + + const transformedData = myStarTemplates.map((element) => { + const { functionTemplate, items } = element + const [template] = functionTemplate + const result = { ...template } + result.items = items + return result + }) + + const res = { + list: transformedData, + total: total[0] ? total[0].count : 0, + page, + pageSize, + } + + return res + } + const pipe = [ { $match: { uid: userid } }, { @@ -727,8 +950,16 @@ export class FunctionTemplateService { .aggregate(pipe) .toArray() + const transformedData = myStarTemplates.map((element) => { + const { functionTemplate, items } = element + const [template] = functionTemplate + const result = { ...template } + result.items = items + return result + }) + const res = { - list: myStarTemplates, + list: transformedData, total: total, page, pageSize, @@ -750,7 +981,7 @@ export class FunctionTemplateService { from: 'FunctionTemplate', localField: 'templateId', foreignField: '_id', - as: 'functionTemplates', + as: 'functionTemplate', }, }, { @@ -775,8 +1006,16 @@ export class FunctionTemplateService { .collection('FunctionTemplateUseRelation') .countDocuments({ uid: userid }) + const transformedData = recentUseFunctionTemplates.map((element) => { + const { functionTemplate, items } = element + const [template] = functionTemplate + const result = { ...template } + result.items = items + return result + }) + const res = { - list: recentUseFunctionTemplates, + list: transformedData, total: total, page, pageSize, @@ -800,7 +1039,7 @@ export class FunctionTemplateService { return res } - async findFunctionTemplateitems(templateId: ObjectId) { + async findFunctionTemplateItems(templateId: ObjectId) { const functionTemplateItems = await this.db .collection('FunctionTemplateItem') .find({ templateId: templateId }) @@ -808,4 +1047,18 @@ export class FunctionTemplateService { return functionTemplateItems } + + async applicationAuthGuard(appid, userid) { + const app = await this.appService.findOne(appid) + if (!app) { + return false + } + + const author_id = app.createdBy?.toString() + if (author_id !== userid.toString()) { + return false + } + + return true + } } From 1a666fa8b632fd5efcff863c404800a37f178108 Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Tue, 13 Jun 2023 02:42:40 +0000 Subject: [PATCH 5/8] chore(server: add metering) --- build/charts/laf-server/templates/metering.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/charts/laf-server/templates/metering.yaml b/build/charts/laf-server/templates/metering.yaml index ee7bf8f7b0..6b55c7cd05 100644 --- a/build/charts/laf-server/templates/metering.yaml +++ b/build/charts/laf-server/templates/metering.yaml @@ -32,7 +32,7 @@ spec: memory: 64Mi env: - name: MONGO_URI - value: {{ .Values.meteringDatabaseUrl | quote }} + value: "mongodb://admin:gvm3kyvdq8kwsd04stdjpof97alzl6cw@mongodb-0.mongo.laf-system.svc.cluster.local:27017/sealos-resources?authSource=admin&replicaSet=rs0&w=majority" imagePullPolicy: Always --- apiVersion: v1 @@ -325,7 +325,7 @@ spec: - /manager env: - name: MONGO_URI - value: {{ .Values.meteringDatabaseUrl | quote }} + value: "mongodb://admin:gvm3kyvdq8kwsd04stdjpof97alzl6cw@mongodb-0.mongo.laf-system.svc.cluster.local:27017/sealos-resources?authSource=admin&replicaSet=rs0&w=majority" image: docker.io/bxy4543/laf-resources-controller:v0.0.2 imagePullPolicy: Always livenessProbe: @@ -353,4 +353,4 @@ spec: securityContext: runAsNonRoot: true serviceAccountName: resources-controller-manager - terminationGracePeriodSeconds: 10 \ No newline at end of file + terminationGracePeriodSeconds: 10 From b0a10df799dae742003ca89884166854b17ebf3e Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Thu, 15 Jun 2023 12:23:54 +0000 Subject: [PATCH 6/8] change some interface --- .../dto/create-function-template.dto.ts | 8 +- .../dto/star-function-template.dto.ts | 12 - .../dto/update-function-template.dto.ts | 16 +- .../dto/use-function-template.dto.ts | 4 +- .../entities/function-template.ts | 9 + .../entities/swagger-help.ts | 8 +- .../function-template.controller.ts | 497 ++++++------- .../function-template.module.ts | 2 - .../function-template.service.ts | 652 +++++++++++++----- 9 files changed, 718 insertions(+), 490 deletions(-) delete mode 100644 server/src/function-template/dto/star-function-template.dto.ts diff --git a/server/src/function-template/dto/create-function-template.dto.ts b/server/src/function-template/dto/create-function-template.dto.ts index fec8ab7bdc..c27fdaaa8e 100644 --- a/server/src/function-template/dto/create-function-template.dto.ts +++ b/server/src/function-template/dto/create-function-template.dto.ts @@ -1,14 +1,10 @@ -import { ObjectId } from 'mongodb' import { CreateEnvironmentDto } from '../../application/dto/create-env.dto' import { CreateDependencyDto } from 'src/dependency/dto/create-dependency.dto' -import { CloudFunctionSource } from 'src/function/entities/cloud-function' import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' import { - IsArray, IsBoolean, IsIn, IsNotEmpty, - IsOptional, IsString, Matches, MaxLength, @@ -19,7 +15,7 @@ import { HTTP_METHODS } from '../../constants' import { HttpMethod } from '../../function/entities/cloud-function' export class FunctionTemplateItemDto { @ApiProperty({ - description: 'FunctionTemplate function name', + description: 'FunctionTemplate item name', }) @IsNotEmpty() @Matches(/^[a-zA-Z0-9_.\-\/]{1,256}$/) @@ -48,6 +44,7 @@ export class CreateFunctionTemplateDto { @ApiProperty({ description: 'function template name' }) @IsNotEmpty() @IsString() + @MaxLength(64) name: string @ApiProperty({ description: 'Dependencies', type: [CreateDependencyDto] }) @@ -67,6 +64,7 @@ export class CreateFunctionTemplateDto { @ApiPropertyOptional({ description: 'function template description' }) @IsString() + @MaxLength(256) description?: string @ApiProperty({ diff --git a/server/src/function-template/dto/star-function-template.dto.ts b/server/src/function-template/dto/star-function-template.dto.ts deleted file mode 100644 index f9995dab98..0000000000 --- a/server/src/function-template/dto/star-function-template.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger' -import { IsNotEmpty } from 'class-validator' -import { ObjectId } from 'mongodb' - -export class StarFunctionTemplateDto { - @ApiProperty({ - description: 'The ObjectId of function template', - type: 'string', - }) - @IsNotEmpty() - functionTemplateId: ObjectId -} diff --git a/server/src/function-template/dto/update-function-template.dto.ts b/server/src/function-template/dto/update-function-template.dto.ts index 972ebfeb18..6a96d750b1 100644 --- a/server/src/function-template/dto/update-function-template.dto.ts +++ b/server/src/function-template/dto/update-function-template.dto.ts @@ -1,30 +1,27 @@ import { ObjectId } from 'mongodb' import { CreateEnvironmentDto } from '../../application/dto/create-env.dto' import { CreateDependencyDto } from 'src/dependency/dto/create-dependency.dto' -import { CloudFunctionSource } from 'src/function/entities/cloud-function' import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' import { - IsArray, IsBoolean, - IsIn, IsNotEmpty, - IsOptional, IsString, - Matches, + Length, MaxLength, ValidateNested, } from 'class-validator' import { Type } from 'class-transformer' -import { HTTP_METHODS } from '../../constants' -import { HttpMethod } from '../../function/entities/cloud-function' import { FunctionTemplateItemDto } from './create-function-template.dto' export class UpdateFunctionTemplateDto { @ApiProperty({ description: 'Function template id' }) + @IsNotEmpty() + @Length(24, 24) functionTemplateId: ObjectId @ApiProperty({ description: 'Template name' }) @IsNotEmpty() + @MaxLength(64) @IsString() name: string @@ -43,12 +40,13 @@ export class UpdateFunctionTemplateDto { @IsBoolean() private: boolean - @ApiPropertyOptional({ description: 'Template description' }) + @ApiPropertyOptional({ description: 'function template description' }) @IsString() + @MaxLength(256) description?: string @ApiPropertyOptional({ - description: 'Template items', + description: 'items of the function template', type: [FunctionTemplateItemDto], }) @IsNotEmpty() diff --git a/server/src/function-template/dto/use-function-template.dto.ts b/server/src/function-template/dto/use-function-template.dto.ts index 58574e49d7..a7bf936cdc 100644 --- a/server/src/function-template/dto/use-function-template.dto.ts +++ b/server/src/function-template/dto/use-function-template.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger' -import { IsNotEmpty } from 'class-validator' +import { IsNotEmpty, Length } from 'class-validator' import { ObjectId } from 'mongodb' export class UseFunctionTemplateDto { @@ -8,8 +8,10 @@ export class UseFunctionTemplateDto { type: 'string', }) @IsNotEmpty() + @Length(24, 24) functionTemplateId: ObjectId + @IsNotEmpty() @ApiProperty() appid: string } diff --git a/server/src/function-template/entities/function-template.ts b/server/src/function-template/entities/function-template.ts index 6d96f82a7b..235944639b 100644 --- a/server/src/function-template/entities/function-template.ts +++ b/server/src/function-template/entities/function-template.ts @@ -6,6 +6,11 @@ import { import { EnvironmentVariable } from 'src/application/entities/application-configuration' import { ObjectId } from 'mongodb' +export enum RelationState { + Enabled = 'Active', + Disabled = 'Inactive', +} + export class FunctionTemplate { @ApiProperty({ type: String }) _id?: ObjectId @@ -82,6 +87,8 @@ export class FunctionTemplateStarRelation { createdAt: Date @ApiProperty() updatedAt: Date + @ApiProperty({ type: String }) + state?: RelationState } export class FunctionTemplateUseRelation { @@ -95,4 +102,6 @@ export class FunctionTemplateUseRelation { createdAt: Date @ApiProperty() updatedAt: Date + @ApiProperty({ type: String }) + state?: RelationState } diff --git a/server/src/function-template/entities/swagger-help.ts b/server/src/function-template/entities/swagger-help.ts index fcf36a3905..01e3401121 100644 --- a/server/src/function-template/entities/swagger-help.ts +++ b/server/src/function-template/entities/swagger-help.ts @@ -1,13 +1,9 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' -import { - CloudFunction, - CloudFunctionSource, - HttpMethod, -} from 'src/function/entities/cloud-function' +import { HttpMethod } from 'src/function/entities/cloud-function' import { EnvironmentVariable } from 'src/application/entities/application-configuration' import { ObjectId } from 'mongodb' import { User } from 'src/user/entities/user' -import { FunctionTemplate, FunctionTemplateItem } from './function-template' +import { FunctionTemplate } from './function-template' class FunctionTemplateItemSourceSwagger { @ApiProperty({ description: 'The source code of the function' }) diff --git a/server/src/function-template/function-template.controller.ts b/server/src/function-template/function-template.controller.ts index 80dbe3e0f0..42ec268f1e 100644 --- a/server/src/function-template/function-template.controller.ts +++ b/server/src/function-template/function-template.controller.ts @@ -9,11 +9,10 @@ import { Req, Query, Param, + Put, } from '@nestjs/common' import { FunctionTemplateService } from './function-template.service' import { CreateFunctionTemplateDto } from './dto/create-function-template.dto' -import { StarFunctionTemplateDto } from './dto/star-function-template.dto' -import { UseFunctionTemplateDto } from './dto/use-function-template.dto' import { UpdateFunctionTemplateDto } from './dto/update-function-template.dto' import * as assert from 'node:assert' import { IRequest } from '../utils/interface' @@ -32,13 +31,11 @@ import { DependencyService } from 'src/dependency/dependency.service' import { FunctionTemplateSwagger, GetFunctionTemplateUsedByItemSwagger, - GetMyStaredFunctionTemplateSwagger, - GetMyRecentUseFunctionTemplateSwagger, } from './entities/swagger-help' @ApiTags('FunctionTemplate') @ApiBearerAuth('Authorization') -@Controller('function-template') +@Controller('function-templates') export class FunctionTemplateController { constructor( private readonly functionsService: FunctionService, @@ -55,7 +52,7 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'create a function template' }) @ApiResponseArray(FunctionTemplateSwagger) @UseGuards(JwtAuthGuard) - @Post('create') + @Post() async createFunctionTemplate( @Body() dto: CreateFunctionTemplateDto, @Req() req: IRequest, @@ -77,16 +74,18 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'use a function template' }) @ApiResponseArray(FunctionTemplateSwagger) @UseGuards(JwtAuthGuard) - @Post('use') + @Post(':templateId/:appid') async useFunctionTemplate( - @Body() dto: UseFunctionTemplateDto, + @Param('templateId') templateId: string, + @Param('appid') appid: string, @Req() req: IRequest, ) { - dto.functionTemplateId = new ObjectId(dto.functionTemplateId) - + if (templateId.length != 24) { + return ResponseUtil.error('invalid templateId') + } // Check if appid is a valid resource const valid = await this.functionTemplateService.applicationAuthGuard( - dto.appid, + appid, req.user._id, ) @@ -96,7 +95,7 @@ export class FunctionTemplateController { // check if the function template is exist const found = await this.functionTemplateService.findOneFunctionTemplate( - dto.functionTemplateId, + new ObjectId(templateId), ) if (!found) { return ResponseUtil.error('function template not found') @@ -105,35 +104,24 @@ export class FunctionTemplateController { // function check const functionTemplateItems = await this.functionTemplateService.findFunctionTemplateItems( - dto.functionTemplateId, + new ObjectId(templateId), ) if (functionTemplateItems.length === 0 || !functionTemplateItems) { return ResponseUtil.error('function template items not found') } - for (const functionTemplateItem of functionTemplateItems) { - // check name is unique - const found = await this.functionsService.findOne( - dto.appid, - functionTemplateItem.name, - ) - if (found) { - return ResponseUtil.error('function name is not unique') - } - // check if meet the count limit - const bundle = await this.bundleService.findOne(dto.appid) - const MAX_FUNCTION_COUNT = - bundle?.resource?.limitCountOfCloudFunction || 0 - const count = await this.functionsService.count(dto.appid) - if (count >= MAX_FUNCTION_COUNT) { - return ResponseUtil.error('exceed the count limit') - } + // check if meet the count limit + const bundle = await this.bundleService.findOne(appid) + const MAX_FUNCTION_COUNT = bundle?.resource?.limitCountOfCloudFunction || 0 + const count = await this.functionsService.count(appid) + if (count >= MAX_FUNCTION_COUNT) { + return ResponseUtil.error('exceed the count limit') } const res = await this.functionTemplateService.useFunctionTemplate( req.user._id, - dto.appid, - dto, + appid, + new ObjectId(templateId), ) return ResponseUtil.ok(res) @@ -142,16 +130,19 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'update a function template' }) @ApiResponseArray(FunctionTemplateSwagger) @UseGuards(JwtAuthGuard) - @Patch('update') + @Patch('update/:id') async updateFunctionTemplate( + @Param('id') templateId: string, @Body() dto: UpdateFunctionTemplateDto, @Req() req: IRequest, ) { - dto.functionTemplateId = new ObjectId(dto.functionTemplateId) + if (templateId.length != 24) { + return ResponseUtil.error('invalid templateId') + } // check if the function template is exist const found = await this.functionTemplateService.findOneFunctionTemplate( - dto.functionTemplateId, + new ObjectId(templateId), req.user._id, ) if (!found) { @@ -160,7 +151,7 @@ export class FunctionTemplateController { const res = await this.functionTemplateService.updateFunctionTemplate( req.user._id, - dto.functionTemplateId, + new ObjectId(templateId), dto, ) @@ -175,6 +166,10 @@ export class FunctionTemplateController { @Param('id') templateId: string, @Req() req: IRequest, ) { + if (templateId.length != 24) { + return ResponseUtil.error('invalid templateId') + } + const found = await this.functionTemplateService.findOneFunctionTemplate( new ObjectId(templateId), req.user._id, @@ -192,20 +187,22 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'star a function template' }) @ApiResponseString() @UseGuards(JwtAuthGuard) - @Post('star') + @Put(':templateId/star') async starFunctionTemplate( - @Body() dto: StarFunctionTemplateDto, + @Param('templateId') templateId: string, @Req() req: IRequest, ) { - dto.functionTemplateId = new ObjectId(dto.functionTemplateId) + if (templateId.length != 24) { + return ResponseUtil.error('invalid templateId') + } const found = await this.functionTemplateService.findOneFunctionTemplate( - dto.functionTemplateId, + new ObjectId(templateId), ) if (!found) { return ResponseUtil.error('function template not found') } const res = await this.functionTemplateService.starFunctionTemplate( - dto.functionTemplateId, + new ObjectId(templateId), req.user._id, ) return ResponseUtil.ok(res) @@ -214,11 +211,15 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'get function template user star state' }) @ApiResponseString() @UseGuards(JwtAuthGuard) - @Get('star/:id') + @Get(':id/star-state') async getUserFunctionTemplateStarState( @Param('id') templateId: string, @Req() req: IRequest, ) { + if (templateId.length != 24) { + return ResponseUtil.error('invalid templateId') + } + const res = await this.functionTemplateService.functionTemplateUserStarState( new ObjectId(templateId), @@ -230,13 +231,17 @@ export class FunctionTemplateController { @ApiOperation({ summary: 'get people who use this function template' }) @ApiResponsePagination(GetFunctionTemplateUsedByItemSwagger) @UseGuards(JwtAuthGuard) - @Get('used-by/:id') + @Get(':id/used-by') async getFunctionTemplateUsedBy( @Param('id') templateId: string, @Query('recent') recent: number, @Query('page') page: number, @Query('pageSize') pageSize: number, ) { + if (templateId.length != 24) { + return ResponseUtil.error('invalid templateId') + } + recent = recent === 0 ? Number(recent) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 @@ -255,46 +260,22 @@ export class FunctionTemplateController { return ResponseUtil.ok(res) } - @ApiOperation({ summary: 'get one function template' }) - @ApiResponseArray(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard) - @Get('one/:id') - async getOneFunctionTemplate( - @Param('id') templateId: string, - @Req() req: IRequest, - ) { - const template = await this.functionTemplateService.findOneFunctionTemplate( - new ObjectId(templateId), - ) - - try { - assert( - template.private === false || - template.uid.toString() === req.user._id.toString(), - 'private function template can only be inspect by the owner', - ) - } catch (error) { - return ResponseUtil.error(error.message) - } - - const res = await this.functionTemplateService.findOne( - new ObjectId(templateId), - ) - - return ResponseUtil.ok(res) - } - - @ApiOperation({ summary: 'get all function template' }) + @ApiOperation({ summary: 'get my template function' }) @ApiResponsePagination(FunctionTemplateSwagger) @UseGuards(JwtAuthGuard) - @Get('all') - async getAllFunctionTemplate( + @Get('my') + async getMyFunctionTemplate( @Query('recent') recent: number, @Query('page') page: number, @Query('pageSize') pageSize: number, @Query('name') name: string, - @Query('starAsc') starAsc: number, + @Query('starName') starName: string, + @Query('stared') stared: boolean, + @Query('recentName') recentName: string, + @Query('recentUsed') recentUsed: boolean, @Query('hot') hot: boolean, + @Query('starAsc') starAsc: number, + @Req() req: IRequest, ) { if (name) { recent = recent === 0 ? Number(recent) : 1 @@ -302,168 +283,109 @@ export class FunctionTemplateController { pageSize = pageSize ? Number(pageSize) : 10 const res = - await this.functionTemplateService.findFunctionTemplatesByName( + await this.functionTemplateService.findMyFunctionTemplatesByName( recent, page, pageSize, + req.user._id, name, ) return ResponseUtil.ok(res) } - if (hot) { - starAsc = starAsc === 0 ? Number(starAsc) : 1 + + if (starName) { + recent = recent === 0 ? Number(recent) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 + const condition = { + recent, + page, + pageSize, + name: starName, + } const res = - await this.functionTemplateService.findMostStarFunctionTemplates( - starAsc, - page, - pageSize, + await this.functionTemplateService.findMyStaredFunctionTemplates( + req.user._id, + condition, ) return ResponseUtil.ok(res) } - recent = recent === 0 ? Number(recent) : 1 - page = page ? Number(page) : 1 - pageSize = pageSize ? Number(pageSize) : 10 - - const res = await this.functionTemplateService.findFunctionTemplates( - recent, - page, - pageSize, - ) - - return ResponseUtil.ok(res) - } - // @ApiOperation({ summary: 'get all function template' }) - // @ApiResponsePagination(FunctionTemplateSwagger) - // @UseGuards(JwtAuthGuard) - // @Get('all-byname') - // async getAllFunctionTemplateByName( - // @Query('recent') recent: number, - // @Query('page') page: number, - // @Query('pageSize') pageSize: number, - // @Query('name') name: string, - // ) { - // recent = recent === 0 ? Number(recent) : 1 - // page = page ? Number(page) : 1 - // pageSize = pageSize ? Number(pageSize) : 10 - - // const res = await this.functionTemplateService.findFunctionTemplatesByName( - // recent, - // page, - // pageSize, - // name, - // ) - // return ResponseUtil.ok(res) - // } - - // @ApiOperation({ summary: 'get most star function template' }) - // @ApiResponsePagination(FunctionTemplateSwagger) - // @UseGuards(JwtAuthGuard) - // @Get('hot') - // async getHotFunctionTemplate( - // @Query('starAsc') starAsc: number, - // @Query('page') page: number, - // @Query('pageSize') pageSize: number, - // ) { - // starAsc = starAsc === 0 ? Number(starAsc) : 1 - // page = page ? Number(page) : 1 - // pageSize = pageSize ? Number(pageSize) : 10 - - // const res = - // await this.functionTemplateService.findMostStarFunctionTemplates( - // starAsc, - // page, - // pageSize, - // ) - // return ResponseUtil.ok(res) - // } - - // @ApiOperation({ summary: 'get my most star function template' }) - // @ApiResponsePagination(FunctionTemplateSwagger) - // @UseGuards(JwtAuthGuard) - // @Get('my-hot') - // async getMyHotFunctionTemplate( - // @Query('starAsc') starAsc: number, - // @Query('page') page: number, - // @Query('pageSize') pageSize: number, - // @Req() req: IRequest, - // ) { - // starAsc = starAsc === 0 ? Number(starAsc) : 1 - // page = page ? Number(page) : 1 - // pageSize = pageSize ? Number(pageSize) : 10 - - // const res = - // await this.functionTemplateService.findMyMostStarFunctionTemplates( - // starAsc, - // page, - // pageSize, - // req.user._id, - // ) - // return ResponseUtil.ok(res) - // } + if (stared && hot) { + starAsc = starAsc === 0 ? Number(starAsc) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + recent = recent === 0 ? Number(recent) : 1 + const condition = { + recent, + page, + pageSize, + hot, + starAsc, + } + const res = + await this.functionTemplateService.findMyStaredFunctionTemplates( + req.user._id, + condition, + ) + return ResponseUtil.ok(res) + } - @ApiOperation({ summary: 'get my template function' }) - @ApiResponsePagination(FunctionTemplateSwagger) - @UseGuards(JwtAuthGuard) - @Get('my') - async getMyFunctionTemplate( - @Query('recent') recent: number, - @Query('page') page: number, - @Query('pageSize') pageSize: number, - @Query('name') name: string, - @Query('starName') starName: string, - @Query('stared') stared: boolean, - @Query('recentUsed') recentUsed: boolean, - @Query('hot') hot: boolean, - @Query('starAsc') starAsc: number, - @Req() req: IRequest, - ) { - if (name) { + if (stared) { recent = recent === 0 ? Number(recent) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 + const condition = { + recent, + page, + pageSize, + } const res = - await this.functionTemplateService.findMyFunctionTemplatesByName( - recent, - page, - pageSize, + await this.functionTemplateService.findMyStaredFunctionTemplates( req.user._id, - name, + condition, ) return ResponseUtil.ok(res) } - if (starName) { + if (recentName) { recent = recent === 0 ? Number(recent) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 + const condition = { + recent, + page, + pageSize, + name: recentName, + } const res = - await this.functionTemplateService.findMyStaredFunctionTemplates( - recent, - page, - pageSize, + await this.functionTemplateService.findMyRecentUseFunctionTemplates( req.user._id, - starName, + condition, ) return ResponseUtil.ok(res) } - if (stared) { + if (recentUsed && hot) { + starAsc = starAsc === 0 ? Number(starAsc) : 1 recent = recent === 0 ? Number(recent) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 + const condition = { + recent, + page, + pageSize, + hot, + starAsc, + } const res = - await this.functionTemplateService.findMyStaredFunctionTemplates( - recent, - page, - pageSize, + await this.functionTemplateService.findMyRecentUseFunctionTemplates( req.user._id, + condition, ) return ResponseUtil.ok(res) } @@ -473,115 +395,134 @@ export class FunctionTemplateController { page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 + const condition = { + recent, + page, + pageSize, + } const res = await this.functionTemplateService.findMyRecentUseFunctionTemplates( - recent, - page, - pageSize, req.user._id, + condition, ) return ResponseUtil.ok(res) } if (hot) { starAsc = starAsc === 0 ? Number(starAsc) : 1 + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = await this.functionTemplateService.findMyFunctionTemplates( + recent, + page, + pageSize, + req.user._id, + hot, + starAsc, + ) + return ResponseUtil.ok(res) + } + + recent = recent === 0 ? Number(recent) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = await this.functionTemplateService.findMyFunctionTemplates( + recent, + page, + pageSize, + req.user._id, + ) + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get one function template' }) + @ApiResponseArray(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard) + @Get(':id') + async getOneFunctionTemplate( + @Param('id') templateId: string, + @Req() req: IRequest, + ) { + if (templateId.length !== 24) { + return ResponseUtil.error('function template id is invalid') + } + + const template = await this.functionTemplateService.findOneFunctionTemplate( + new ObjectId(templateId), + ) + + try { + assert( + template.private === false || + template.uid.toString() === req.user._id.toString(), + 'private function template can only be inspect by the owner', + ) + } catch (error) { + return ResponseUtil.error(error.message) + } + + const res = await this.functionTemplateService.findOne( + new ObjectId(templateId), + ) + + return ResponseUtil.ok(res) + } + + @ApiOperation({ summary: 'get all function template' }) + @ApiResponsePagination(FunctionTemplateSwagger) + @UseGuards(JwtAuthGuard) + @Get() + async getAllFunctionTemplate( + @Query('recent') recent: number, + @Query('page') page: number, + @Query('pageSize') pageSize: number, + @Query('name') name: string, + @Query('starAsc') starAsc: number, + @Query('hot') hot: boolean, + ) { + if (name) { + recent = recent === 0 ? Number(recent) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const res = - await this.functionTemplateService.findMyMostStarFunctionTemplates( - starAsc, + await this.functionTemplateService.findFunctionTemplatesByName( + recent, page, pageSize, - req.user._id, + name, ) return ResponseUtil.ok(res) } + if (hot) { + starAsc = starAsc === 0 ? Number(starAsc) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = await this.functionTemplateService.findFunctionTemplates( + starAsc, + page, + pageSize, + hot, + starAsc, + ) + return ResponseUtil.ok(res) + } + recent = recent === 0 ? Number(recent) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 - const res = await this.functionTemplateService.findMyFunctionTemplates( + const res = await this.functionTemplateService.findFunctionTemplates( recent, page, pageSize, - req.user._id, ) + return ResponseUtil.ok(res) } - - // @ApiOperation({ summary: 'get my template function by name' }) - // @ApiResponsePagination(FunctionTemplateSwagger) - // @UseGuards(JwtAuthGuard) - // @Get('my-byname') - // async getMyFunctionTemplateByName( - // @Query('recent') recent: number, - // @Query('page') page: number, - // @Query('pageSize') pageSize: number, - // @Query('name') name: string, - // @Req() req: IRequest, - // ) { - // recent = recent === 0 ? Number(recent) : 1 - // page = page ? Number(page) : 1 - // pageSize = pageSize ? Number(pageSize) : 10 - - // const res = - // await this.functionTemplateService.findMyFunctionTemplatesByName( - // recent, - // page, - // pageSize, - // req.user._id, - // name, - // ) - // return ResponseUtil.ok(res) - // } - - // @ApiOperation({ summary: 'get my star function template' }) - // @ApiResponsePagination(FunctionTemplateSwagger) - // @UseGuards(JwtAuthGuard) - // @Get('my-star') - // async getMyStaredFunctionTemplate( - // @Query('recent') recent: number, - // @Query('page') page: number, - // @Query('pageSize') pageSize: number, - // @Req() req: IRequest, - // ) { - // recent = recent === 0 ? Number(recent) : 1 - // page = page ? Number(page) : 1 - // pageSize = pageSize ? Number(pageSize) : 10 - - // const res = - // await this.functionTemplateService.findMyStaredFunctionTemplates( - // recent, - // page, - // pageSize, - // req.user._id, - // ) - // return ResponseUtil.ok(res) - // } - - // @ApiOperation({ summary: 'get my recent used function template' }) - // @ApiResponsePagination(FunctionTemplateSwagger) - // @UseGuards(JwtAuthGuard) - // @Get('my-recent') - // async getMyRecentUseFunctionTemplate( - // @Query('recent') recent: number, - // @Query('page') page: number, - // @Query('pageSize') pageSize: number, - // @Req() req: IRequest, - // ) { - // recent = recent === 0 ? Number(recent) : 1 - // page = page ? Number(page) : 1 - // pageSize = pageSize ? Number(pageSize) : 10 - - // const res = - // await this.functionTemplateService.findMyRecentUseFunctionTemplates( - // recent, - // page, - // pageSize, - // req.user._id, - // ) - // return ResponseUtil.ok(res) - // } } diff --git a/server/src/function-template/function-template.module.ts b/server/src/function-template/function-template.module.ts index 7a7205657c..6bfaa15494 100644 --- a/server/src/function-template/function-template.module.ts +++ b/server/src/function-template/function-template.module.ts @@ -1,7 +1,6 @@ import { Module } from '@nestjs/common' import { FunctionTemplateService } from './function-template.service' import { FunctionTemplateController } from './function-template.controller' -// import { EnvironmentVariableService } from '../application/environment.service' import { ApplicationModule } from 'src/application/application.module' import { DatabaseModule } from 'src/database/database.module' import { FunctionModule } from 'src/function/function.module' @@ -15,7 +14,6 @@ import { DependencyModule } from '../dependency/dependency.module' DependencyModule, ], controllers: [FunctionTemplateController], - // providers: [FunctionTemplateService, EnvironmentVariableService], providers: [FunctionTemplateService], }) export class FunctionTemplateModule {} diff --git a/server/src/function-template/function-template.service.ts b/server/src/function-template/function-template.service.ts index bc4473ff10..477f286365 100644 --- a/server/src/function-template/function-template.service.ts +++ b/server/src/function-template/function-template.service.ts @@ -1,6 +1,5 @@ import { Injectable, Logger } from '@nestjs/common' import { CreateFunctionTemplateDto } from './dto/create-function-template.dto' -import { UseFunctionTemplateDto } from './dto/use-function-template.dto' import { UpdateFunctionTemplateDto } from './dto/update-function-template.dto' import { SystemDatabase } from 'src/system-database' import { @@ -8,6 +7,7 @@ import { FunctionTemplateItem, FunctionTemplateUseRelation, FunctionTemplateStarRelation, + RelationState, } from './entities/function-template' import { ObjectId } from 'mongodb' import { EnvironmentVariableService } from 'src/application/environment.service' @@ -21,6 +21,15 @@ import { DatabaseService } from 'src/database/database.service' import { DependencyService } from 'src/dependency/dependency.service' import { ApplicationService } from '../application/application.service' +interface FindFunctionTemplatesParams { + recent: number + page: number + pageSize: number + name?: string + hot?: boolean + starAsc?: number +} + @Injectable() export class FunctionTemplateService { constructor( @@ -125,40 +134,53 @@ export class FunctionTemplateService { .collection( 'FunctionTemplateStarRelation', ) - .deleteMany( + .updateMany( { templateId: templateId, uid: { $ne: userid } }, + { + $set: { state: RelationState.Disabled }, + $currentDate: { updatedAt: true }, + }, { session }, ) await this.db .collection( 'FunctionTemplateUseRelation', ) - .deleteMany( + .updateMany( { templateId: templateId, uid: { $ne: userid } }, + { + $set: { state: RelationState.Disabled }, + $currentDate: { updatedAt: true }, + }, { session }, ) - const star = await this.db + } + + if (found.private === true && dto.private === false) { + await this.db .collection( 'FunctionTemplateStarRelation', ) - .findOne({ uid: userid, templateId: templateId }, { session }) - if (star) { - await this.db - .collection('FunctionTemplate') - .updateOne( - { _id: templateId, uid: userid }, - { $set: { star: 1 } }, - { session }, - ) - } else { - await this.db - .collection('FunctionTemplate') - .updateOne( - { _id: templateId, uid: userid }, - { $set: { star: 0 } }, - { session }, - ) - } + .updateMany( + { templateId: templateId, uid: { $ne: userid } }, + { + $set: { state: RelationState.Enabled }, + $currentDate: { updatedAt: true }, + }, + { session }, + ) + await this.db + .collection( + 'FunctionTemplateUseRelation', + ) + .updateMany( + { templateId: templateId, uid: { $ne: userid } }, + { + $set: { state: RelationState.Enabled }, + $currentDate: { updatedAt: true }, + }, + { session }, + ) } const res = await this.db @@ -183,6 +205,7 @@ export class FunctionTemplateService { returnDocument: 'after', }, ) + if (res.lastErrorObject.updatedExisting) { const functionTemplateItems = dto.items.map((item) => ({ templateId: res.value._id, @@ -200,6 +223,7 @@ export class FunctionTemplateService { .collection('FunctionTemplateItem') .insertMany(functionTemplateItems, { session }) } + await session.commitTransaction() const pipe = [ @@ -213,6 +237,7 @@ export class FunctionTemplateService { }, }, ] + const template = await this.db .collection('FunctionTemplate') .aggregate(pipe) @@ -227,11 +252,11 @@ export class FunctionTemplateService { } } - // use function template + // use a function template async useFunctionTemplate( userid: ObjectId, appid: string, - dto: UseFunctionTemplateDto, + templateId: ObjectId, ) { const client = SystemDatabase.client const appDataBaseInfo = await this.databaseService.findOne(appid) @@ -242,14 +267,14 @@ export class FunctionTemplateService { try { session.startTransaction() - const functionTemplate = await this.findOneFunctionTemplate( - dto.functionTemplateId, - ) + const functionTemplate = await this.findOneFunctionTemplate(templateId) + assert( functionTemplate.private === false || functionTemplate.uid.toString() === userid.toString(), 'private function template can only be used by the owner', ) + // add function template dependencies to application configuration // const extraDependencies = await this.dependencyService.getExtras(appid) @@ -305,49 +330,84 @@ export class FunctionTemplateService { // publish function template items to CloudFunction and app database // const functionTemplateItems = await this.findFunctionTemplateItems( - dto.functionTemplateId, + templateId, + ) + + const existingNames = new Set( + await this.db + .collection('CloudFunction') + .aggregate([ + { $match: { appid: appid } }, + { $group: { _id: null, names: { $addToSet: '$name' } } }, + ]) + .toArray() + .then((result) => (result.length > 0 ? result[0].names : [])), ) + const documents = [] + for (const functionTemplateItem of functionTemplateItems) { - await this.db.collection('CloudFunction').insertOne( - { - appid, - name: functionTemplateItem.name, - source: { - code: functionTemplateItem.source.code, - compiled: compileTs2js(functionTemplateItem.source.code), - version: 0, - }, - desc: functionTemplateItem.desc || '', - createdBy: userid, - methods: functionTemplateItem.methods, - tags: [], - createdAt: new Date(), - updatedAt: new Date(), + // Check name is unique + while (existingNames.has(functionTemplateItem.name)) { + functionTemplateItem.name = `${functionTemplateItem.name}-copy` + } + + // Add the unique name to the set + existingNames.add(functionTemplateItem.name) + + const document = { + appid, + name: functionTemplateItem.name, + source: { + code: functionTemplateItem.source.code, + compiled: compileTs2js(functionTemplateItem.source.code), + version: 0, }, - { session }, - ) + desc: functionTemplateItem.desc || '', + createdBy: userid, + methods: functionTemplateItem.methods, + tags: [], + createdAt: new Date(), + updatedAt: new Date(), + } + + documents.push(document) } - // add function template items to app database - for (const functionTemplateItem of functionTemplateItems) { - const fn = await this.db - .collection('CloudFunction') - .findOne( - { appid: appid, name: functionTemplateItem.name }, - { session }, - ) + await this.db + .collection('CloudFunction') + .insertMany(documents, { session }) - const coll = appDataBase.collection(CN_PUBLISHED_FUNCTIONS) - await coll.deleteOne({ name: fn.name }, { session }) - await coll.insertOne(fn, { session }) - } + // Prepare the names to be searched and documents to be inserted + const namesToSearch = functionTemplateItems.map((item) => item.name) + let documentsToInsert + + // add function template items to app database + // Find all documents with matching names + await this.db + .collection('CloudFunction') + .find({ appid: appid, name: { $in: namesToSearch } }, { session }) + .toArray() + .then((docs) => { + documentsToInsert = docs + }) + + // Get the collection + const collectionFunction = appDataBase.collection(CN_PUBLISHED_FUNCTIONS) + // Delete existing documents with the matching names + await collectionFunction.deleteMany( + { name: { $in: namesToSearch } }, + { session }, + ) + // Insert all new documents + await collectionFunction.insertMany(documentsToInsert, { session }) - // user use relation + // add user use relation + // const res = await this.db .collection('FunctionTemplateUseRelation') .findOneAndUpdate( - { uid: userid, templateId: dto.functionTemplateId }, + { uid: userid, templateId: templateId }, { $currentDate: { updatedAt: true }, }, @@ -361,7 +421,8 @@ export class FunctionTemplateService { .insertOne( { uid: userid, - templateId: dto.functionTemplateId, + templateId: templateId, + state: RelationState.Enabled, createdAt: new Date(), updatedAt: new Date(), }, @@ -372,7 +433,7 @@ export class FunctionTemplateService { await session.commitTransaction() const pipe = [ - { $match: { _id: dto.functionTemplateId } }, + { $match: { _id: templateId } }, { $lookup: { from: 'FunctionTemplateItem', @@ -422,10 +483,22 @@ export class FunctionTemplateService { await this.db .collection('FunctionTemplate') .deleteOne({ _id: templateId, uid: userid }, { session }) + await this.db .collection('FunctionTemplateItem') .deleteMany({ templateId: templateId }, { session }) + // remove use relation and star relation + await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .deleteMany({ templateId: templateId }, { session }) + + await this.db + .collection('FunctionTemplateUseRelation') + .deleteMany({ templateId: templateId }, { session }) + await session.commitTransaction() return deletedFunctionTemplate @@ -441,6 +514,7 @@ export class FunctionTemplateService { const session = client.startSession() try { session.startTransaction() + const functionTemplate = await this.findOneFunctionTemplate(templateId) assert( functionTemplate.private === false || @@ -457,27 +531,21 @@ export class FunctionTemplateService { if (found) { await this.db .collection('FunctionTemplate') - .updateOne( - { _id: templateId }, - { $inc: { star: -1 }, $currentDate: { updatedAt: true } }, - { session }, - ) + .updateOne({ _id: templateId }, { $inc: { star: -1 } }, { session }) + await this.db .collection( 'FunctionTemplateStarRelation', ) .deleteOne({ uid: userid, templateId: templateId }, { session }) + await session.commitTransaction() return 'unstar' } await this.db .collection('FunctionTemplate') - .updateOne( - { _id: templateId }, - { $inc: { star: 1 }, $currentDate: { updatedAt: true } }, - { session }, - ) + .updateOne({ _id: templateId }, { $inc: { star: 1 } }, { session }) await this.db .collection( @@ -487,6 +555,7 @@ export class FunctionTemplateService { { uid: userid, templateId: templateId, + state: RelationState.Enabled, createdAt: new Date(), updatedAt: new Date(), }, @@ -565,6 +634,7 @@ export class FunctionTemplateService { }, }, ] + const functionTemplate = await this.db .collection('FunctionTemplate') .aggregate(pipe) @@ -573,46 +643,53 @@ export class FunctionTemplateService { return functionTemplate } - async findFunctionTemplates(recent: number, page: number, pageSize: number) { - const pipe = [ - { $match: { private: false } }, - { - $lookup: { - from: 'FunctionTemplateItem', - localField: '_id', - foreignField: 'templateId', - as: 'items', + // get all public function templates + async findFunctionTemplates( + recent: number, + page: number, + pageSize: number, + hot?: boolean, + starAsc?: number, + ) { + if (hot) { + const pipe = [ + { $match: { private: false } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, }, - }, - { $sort: { createdAt: recent === 0 ? 1 : -1 } }, - { $skip: (page - 1) * pageSize }, - { $limit: pageSize }, - ] + { + $sort: { + star: starAsc === 0 ? 1 : -1, + }, + }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] - const total = await this.db - .collection('FunctionTemplate') - .countDocuments({ private: false }) + const total = await this.db + .collection('FunctionTemplate') + .countDocuments({ private: false }) - const functionTemplate = await this.db - .collection('FunctionTemplate') - .aggregate(pipe) - .toArray() + const functionTemplate = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() - const res = { - list: functionTemplate, - total: total, - page, - pageSize, - } + const res = { + list: functionTemplate, + total: total, + page, + pageSize, + } - return res - } + return res + } - async findMostStarFunctionTemplates( - starAsc: number, - page: number, - pageSize: number, - ) { const pipe = [ { $match: { private: false } }, { @@ -623,11 +700,7 @@ export class FunctionTemplateService { as: 'items', }, }, - { - $sort: { - star: starAsc === 0 ? 1 : -1, - }, - }, + { $sort: { createdAt: recent === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] @@ -636,13 +709,13 @@ export class FunctionTemplateService { .collection('FunctionTemplate') .countDocuments({ private: false }) - const templates = await this.db + const functionTemplate = await this.db .collection('FunctionTemplate') .aggregate(pipe) .toArray() const res = { - list: templates, + list: functionTemplate, total: total, page, pageSize, @@ -659,6 +732,7 @@ export class FunctionTemplateService { ) { const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const reg = new RegExp(safeName, 'i') + const pipe = [ { $match: { private: false, name: { $regex: reg } } }, @@ -698,61 +772,55 @@ export class FunctionTemplateService { return res } - async findMyMostStarFunctionTemplates( - starAsc: number, + async findMyFunctionTemplates( + recent: number, page: number, pageSize: number, userid: ObjectId, + hot?: boolean, + starAsc?: number, ) { - const pipe = [ - { $match: { private: false, uid: userid } }, - { - $lookup: { - from: 'FunctionTemplateItem', - localField: '_id', - foreignField: 'templateId', - as: 'items', + if (hot) { + const pipe = [ + { $match: { uid: userid } }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: '_id', + foreignField: 'templateId', + as: 'items', + }, }, - }, - { - $sort: { - star: starAsc === 0 ? 1 : -1, + { + $sort: { + star: starAsc === 0 ? 1 : -1, + }, }, - }, - { $skip: (page - 1) * pageSize }, - { $limit: pageSize }, - ] + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] - const total = await this.db - .collection('FunctionTemplate') - .countDocuments({ private: false, uid: userid }) + const total = await this.db + .collection('FunctionTemplate') + .countDocuments({ uid: userid }) - const templates = await this.db - .collection('FunctionTemplate') - .aggregate(pipe) - .toArray() + const myTemplate = await this.db + .collection('FunctionTemplate') + .aggregate(pipe) + .toArray() - const res = { - list: templates, - total: total, - page, - pageSize, - } + const res = { + list: myTemplate, + total: total, + page, + pageSize, + } - return res - } + return res + } - async findMyFunctionTemplatesByName( - recent: number, - page: number, - pageSize: number, - userid: ObjectId, - name: string, - ) { - const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - const reg = new RegExp(safeName, 'i') const pipe = [ - { $match: { uid: userid, name: { $regex: reg } } }, + { $match: { uid: userid } }, { $lookup: { from: 'FunctionTemplateItem', @@ -768,15 +836,11 @@ export class FunctionTemplateService { const total = await this.db .collection('FunctionTemplate') - .countDocuments( - { uid: userid, name: { $regex: reg } }, - { maxTimeMS: 5000 }, - ) + .countDocuments({ uid: userid }) const myTemplate = await this.db .collection('FunctionTemplate') .aggregate(pipe) - .maxTimeMS(5000) .toArray() const res = { @@ -789,14 +853,18 @@ export class FunctionTemplateService { return res } - async findMyFunctionTemplates( + async findMyFunctionTemplatesByName( recent: number, page: number, pageSize: number, userid: ObjectId, + name: string, ) { + const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const reg = new RegExp(safeName, 'i') + const pipe = [ - { $match: { uid: userid } }, + { $match: { uid: userid, name: { $regex: reg } } }, { $lookup: { from: 'FunctionTemplateItem', @@ -812,11 +880,15 @@ export class FunctionTemplateService { const total = await this.db .collection('FunctionTemplate') - .countDocuments({ uid: userid }) + .countDocuments( + { uid: userid, name: { $regex: reg } }, + { maxTimeMS: 5000 }, + ) const myTemplate = await this.db .collection('FunctionTemplate') .aggregate(pipe) + .maxTimeMS(5000) .toArray() const res = { @@ -830,12 +902,16 @@ export class FunctionTemplateService { } async findMyStaredFunctionTemplates( - recent: number, - page: number, - pageSize: number, userid: ObjectId, - name?: string, + condition: FindFunctionTemplatesParams, ) { + const recent = condition.recent + const page = condition.page + const pageSize = condition.pageSize + const name = condition.name + const hot = condition.hot + const starAsc = condition.starAsc + if (name) { const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const reg = new RegExp(safeName, 'i') @@ -857,7 +933,13 @@ export class FunctionTemplateService { as: 'items', }, }, - { $match: { uid: userid, 'functionTemplate.name': { $regex: reg } } }, + { + $match: { + uid: userid, + state: RelationState.Enabled, + 'functionTemplate.name': { $regex: reg }, + }, + }, { $sort: { createdAt: recent === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, @@ -880,7 +962,13 @@ export class FunctionTemplateService { as: 'items', }, }, - { $match: { uid: userid, 'functionTemplate.name': { $regex: reg } } }, + { + $match: { + uid: userid, + state: RelationState.Enabled, + 'functionTemplate.name': { $regex: reg }, + }, + }, { $group: { _id: null, count: { $sum: 1 } } }, ] @@ -918,8 +1006,67 @@ export class FunctionTemplateService { return res } + if (hot) { + const pipe = [ + { $match: { uid: userid, state: RelationState.Enabled } }, + { + $lookup: { + from: 'FunctionTemplate', + localField: 'templateId', + foreignField: '_id', + as: 'functionTemplate', + }, + }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: 'templateId', + foreignField: 'templateId', + as: 'items', + }, + }, + { + $sort: { + 'functionTemplate.star': starAsc === 0 ? 1 : -1, + }, + }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const total = await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .countDocuments({ uid: userid, state: RelationState.Enabled }) + + const myStarTemplates = await this.db + .collection( + 'FunctionTemplateStarRelation', + ) + .aggregate(pipe) + .toArray() + + const transformedData = myStarTemplates.map((element) => { + const { functionTemplate, items } = element + const [template] = functionTemplate + const result = { ...template } + result.items = items + return result + }) + + const res = { + list: transformedData, + total: total, + page, + pageSize, + } + + return res + } + const pipe = [ - { $match: { uid: userid } }, + { $match: { uid: userid, state: RelationState.Enabled } }, { $lookup: { from: 'FunctionTemplate', @@ -943,7 +1090,7 @@ export class FunctionTemplateService { const total = await this.db .collection('FunctionTemplateStarRelation') - .countDocuments({ uid: userid }) + .countDocuments({ uid: userid, state: RelationState.Enabled }) const myStarTemplates = await this.db .collection('FunctionTemplateStarRelation') @@ -969,13 +1116,162 @@ export class FunctionTemplateService { } async findMyRecentUseFunctionTemplates( - recent: number, - page: number, - pageSize: number, userid: ObjectId, + condition: FindFunctionTemplatesParams, ) { + const recent = condition.recent + const page = condition.page + const pageSize = condition.pageSize + const starAsc = condition.starAsc + const name = condition.name + const hot = condition.hot + + if (name) { + const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const reg = new RegExp(safeName, 'i') + const pipe = [ + { + $lookup: { + from: 'FunctionTemplate', + localField: 'templateId', + foreignField: '_id', + as: 'functionTemplate', + }, + }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: 'templateId', + foreignField: 'templateId', + as: 'items', + }, + }, + { + $match: { + uid: userid, + state: RelationState.Enabled, + 'functionTemplate.name': { $regex: reg }, + }, + }, + { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const pipe2 = [ + { + $lookup: { + from: 'FunctionTemplate', + localField: 'templateId', + foreignField: '_id', + as: 'functionTemplate', + }, + }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: 'templateId', + foreignField: 'templateId', + as: 'items', + }, + }, + { + $match: { + uid: userid, + state: RelationState.Enabled, + 'functionTemplate.name': { $regex: reg }, + }, + }, + { $group: { _id: null, count: { $sum: 1 } } }, + ] + + const total = await this.db + .collection('FunctionTemplateUseRelation') + .aggregate(pipe2) + .maxTimeMS(5000) + .toArray() + + const myStarTemplates = await this.db + .collection('FunctionTemplateUseRelation') + .aggregate(pipe) + .maxTimeMS(5000) + .toArray() + + const transformedData = myStarTemplates.map((element) => { + const { functionTemplate, items } = element + const [template] = functionTemplate + const result = { ...template } + result.items = items + return result + }) + + const res = { + list: transformedData, + total: total[0] ? total[0].count : 0, + page, + pageSize, + } + + return res + } + + if (hot) { + const pipe = [ + { $match: { uid: userid, state: RelationState.Enabled } }, + { + $lookup: { + from: 'FunctionTemplate', + localField: 'templateId', + foreignField: '_id', + as: 'functionTemplate', + }, + }, + { + $lookup: { + from: 'FunctionTemplateItem', + localField: 'templateId', + foreignField: 'templateId', + as: 'items', + }, + }, + { + $sort: { + 'functionTemplate.star': starAsc === 0 ? 1 : -1, + }, + }, + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + ] + + const recentUseFunctionTemplates = await this.db + .collection('FunctionTemplateUseRelation') + .aggregate(pipe) + .toArray() + + const total = await this.db + .collection('FunctionTemplateUseRelation') + .countDocuments({ uid: userid, state: RelationState.Enabled }) + + const transformedData = recentUseFunctionTemplates.map((element) => { + const { functionTemplate, items } = element + const [template] = functionTemplate + const result = { ...template } + result.items = items + return result + }) + + const res = { + list: transformedData, + total: total, + page, + pageSize, + } + + return res + } + const pipe = [ - { $match: { uid: userid } }, + { $match: { uid: userid, state: RelationState.Enabled } }, { $lookup: { from: 'FunctionTemplate', @@ -1004,7 +1300,7 @@ export class FunctionTemplateService { const total = await this.db .collection('FunctionTemplateUseRelation') - .countDocuments({ uid: userid }) + .countDocuments({ uid: userid, state: RelationState.Enabled }) const transformedData = recentUseFunctionTemplates.map((element) => { const { functionTemplate, items } = element @@ -1026,7 +1322,7 @@ export class FunctionTemplateService { async findOneFunctionTemplate(templateId: ObjectId, userid?: ObjectId) { if (userid) { - // 查找一个 function template + // find one function template const res = await this.db .collection('FunctionTemplate') .findOne({ _id: templateId, uid: userid }) @@ -1039,6 +1335,7 @@ export class FunctionTemplateService { return res } + // get items of a function template async findFunctionTemplateItems(templateId: ObjectId) { const functionTemplateItems = await this.db .collection('FunctionTemplateItem') @@ -1048,6 +1345,7 @@ export class FunctionTemplateService { return functionTemplateItems } + // Verify the relationship between the user and the appid async applicationAuthGuard(appid, userid) { const app = await this.appService.findOne(appid) if (!app) { From 774d0ec7d9ed542bdd21d0e6bde5a14503a4aafd Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Tue, 20 Jun 2023 03:35:50 +0000 Subject: [PATCH 7/8] fix --- .../charts/laf-server/templates/metering.yaml | 4 +- .../function-template.controller.ts | 188 +++++++++--------- .../function-template.service.ts | 59 ++---- 3 files changed, 119 insertions(+), 132 deletions(-) diff --git a/build/charts/laf-server/templates/metering.yaml b/build/charts/laf-server/templates/metering.yaml index 6b55c7cd05..4503c131cd 100644 --- a/build/charts/laf-server/templates/metering.yaml +++ b/build/charts/laf-server/templates/metering.yaml @@ -32,7 +32,7 @@ spec: memory: 64Mi env: - name: MONGO_URI - value: "mongodb://admin:gvm3kyvdq8kwsd04stdjpof97alzl6cw@mongodb-0.mongo.laf-system.svc.cluster.local:27017/sealos-resources?authSource=admin&replicaSet=rs0&w=majority" + value: {{ .Values.meteringDatabaseUrl | quote }} imagePullPolicy: Always --- apiVersion: v1 @@ -325,7 +325,7 @@ spec: - /manager env: - name: MONGO_URI - value: "mongodb://admin:gvm3kyvdq8kwsd04stdjpof97alzl6cw@mongodb-0.mongo.laf-system.svc.cluster.local:27017/sealos-resources?authSource=admin&replicaSet=rs0&w=majority" + value: {{ .Values.meteringDatabaseUrl | quote }} image: docker.io/bxy4543/laf-resources-controller:v0.0.2 imagePullPolicy: Always livenessProbe: diff --git a/server/src/function-template/function-template.controller.ts b/server/src/function-template/function-template.controller.ts index 42ec268f1e..f80c877c9d 100644 --- a/server/src/function-template/function-template.controller.ts +++ b/server/src/function-template/function-template.controller.ts @@ -14,7 +14,7 @@ import { import { FunctionTemplateService } from './function-template.service' import { CreateFunctionTemplateDto } from './dto/create-function-template.dto' import { UpdateFunctionTemplateDto } from './dto/update-function-template.dto' -import * as assert from 'node:assert' +// import * as assert from 'node:assert' import { IRequest } from '../utils/interface' import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger' import { @@ -234,7 +234,7 @@ export class FunctionTemplateController { @Get(':id/used-by') async getFunctionTemplateUsedBy( @Param('id') templateId: string, - @Query('recent') recent: number, + @Query('asc') asc: number, @Query('page') page: number, @Query('pageSize') pageSize: number, ) { @@ -242,7 +242,7 @@ export class FunctionTemplateController { return ResponseUtil.error('invalid templateId') } - recent = recent === 0 ? Number(recent) : 1 + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const found = await this.functionTemplateService.findOneFunctionTemplate( @@ -253,56 +253,95 @@ export class FunctionTemplateController { } const res = await this.functionTemplateService.functionTemplateUsedBy( new ObjectId(templateId), - recent, + asc, page, pageSize, ) return ResponseUtil.ok(res) } - @ApiOperation({ summary: 'get my template function' }) + /** + * asc default is time + * type sort asc= sort type + * + * @param page + * @param pageSize + * @param keyword + * @param asc + * @param sort + * @param type + * @param req + * @returns + */ + @ApiOperation({ summary: 'get my function template' }) @ApiResponsePagination(FunctionTemplateSwagger) @UseGuards(JwtAuthGuard) @Get('my') async getMyFunctionTemplate( - @Query('recent') recent: number, @Query('page') page: number, @Query('pageSize') pageSize: number, - @Query('name') name: string, - @Query('starName') starName: string, - @Query('stared') stared: boolean, - @Query('recentName') recentName: string, - @Query('recentUsed') recentUsed: boolean, - @Query('hot') hot: boolean, - @Query('starAsc') starAsc: number, + @Query('keyword') keyword: string, + @Query('asc') asc: number, + @Query('sort') sort: string, + @Query('type') type: string, @Req() req: IRequest, ) { - if (name) { - recent = recent === 0 ? Number(recent) : 1 + if (type === 'default' && keyword) { + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const res = await this.functionTemplateService.findMyFunctionTemplatesByName( - recent, + asc, page, pageSize, req.user._id, - name, + keyword, ) return ResponseUtil.ok(res) } - if (starName) { - recent = recent === 0 ? Number(recent) : 1 + if (type === 'default' && sort === 'hot') { + asc = asc === 0 ? Number(asc) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + const hot = true + + const res = await this.functionTemplateService.findMyFunctionTemplates( + asc, + page, + pageSize, + req.user._id, + hot, + ) + return ResponseUtil.ok(res) + } + + if (type === 'default') { + asc = asc === 0 ? Number(asc) : 1 + page = page ? Number(page) : 1 + pageSize = pageSize ? Number(pageSize) : 10 + + const res = await this.functionTemplateService.findMyFunctionTemplates( + asc, + page, + pageSize, + req.user._id, + ) + return ResponseUtil.ok(res) + } + + if (type === 'stared' && keyword) { + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const condition = { - recent, + asc, page, pageSize, - name: starName, + name: keyword, } const res = await this.functionTemplateService.findMyStaredFunctionTemplates( @@ -312,17 +351,15 @@ export class FunctionTemplateController { return ResponseUtil.ok(res) } - if (stared && hot) { - starAsc = starAsc === 0 ? Number(starAsc) : 1 + if (type === 'stared' && sort === 'hot') { + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 - recent = recent === 0 ? Number(recent) : 1 const condition = { - recent, page, pageSize, - hot, - starAsc, + asc, + hot: true, } const res = await this.functionTemplateService.findMyStaredFunctionTemplates( @@ -332,13 +369,13 @@ export class FunctionTemplateController { return ResponseUtil.ok(res) } - if (stared) { - recent = recent === 0 ? Number(recent) : 1 + if (type === 'stared') { + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const condition = { - recent, + asc, page, pageSize, } @@ -350,16 +387,16 @@ export class FunctionTemplateController { return ResponseUtil.ok(res) } - if (recentName) { - recent = recent === 0 ? Number(recent) : 1 + if ((type = 'recentUsed' && keyword)) { + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const condition = { - recent, + asc, page, pageSize, - name: recentName, + name: keyword, } const res = await this.functionTemplateService.findMyRecentUseFunctionTemplates( @@ -369,18 +406,16 @@ export class FunctionTemplateController { return ResponseUtil.ok(res) } - if (recentUsed && hot) { - starAsc = starAsc === 0 ? Number(starAsc) : 1 - recent = recent === 0 ? Number(recent) : 1 + if (type === 'recentUsed' && sort === 'hot') { + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const condition = { - recent, + asc, page, pageSize, - hot, - starAsc, + hot: true, } const res = await this.functionTemplateService.findMyRecentUseFunctionTemplates( @@ -390,13 +425,13 @@ export class FunctionTemplateController { return ResponseUtil.ok(res) } - if (recentUsed) { - recent = recent === 0 ? Number(recent) : 1 + if (type === 'recentUsed') { + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const condition = { - recent, + asc, page, pageSize, } @@ -407,35 +442,6 @@ export class FunctionTemplateController { ) return ResponseUtil.ok(res) } - - if (hot) { - starAsc = starAsc === 0 ? Number(starAsc) : 1 - recent = recent === 0 ? Number(recent) : 1 - page = page ? Number(page) : 1 - pageSize = pageSize ? Number(pageSize) : 10 - - const res = await this.functionTemplateService.findMyFunctionTemplates( - recent, - page, - pageSize, - req.user._id, - hot, - starAsc, - ) - return ResponseUtil.ok(res) - } - - recent = recent === 0 ? Number(recent) : 1 - page = page ? Number(page) : 1 - pageSize = pageSize ? Number(pageSize) : 10 - - const res = await this.functionTemplateService.findMyFunctionTemplates( - recent, - page, - pageSize, - req.user._id, - ) - return ResponseUtil.ok(res) } @ApiOperation({ summary: 'get one function template' }) @@ -454,14 +460,13 @@ export class FunctionTemplateController { new ObjectId(templateId), ) - try { - assert( - template.private === false || - template.uid.toString() === req.user._id.toString(), + if ( + template.private === true && + template.uid.toString() !== req.user._id.toString() + ) { + return ResponseUtil.error( 'private function template can only be inspect by the owner', ) - } catch (error) { - return ResponseUtil.error(error.message) } const res = await this.functionTemplateService.findOne( @@ -476,49 +481,48 @@ export class FunctionTemplateController { @UseGuards(JwtAuthGuard) @Get() async getAllFunctionTemplate( - @Query('recent') recent: number, + @Query('asc') asc: number, @Query('page') page: number, @Query('pageSize') pageSize: number, - @Query('name') name: string, - @Query('starAsc') starAsc: number, - @Query('hot') hot: boolean, + @Query('keyword') keyword: string, + @Query('sort') sort: string, ) { - if (name) { - recent = recent === 0 ? Number(recent) : 1 + if (keyword) { + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const res = await this.functionTemplateService.findFunctionTemplatesByName( - recent, + asc, page, pageSize, - name, + keyword, ) return ResponseUtil.ok(res) } - if (hot) { - starAsc = starAsc === 0 ? Number(starAsc) : 1 + if (sort === 'hot') { + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 + const hot = true const res = await this.functionTemplateService.findFunctionTemplates( - starAsc, + asc, page, pageSize, hot, - starAsc, ) return ResponseUtil.ok(res) } - recent = recent === 0 ? Number(recent) : 1 + asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 const res = await this.functionTemplateService.findFunctionTemplates( - recent, + asc, page, pageSize, ) diff --git a/server/src/function-template/function-template.service.ts b/server/src/function-template/function-template.service.ts index 477f286365..10c7306d47 100644 --- a/server/src/function-template/function-template.service.ts +++ b/server/src/function-template/function-template.service.ts @@ -22,7 +22,7 @@ import { DependencyService } from 'src/dependency/dependency.service' import { ApplicationService } from '../application/application.service' interface FindFunctionTemplatesParams { - recent: number + asc: number page: number pageSize: number name?: string @@ -394,11 +394,6 @@ export class FunctionTemplateService { // Get the collection const collectionFunction = appDataBase.collection(CN_PUBLISHED_FUNCTIONS) - // Delete existing documents with the matching names - await collectionFunction.deleteMany( - { name: { $in: namesToSearch } }, - { session }, - ) // Insert all new documents await collectionFunction.insertMany(documentsToInsert, { session }) @@ -585,7 +580,7 @@ export class FunctionTemplateService { async functionTemplateUsedBy( templateId: ObjectId, - recent: number, + asc: number, page: number, pageSize: number, ) { @@ -599,7 +594,7 @@ export class FunctionTemplateService { as: 'users', }, }, - { $sort: { updatedAt: recent === 0 ? 1 : -1 } }, + { $sort: { updatedAt: asc === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] @@ -645,11 +640,10 @@ export class FunctionTemplateService { // get all public function templates async findFunctionTemplates( - recent: number, + asc: number, page: number, pageSize: number, hot?: boolean, - starAsc?: number, ) { if (hot) { const pipe = [ @@ -664,7 +658,7 @@ export class FunctionTemplateService { }, { $sort: { - star: starAsc === 0 ? 1 : -1, + star: asc === 0 ? 1 : -1, }, }, { $skip: (page - 1) * pageSize }, @@ -700,7 +694,7 @@ export class FunctionTemplateService { as: 'items', }, }, - { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $sort: { createdAt: asc === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] @@ -725,7 +719,7 @@ export class FunctionTemplateService { } async findFunctionTemplatesByName( - recent: number, + asc: number, page: number, pageSize: number, name: string, @@ -744,7 +738,7 @@ export class FunctionTemplateService { as: 'items', }, }, - { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $sort: { createdAt: asc === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] @@ -773,12 +767,11 @@ export class FunctionTemplateService { } async findMyFunctionTemplates( - recent: number, + asc: number, page: number, pageSize: number, userid: ObjectId, hot?: boolean, - starAsc?: number, ) { if (hot) { const pipe = [ @@ -793,7 +786,7 @@ export class FunctionTemplateService { }, { $sort: { - star: starAsc === 0 ? 1 : -1, + star: asc === 0 ? 1 : -1, }, }, { $skip: (page - 1) * pageSize }, @@ -829,7 +822,7 @@ export class FunctionTemplateService { as: 'items', }, }, - { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $sort: { createdAt: asc === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] @@ -854,7 +847,7 @@ export class FunctionTemplateService { } async findMyFunctionTemplatesByName( - recent: number, + asc: number, page: number, pageSize: number, userid: ObjectId, @@ -873,7 +866,7 @@ export class FunctionTemplateService { as: 'items', }, }, - { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $sort: { createdAt: asc === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] @@ -905,12 +898,7 @@ export class FunctionTemplateService { userid: ObjectId, condition: FindFunctionTemplatesParams, ) { - const recent = condition.recent - const page = condition.page - const pageSize = condition.pageSize - const name = condition.name - const hot = condition.hot - const starAsc = condition.starAsc + const { asc, page, pageSize, name, hot } = condition if (name) { const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') @@ -940,7 +928,7 @@ export class FunctionTemplateService { 'functionTemplate.name': { $regex: reg }, }, }, - { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $sort: { createdAt: asc === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] @@ -1027,7 +1015,7 @@ export class FunctionTemplateService { }, { $sort: { - 'functionTemplate.star': starAsc === 0 ? 1 : -1, + 'functionTemplate.star': asc === 0 ? 1 : -1, }, }, { $skip: (page - 1) * pageSize }, @@ -1083,7 +1071,7 @@ export class FunctionTemplateService { as: 'items', }, }, - { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $sort: { createdAt: asc === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] @@ -1119,12 +1107,7 @@ export class FunctionTemplateService { userid: ObjectId, condition: FindFunctionTemplatesParams, ) { - const recent = condition.recent - const page = condition.page - const pageSize = condition.pageSize - const starAsc = condition.starAsc - const name = condition.name - const hot = condition.hot + const { asc, page, pageSize, name, hot } = condition if (name) { const safeName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') @@ -1153,7 +1136,7 @@ export class FunctionTemplateService { 'functionTemplate.name': { $regex: reg }, }, }, - { $sort: { createdAt: recent === 0 ? 1 : -1 } }, + { $sort: { updatedAt: asc === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] @@ -1236,7 +1219,7 @@ export class FunctionTemplateService { }, { $sort: { - 'functionTemplate.star': starAsc === 0 ? 1 : -1, + 'functionTemplate.star': asc === 0 ? 1 : -1, }, }, { $skip: (page - 1) * pageSize }, @@ -1288,7 +1271,7 @@ export class FunctionTemplateService { as: 'items', }, }, - { $sort: { updatedAt: recent === 0 ? 1 : -1 } }, + { $sort: { updatedAt: asc === 0 ? 1 : -1 } }, { $skip: (page - 1) * pageSize }, { $limit: pageSize }, ] From 6ad8aa6995565e0e8dd33b949cd0f857c75be626 Mon Sep 17 00:00:00 2001 From: HUAHUAI23 Date: Tue, 20 Jun 2023 06:31:12 +0000 Subject: [PATCH 8/8] fix --- .../entities/swagger-help.ts | 2 +- .../function-template.controller.ts | 43 +++++++++---------- .../function-template.service.ts | 18 +++----- 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/server/src/function-template/entities/swagger-help.ts b/server/src/function-template/entities/swagger-help.ts index 01e3401121..27fbbd63bd 100644 --- a/server/src/function-template/entities/swagger-help.ts +++ b/server/src/function-template/entities/swagger-help.ts @@ -73,7 +73,7 @@ export class FunctionTemplateSwagger { items: FunctionTemplateItemSwagger[] } -export class GetFunctionTemplateUsedByItemSwagger { +export class GetFunctionTemplateUsedBySwagger { @ApiProperty({ type: String }) _id: ObjectId diff --git a/server/src/function-template/function-template.controller.ts b/server/src/function-template/function-template.controller.ts index f80c877c9d..44604f61f1 100644 --- a/server/src/function-template/function-template.controller.ts +++ b/server/src/function-template/function-template.controller.ts @@ -14,7 +14,6 @@ import { import { FunctionTemplateService } from './function-template.service' import { CreateFunctionTemplateDto } from './dto/create-function-template.dto' import { UpdateFunctionTemplateDto } from './dto/update-function-template.dto' -// import * as assert from 'node:assert' import { IRequest } from '../utils/interface' import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger' import { @@ -30,7 +29,7 @@ import { BundleService } from 'src/application/bundle.service' import { DependencyService } from 'src/dependency/dependency.service' import { FunctionTemplateSwagger, - GetFunctionTemplateUsedByItemSwagger, + GetFunctionTemplateUsedBySwagger, } from './entities/swagger-help' @ApiTags('FunctionTemplate') @@ -80,7 +79,7 @@ export class FunctionTemplateController { @Param('appid') appid: string, @Req() req: IRequest, ) { - if (templateId.length != 24) { + if (templateId.length !== 24) { return ResponseUtil.error('invalid templateId') } // Check if appid is a valid resource @@ -106,7 +105,7 @@ export class FunctionTemplateController { await this.functionTemplateService.findFunctionTemplateItems( new ObjectId(templateId), ) - if (functionTemplateItems.length === 0 || !functionTemplateItems) { + if (!functionTemplateItems || functionTemplateItems.length === 0) { return ResponseUtil.error('function template items not found') } @@ -136,7 +135,7 @@ export class FunctionTemplateController { @Body() dto: UpdateFunctionTemplateDto, @Req() req: IRequest, ) { - if (templateId.length != 24) { + if (templateId.length !== 24) { return ResponseUtil.error('invalid templateId') } @@ -145,6 +144,7 @@ export class FunctionTemplateController { new ObjectId(templateId), req.user._id, ) + if (!found) { return ResponseUtil.error('function template not found') } @@ -166,7 +166,7 @@ export class FunctionTemplateController { @Param('id') templateId: string, @Req() req: IRequest, ) { - if (templateId.length != 24) { + if (templateId.length !== 24) { return ResponseUtil.error('invalid templateId') } @@ -174,13 +174,16 @@ export class FunctionTemplateController { new ObjectId(templateId), req.user._id, ) + if (!found) { return ResponseUtil.error('function template not found') } + const res = await this.functionTemplateService.deleteFunctionTemplate( new ObjectId(templateId), req.user._id, ) + return ResponseUtil.ok(res) } @@ -192,19 +195,23 @@ export class FunctionTemplateController { @Param('templateId') templateId: string, @Req() req: IRequest, ) { - if (templateId.length != 24) { + if (templateId.length !== 24) { return ResponseUtil.error('invalid templateId') } + const found = await this.functionTemplateService.findOneFunctionTemplate( new ObjectId(templateId), ) + if (!found) { return ResponseUtil.error('function template not found') } + const res = await this.functionTemplateService.starFunctionTemplate( new ObjectId(templateId), req.user._id, ) + return ResponseUtil.ok(res) } @@ -216,7 +223,7 @@ export class FunctionTemplateController { @Param('id') templateId: string, @Req() req: IRequest, ) { - if (templateId.length != 24) { + if (templateId.length !== 24) { return ResponseUtil.error('invalid templateId') } @@ -229,7 +236,7 @@ export class FunctionTemplateController { } @ApiOperation({ summary: 'get people who use this function template' }) - @ApiResponsePagination(GetFunctionTemplateUsedByItemSwagger) + @ApiResponsePagination(GetFunctionTemplateUsedBySwagger) @UseGuards(JwtAuthGuard) @Get(':id/used-by') async getFunctionTemplateUsedBy( @@ -238,7 +245,7 @@ export class FunctionTemplateController { @Query('page') page: number, @Query('pageSize') pageSize: number, ) { - if (templateId.length != 24) { + if (templateId.length !== 24) { return ResponseUtil.error('invalid templateId') } @@ -261,17 +268,9 @@ export class FunctionTemplateController { } /** - * asc default is time - * type sort asc= sort type - * - * @param page - * @param pageSize - * @param keyword - * @param asc - * @param sort - * @param type - * @param req - * @returns + * asc is sorted by time by default + * If sort has a value, then asc's sort type is the type of sort's value + * For example, if the value of sort is hot, then asc's sort is the star field */ @ApiOperation({ summary: 'get my function template' }) @ApiResponsePagination(FunctionTemplateSwagger) @@ -387,7 +386,7 @@ export class FunctionTemplateController { return ResponseUtil.ok(res) } - if ((type = 'recentUsed' && keyword)) { + if (type === 'recentUsed' && keyword) { asc = asc === 0 ? Number(asc) : 1 page = page ? Number(page) : 1 pageSize = pageSize ? Number(pageSize) : 10 diff --git a/server/src/function-template/function-template.service.ts b/server/src/function-template/function-template.service.ts index 10c7306d47..b3f85370a6 100644 --- a/server/src/function-template/function-template.service.ts +++ b/server/src/function-template/function-template.service.ts @@ -27,7 +27,6 @@ interface FindFunctionTemplatesParams { pageSize: number name?: string hot?: boolean - starAsc?: number } @Injectable() @@ -155,7 +154,7 @@ export class FunctionTemplateService { { session }, ) } - + // If a template becomes public, restart the usage and collection relationship for that template if (found.private === true && dto.private === false) { await this.db .collection( @@ -378,23 +377,16 @@ export class FunctionTemplateService { .collection('CloudFunction') .insertMany(documents, { session }) - // Prepare the names to be searched and documents to be inserted - const namesToSearch = functionTemplateItems.map((item) => item.name) - let documentsToInsert - // add function template items to app database - // Find all documents with matching names - await this.db + const namesToSearch = functionTemplateItems.map((item) => item.name) + const documentsToInsert = await this.db .collection('CloudFunction') .find({ appid: appid, name: { $in: namesToSearch } }, { session }) .toArray() - .then((docs) => { - documentsToInsert = docs - }) - // Get the collection + // Get the app database collection const collectionFunction = appDataBase.collection(CN_PUBLISHED_FUNCTIONS) - // Insert all new documents + // Insert function template items await collectionFunction.insertMany(documentsToInsert, { session }) // add user use relation