diff --git a/.lingma/rules/project_rule.md b/.lingma/rules/project_rule.md new file mode 100644 index 0000000..35a81c3 --- /dev/null +++ b/.lingma/rules/project_rule.md @@ -0,0 +1,8 @@ +**添加规则文件可帮助模型精准理解你的编码偏好,如框架、代码风格等** +**规则文件只对当前工程生效,单文件限制10000字符。如果无需将该文件提交到远程 Git 仓库,请将其添加到 .gitignore** +# 技术栈规范 + +## 基础环境 +1. 语言:TypeScript +2. nodejs 使用版本v22 +3. 执行node, npm 相关命令时先执行`nvm use v22`切换到v22版本 diff --git a/.nvmrc b/.nvmrc index fb61e2c..4377937 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v22.14 \ No newline at end of file +v22.13.1 \ No newline at end of file diff --git a/backend/index.js b/backend/index.js new file mode 100644 index 0000000..111425d --- /dev/null +++ b/backend/index.js @@ -0,0 +1 @@ +require('./dist/main'); \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index 190cb31..bca9bdc 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,7 +9,9 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@nestjs/axios": "^4.0.0", "@nestjs/common": "^11.1.3", + "@nestjs/config": "^4.0.2", "@nestjs/core": "^11.1.3", "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^11.1.3", @@ -102,6 +104,17 @@ "node": ">=8" } }, + "node_modules/@nestjs/axios": { + "version": "4.0.0", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/@nestjs/axios/-/axios-4.0.0.tgz", + "integrity": "sha512-1cB+Jyltu/uUPNQrpUimRHEQHrnQrpLzVj6dU3dgn6iDDDdahr10TgHFGTmw5VuJ9GzKZsCLDL78VSwJAs/9JQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "axios": "^1.3.1", + "rxjs": "^7.0.0" + } + }, "node_modules/@nestjs/common": { "version": "11.1.3", "resolved": "https://registry.npmmirror.com/@nestjs/common/-/common-11.1.3.tgz", @@ -133,6 +146,33 @@ } } }, + "node_modules/@nestjs/config": { + "version": "4.0.2", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/@nestjs/config/-/config-4.0.2.tgz", + "integrity": "sha512-McMW6EXtpc8+CwTUwFdg6h7dYcBUpH5iUILCclAsa+MbCEvC9ZKu4dCHRlJqALuhjLw97pbQu62l4+wRwGeZqA==", + "license": "MIT", + "dependencies": { + "dotenv": "16.4.7", + "dotenv-expand": "12.0.1", + "lodash": "4.17.21" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/@nestjs/core": { "version": "11.1.3", "resolved": "https://registry.npmmirror.com/@nestjs/core/-/core-11.1.3.tgz", @@ -569,6 +609,25 @@ "devOptional": true, "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT", + "peer": true + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "peer": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", @@ -839,6 +898,19 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-2.0.0.tgz", @@ -973,6 +1045,16 @@ } } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", @@ -1004,6 +1086,21 @@ "url": "https://dotenvx.com" } }, + "node_modules/dotenv-expand": { + "version": "12.0.1", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/dotenv-expand/-/dotenv-expand-12.0.1.tgz", + "integrity": "sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1084,6 +1181,22 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", @@ -1197,6 +1310,27 @@ "node": ">= 0.8" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", @@ -1213,6 +1347,46 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", @@ -1334,6 +1508,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", @@ -1545,6 +1735,12 @@ "node": ">=13.2.0" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -2071,6 +2267,13 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://npm.dacentsoft.com:1443/repository/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT", + "peer": true + }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz", diff --git a/backend/package.json b/backend/package.json index 0ae9208..168a9e2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,7 +17,9 @@ "author": "", "license": "ISC", "dependencies": { + "@nestjs/axios": "^4.0.0", "@nestjs/common": "^11.1.3", + "@nestjs/config": "^4.0.2", "@nestjs/core": "^11.1.3", "@nestjs/passport": "^11.0.5", "@nestjs/platform-express": "^11.1.3", diff --git a/backend/src/ai/ai-config.controller.ts b/backend/src/ai/ai-config.controller.ts new file mode 100644 index 0000000..ff9015a --- /dev/null +++ b/backend/src/ai/ai-config.controller.ts @@ -0,0 +1,37 @@ +import { Controller, Get, Post, Body, UseGuards, Request } from '@nestjs/common'; +import { JwtAuthGuard } from '../auth/jwt-auth.guard'; +import { AiConfigService } from './ai-config.service'; +import { CreateAiConfigDto } from './dto/create-ai-config.dto'; +import { UnauthorizedException } from '@nestjs/common'; + +interface UserRequest extends Express.Request { + user?: { + email: string; + workspaceId: number; + }; +} + +@Controller('ai/config') +@UseGuards(JwtAuthGuard) +export class AiConfigController { + constructor(private readonly aiConfigService: AiConfigService) {} + + @Get() + getAiConfig(@Request() req: UserRequest) { + if (!req.user?.workspaceId) { + throw new UnauthorizedException('用户未关联工作区'); + } + return this.aiConfigService.getAiConfigByWorkspace(req.user.workspaceId); + } + + @Post() + createAiConfig( + @Request() req: UserRequest, + @Body() createAiConfigDto: CreateAiConfigDto, + ) { + if (!req.user?.workspaceId) { + throw new UnauthorizedException('用户未关联工作区'); + } + return this.aiConfigService.createAiConfig(createAiConfigDto, req.user.workspaceId); + } +} \ No newline at end of file diff --git a/backend/src/ai/ai-config.service.ts b/backend/src/ai/ai-config.service.ts new file mode 100644 index 0000000..c065b70 --- /dev/null +++ b/backend/src/ai/ai-config.service.ts @@ -0,0 +1,43 @@ +import { Injectable } from '@nestjs/common'; +import { Repository } from 'typeorm'; +import { InjectRepository } from '@nestjs/typeorm'; +import { AiConfig } from './ai-config.entity'; +import { CreateAiConfigDto } from './dto/create-ai-config.dto'; +import { Workspace } from '../workspace/workspace.entity'; +import { NotFoundException } from '@nestjs/common'; + +@Injectable() +export class AiConfigService { + constructor( + @InjectRepository(AiConfig) + private aiConfigRepository: Repository, + @InjectRepository(Workspace) + private workspaceRepository: Repository, + ) {} + + async getAiConfig() { + return await this.aiConfigRepository.find(); + } + + async createAiConfig(createAiConfigDto: CreateAiConfigDto, workspaceId: number = 1) { + const workspace = await this.workspaceRepository.findOneBy({ id: workspaceId }); + if (!workspace) { + throw new NotFoundException('工作区不存在'); + } + + const aiConfig = this.aiConfigRepository.create({ + ...createAiConfigDto, + workspace, + }); + + return await this.aiConfigRepository.save(aiConfig); + } + + // 新增根据工作区获取AI配置的方法 + async getAiConfigByWorkspace(workspaceId: number) { + return await this.aiConfigRepository.findOne({ + where: { workspace: { id: workspaceId } }, + select: ['id', 'apiUrl', 'apiKey'] + }); + } +} \ No newline at end of file diff --git a/backend/src/ai/ai.controller.ts b/backend/src/ai/ai.controller.ts new file mode 100644 index 0000000..4e86547 --- /dev/null +++ b/backend/src/ai/ai.controller.ts @@ -0,0 +1,14 @@ +import { Controller, Post, Body, UseGuards } from '@nestjs/common'; +import { AiService } from './ai.service'; +import { JwtAuthGuard } from '../auth/jwt-auth.guard'; + +@Controller('ai') +@UseGuards(JwtAuthGuard) +export class AiController { + constructor(private readonly aiService: AiService) {} + + @Post('/generate-code') + generateCode(@Body() body: { prompt: string; workspaceId: number }) { + return this.aiService.generateCode(body.prompt, body.workspaceId); + } +} \ No newline at end of file diff --git a/backend/src/ai/ai.module.ts b/backend/src/ai/ai.module.ts new file mode 100644 index 0000000..53f4246 --- /dev/null +++ b/backend/src/ai/ai.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { AiConfig } from './ai-config.entity'; +import { AiConfigController } from './ai-config.controller'; +import { AiConfigService } from './ai-config.service'; +import { AiService } from './ai.service'; +import { AiController } from './ai.controller'; + +@Module({ + imports: [TypeOrmModule.forFeature([AiConfig])], + controllers: [AiConfigController, AiController], + providers: [AiConfigService, AiService], + exports: [AiConfigService] // 添加服务导出 +}) +export class AiModule {} \ No newline at end of file diff --git a/backend/src/ai/ai.service.ts b/backend/src/ai/ai.service.ts new file mode 100644 index 0000000..77c68d7 --- /dev/null +++ b/backend/src/ai/ai.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@nestjs/common'; +import { HttpService } from '@nestjs/axios'; +import { ConfigService } from '@nestjs/config'; +import { AiConfigService } from './ai-config.service'; + +@Injectable() +export class AiService { + private readonly defaultModel: string; + + constructor( + private readonly httpService: HttpService, + private readonly configService: ConfigService, + private readonly aiConfigService: AiConfigService, + ) { + this.defaultModel = this.configService.get('AI_DEFAULT_MODEL', 'gpt-3.5-turbo'); + } + + async generateCode(prompt: string, workspaceId: number) { + // 获取工作区的AI配置 + const aiConfig = await this.aiConfigService.getAiConfigByWorkspace(workspaceId); + + // 这里实现调用外部AI服务的逻辑 + // 需要根据实际的AI服务API进行实现 + return { + success: true, + code: '// 生成的代码示例\nconsole.log("Hello World");', + model: this.defaultModel, + }; + } +} \ No newline at end of file diff --git a/backend/src/ai/dto/create-ai-config.dto.ts b/backend/src/ai/dto/create-ai-config.dto.ts new file mode 100644 index 0000000..7837e2a --- /dev/null +++ b/backend/src/ai/dto/create-ai-config.dto.ts @@ -0,0 +1,9 @@ +import { IsString } from 'class-validator'; + +export class CreateAiConfigDto { + @IsString() + apiUrl!: string; + + @IsString() + apiKey!: string; +} \ No newline at end of file diff --git a/backend/src/app.controller.ts b/backend/src/app.controller.ts new file mode 100644 index 0000000..57985e9 --- /dev/null +++ b/backend/src/app.controller.ts @@ -0,0 +1,4 @@ +import { Controller } from '@nestjs/common'; + +@Controller() +export class AppController {} \ No newline at end of file diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 6242093..3791b0e 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -1,25 +1,46 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { UserModule } from './user/user.module'; +import { WorkspaceModule } from './workspace/workspace.module'; +import { ProjectModule } from './project/project.module'; import { AuthModule } from './auth/auth.module'; +import { AiModule } from './ai/ai.module'; import { User } from './user/user.entity'; import { Workspace } from './workspace/workspace.entity'; import { Project } from './project/project.entity'; import { AiConfig } from './ai/ai-config.entity'; import { PluginConfig } from './plugins/plugin-config.entity'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +// 数据库实体 +const ENTITIES = [User, Workspace, Project, AiConfig, PluginConfig]; + +// 数据库配置 +const DATABASE_CONFIG = TypeOrmModule.forRoot({ + type: 'postgres', + host: 'localhost', + port: 5432, + username: 'postgres', + password: 'postgres', + database: 'aiframe', + entities: ENTITIES, + synchronize: true, +}); + +// 功能模块 +const MODULES = [ + DATABASE_CONFIG, + AuthModule, + UserModule, + WorkspaceModule, + ProjectModule, + AiModule, +]; @Module({ - imports: [ - TypeOrmModule.forRoot({ - type: 'postgres', - host: 'localhost', - port: 5432, - username: 'postgres', - password: 'postgres', - database: 'aiframe', - entities: [User, Workspace, Project, AiConfig, PluginConfig], - synchronize: true, - }), - AuthModule, - ], + imports: MODULES, + controllers: [AppController], + providers: [AppService], }) export class AppModule {} \ No newline at end of file diff --git a/backend/src/app.service.ts b/backend/src/app.service.ts new file mode 100644 index 0000000..1cdab92 --- /dev/null +++ b/backend/src/app.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} \ No newline at end of file diff --git a/backend/src/auth/jwt-auth.guard.ts b/backend/src/auth/jwt-auth.guard.ts new file mode 100644 index 0000000..33f3300 --- /dev/null +++ b/backend/src/auth/jwt-auth.guard.ts @@ -0,0 +1,27 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { JwtStrategy } from './jwt.strategy'; + +@Injectable() +export class JwtAuthGuard implements CanActivate { + constructor(private reflector: Reflector, private jwtStrategy: JwtStrategy) {} + + async canActivate(context: ExecutionContext): Promise { + const req = context.switchToHttp().getRequest(); + const token = req.headers.authorization?.split(' ')[1]; + if (!token) return false; + + try { + const user = await new Promise((resolve, reject) => { + this.jwtStrategy.validate({ sub: 0, email: token }, (err, user) => { + if (err) reject(err); + resolve(user); + }); + }); + req.user = user; + return true; + } catch (e) { + return false; + } + } +} \ No newline at end of file diff --git a/backend/src/auth/jwt.strategy.ts b/backend/src/auth/jwt.strategy.ts index 2ef4109..ff2fb71 100644 --- a/backend/src/auth/jwt.strategy.ts +++ b/backend/src/auth/jwt.strategy.ts @@ -12,7 +12,11 @@ export class JwtStrategy extends PassportStrategy(Strategy) { }); } - async validate(payload: any) { - return { userId: payload.sub, email: payload.email }; + async validate(payload: { sub: number; email: string }, done: (error: any, user?: any, info?: any) => void) { + const user = { + sub: payload.sub, + email: payload.email, + }; + done(null, user); } } \ No newline at end of file diff --git a/backend/src/project/project.module.ts b/backend/src/project/project.module.ts new file mode 100644 index 0000000..5c0daeb --- /dev/null +++ b/backend/src/project/project.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Project } from './project.entity'; +import { ProjectRepository } from './project.repository'; // 路径已确认为正确相对路径 + +@Module({ + imports: [TypeOrmModule.forFeature([Project])], + providers: [ProjectRepository], + exports: [ProjectRepository] +}) +export class ProjectModule {} \ No newline at end of file diff --git a/backend/src/project/project.repository.ts b/backend/src/project/project.repository.ts new file mode 100644 index 0000000..1a7ab93 --- /dev/null +++ b/backend/src/project/project.repository.ts @@ -0,0 +1,5 @@ +import { EntityRepository, Repository } from 'typeorm'; +import { Project } from './project.entity'; + +@EntityRepository(Project) +export class ProjectRepository extends Repository {} \ No newline at end of file diff --git a/backend/src/user/user.module.ts b/backend/src/user/user.module.ts new file mode 100644 index 0000000..5d1aa66 --- /dev/null +++ b/backend/src/user/user.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { User } from './user.entity'; +import { UserRepository } from './user.repository'; +import { UserService } from './user.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([User])], + providers: [UserService, UserRepository], + exports: [UserService] +}) +export class UserModule {} \ No newline at end of file diff --git a/backend/src/workspace/workspace.module.ts b/backend/src/workspace/workspace.module.ts new file mode 100644 index 0000000..276e1a7 --- /dev/null +++ b/backend/src/workspace/workspace.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Workspace } from './workspace.entity'; +import { WorkspaceRepository } from './workspace.repository'; + +@Module({ + imports: [TypeOrmModule.forFeature([Workspace], 'YourDataSourceName')], // 如果使用了特定数据源,替换'YourDataSourceName'为实际名称 + providers: [ + { + provide: 'WORKSPACE_REPOSITORY', + useClass: WorkspaceRepository, + }, + ], + exports: ['WORKSPACE_REPOSITORY'], +}) +export class WorkspaceModule {} \ No newline at end of file diff --git a/backend/src/workspace/workspace.repository.ts b/backend/src/workspace/workspace.repository.ts new file mode 100644 index 0000000..96b7d48 --- /dev/null +++ b/backend/src/workspace/workspace.repository.ts @@ -0,0 +1,5 @@ +import { EntityRepository, Repository } from 'typeorm'; +import { Workspace } from './workspace.entity'; + +@EntityRepository(Workspace) +export class WorkspaceRepository extends Repository {} \ No newline at end of file