first code
This commit is contained in:
commit
16a04dbb27
|
@ -0,0 +1,4 @@
|
|||
dist
|
||||
.env
|
||||
node_modules
|
||||
*.log
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "rimraf dist && tsc",
|
||||
"start": "npm run build && node dist/main.js",
|
||||
"dev": "ts-node -r tsconfig-paths/register src/main.ts",
|
||||
"typeorm": "typeorm-ts-node-commonjs -d dist/data-source.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^9.4.3",
|
||||
"@nestjs/core": "^9.4.3",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^9.4.3",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"class-validator": "^0.14.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"passport": "^0.4.1",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"reflect-metadata": "^0.1.14",
|
||||
"rxjs": "^7.8.2",
|
||||
"typeorm": "^0.3.24",
|
||||
"uid": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm';
|
||||
import { Workspace } from '../workspace/workspace.entity';
|
||||
|
||||
@Entity()
|
||||
export class AiConfig {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column()
|
||||
apiUrl!: string;
|
||||
|
||||
@Column()
|
||||
apiKey!: string;
|
||||
|
||||
@OneToOne(() => Workspace)
|
||||
@JoinColumn()
|
||||
workspace!: Workspace;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AuthModule } from './auth/auth.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';
|
||||
|
||||
@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,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
|
@ -0,0 +1,19 @@
|
|||
import { Body, Controller, Post } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { LoginDto } from './dto/login.dto';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
@Post('register')
|
||||
register(@Body() createUserDto: CreateUserDto) {
|
||||
return this.authService.register(createUserDto);
|
||||
}
|
||||
|
||||
@Post('login')
|
||||
login(@Body() loginDto: LoginDto) {
|
||||
return this.authService.login(loginDto);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { User } from '../user/user.entity';
|
||||
import { UserService } from '../user/user.service';
|
||||
import { UserRepository } from '../user/user.repository';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { AuthService } from './auth.service';
|
||||
import { JwtStrategy } from './jwt.strategy';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([User])],
|
||||
controllers: [AuthController],
|
||||
providers: [
|
||||
AuthService,
|
||||
UserService,
|
||||
UserRepository,
|
||||
JwtStrategy,
|
||||
],
|
||||
})
|
||||
export class AuthModule {}
|
|
@ -0,0 +1,40 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { UserService } from '../user/user.service';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { LoginDto } from './dto/login.dto';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
async register(createUserDto: CreateUserDto) {
|
||||
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
|
||||
return this.userService.create({
|
||||
...createUserDto,
|
||||
password: hashedPassword,
|
||||
});
|
||||
}
|
||||
|
||||
async login(loginDto: LoginDto) {
|
||||
const user = await this.userService.findByEmail(loginDto.email);
|
||||
if (!user) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
const isPasswordValid = await bcrypt.compare(loginDto.password, user.password);
|
||||
if (!isPasswordValid) {
|
||||
throw new Error('Invalid credentials');
|
||||
}
|
||||
|
||||
return {
|
||||
accessToken: this.generateJwtToken(user),
|
||||
};
|
||||
}
|
||||
|
||||
private generateJwtToken(user: any): string {
|
||||
// 实际实现应使用jsonwebtoken库生成JWT
|
||||
// 这里仅作为示例返回模拟令牌
|
||||
return `mock-jwt-token-for-${user.email}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { IsEmail, IsString, MinLength } from 'class-validator';
|
||||
|
||||
export class CreateUserDto {
|
||||
@IsEmail()
|
||||
email!: string;
|
||||
|
||||
@IsString()
|
||||
@MinLength(6)
|
||||
password!: string;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { IsEmail, IsString, MinLength } from 'class-validator';
|
||||
|
||||
export class LoginDto {
|
||||
@IsEmail()
|
||||
email!: string;
|
||||
|
||||
@IsString()
|
||||
@MinLength(6)
|
||||
password!: string;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export * from './dto/create-user.dto';
|
||||
export * from './dto/login.dto';
|
||||
export * from './jwt.strategy';
|
|
@ -0,0 +1,18 @@
|
|||
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor() {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: process.env.JWT_SECRET || 'secret_key',
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
return { userId: payload.sub, email: payload.email };
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { DataSource } from 'typeorm';
|
||||
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';
|
||||
|
||||
export const AppDataSource = new DataSource({
|
||||
type: 'postgres',
|
||||
host: process.env.POSTGRES_HOST || 'localhost',
|
||||
port: parseInt(process.env.POSTGRES_PORT) || 5432,
|
||||
username: process.env.POSTGRES_USER || 'postgres',
|
||||
password: process.env.POSTGRES_PASSWORD || 'postgres',
|
||||
database: process.env.POSTGRES_DB || 'aiframe',
|
||||
entities: [User, Workspace, Project, AiConfig, PluginConfig],
|
||||
synchronize: true,
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
import { NestFactory } from '@nestjs/core'
|
||||
import { AppModule } from './app.module'
|
||||
import 'reflect-metadata'
|
||||
async function bootstrap () {
|
||||
const app = await NestFactory.create(AppModule)
|
||||
await app.listen(3000)
|
||||
}
|
||||
bootstrap()
|
|
@ -0,0 +1,18 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm';
|
||||
import { Workspace } from '../workspace/workspace.entity';
|
||||
|
||||
@Entity()
|
||||
export class PluginConfig {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column()
|
||||
pluginId!: string;
|
||||
|
||||
@Column({ type: 'json', nullable: true })
|
||||
settings!: Record<string, any>;
|
||||
|
||||
@OneToOne(() => Workspace)
|
||||
@JoinColumn()
|
||||
workspace!: Workspace;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
|
||||
import { Workspace } from '../workspace/workspace.entity';
|
||||
|
||||
@Entity()
|
||||
export class Project {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column()
|
||||
name!: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description!: string;
|
||||
|
||||
@ManyToOne(() => Workspace, (workspace: Workspace) => workspace.projects)
|
||||
workspace!: Workspace;
|
||||
|
||||
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
||||
createdAt!: Date;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { Entity, Column, OneToOne, JoinColumn } from 'typeorm';
|
||||
import { Workspace } from '../workspace/workspace.entity';
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@Column()
|
||||
id!: number;
|
||||
|
||||
@Column({ unique: true })
|
||||
email!: string;
|
||||
|
||||
@Column()
|
||||
password!: string;
|
||||
|
||||
@OneToOne(() => Workspace)
|
||||
@JoinColumn()
|
||||
workspace!: Workspace;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@EntityRepository(User)
|
||||
export class UserRepository extends Repository<User> {
|
||||
// 可以添加自定义查询方法
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { UserRepository } from './user.repository';
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
constructor(private readonly userRepository: UserRepository) {}
|
||||
|
||||
create(createUserDto: any) {
|
||||
return this.userRepository.create(createUserDto);
|
||||
}
|
||||
|
||||
findByEmail(email: string) {
|
||||
return this.userRepository.findOne({ where: { email } });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, OneToMany } from 'typeorm';
|
||||
import { User } from '../user/user.entity';
|
||||
import { Project } from '../project/project.entity';
|
||||
|
||||
@Entity()
|
||||
export class Workspace {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column()
|
||||
name!: string;
|
||||
|
||||
@OneToOne(() => User)
|
||||
@JoinColumn()
|
||||
user!: User;
|
||||
|
||||
@OneToMany(() => Project, (project: Project) => project.workspace)
|
||||
projects!: Project[];
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "CommonJS",
|
||||
"strict": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"resolveJsonModule": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
# API接口规范
|
||||
|
||||
## 用户认证
|
||||
- `POST /auth/register` - 用户注册
|
||||
- 请求体:{email: string, password: string}
|
||||
- 响应:{accessToken: string}
|
||||
|
||||
- `POST /auth/login` - 用户登录
|
||||
- 请求体:{email: string, password: string}
|
||||
- 响应:{accessToken: string}
|
||||
|
||||
## 工作区管理
|
||||
- `GET /workspace` - 获取用户工作区
|
||||
- `POST /workspace` - 创建工作区
|
||||
|
||||
## AI服务配置
|
||||
- `GET /ai/config` - 获取AI配置
|
||||
- `POST /ai/config` - 保存AI配置
|
||||
|
||||
## 插件管理
|
||||
- `GET /plugins` - 获取可用插件
|
||||
- `POST /plugins/install` - 安装插件
|
||||
|
||||
## 项目管理
|
||||
- `GET /projects` - 获取项目列表
|
||||
- `POST /projects` - 创建新项目
|
||||
- `GET /projects/:id` - 获取项目详情
|
|
@ -0,0 +1,23 @@
|
|||
# 系统架构设计
|
||||
|
||||
## 技术栈
|
||||
- 前端:Angular 16 + Material Design
|
||||
- 后端:NestJS + TypeORM + JWT
|
||||
- 数据库:PostgreSQL
|
||||
- 文件存储:MinIO
|
||||
- 实时通信:WebSocket
|
||||
|
||||
## 模块划分
|
||||
1. 用户认证模块
|
||||
2. 工作区管理模块
|
||||
3. AI服务集成模块
|
||||
4. 插件管理模块
|
||||
5. 项目管理模块
|
||||
|
||||
## 数据模型关系
|
||||
```mermaid
|
||||
graph TD
|
||||
User --> Workspace
|
||||
Workspace --> Project
|
||||
Workspace --> AiConfig
|
||||
Workspace --> PluginConfig
|
|
@ -0,0 +1,36 @@
|
|||
# 数据库设计
|
||||
|
||||
## 用户表 (User)
|
||||
| 字段名 | 类型 | 描述 |
|
||||
|-------|------|------|
|
||||
| id | integer | 主键 |
|
||||
| email | string | 邮箱(唯一) |
|
||||
| password | string | 密码 |
|
||||
|
||||
## 工作区表 (Workspace)
|
||||
| 字段名 | 类型 | 描述 |
|
||||
|-------|------|------|
|
||||
| id | integer | 主键 |
|
||||
| name | string | 工作区名称 |
|
||||
|
||||
## 项目表 (Project)
|
||||
| 字段名 | 类型 | 描述 |
|
||||
|-------|------|------|
|
||||
| id | integer | 主键 |
|
||||
| name | string | 项目名称 |
|
||||
| description | text | 项目描述 |
|
||||
| createdAt | timestamp | 创建时间 |
|
||||
|
||||
## AI配置表 (AiConfig)
|
||||
| 字段名 | 类型 | 描述 |
|
||||
|-------|------|------|
|
||||
| id | integer | 主键 |
|
||||
| apiUrl | string | API地址 |
|
||||
| apiKey | string | API密钥 |
|
||||
|
||||
## 插件配置表 (PluginConfig)
|
||||
| 字段名 | 类型 | 描述 |
|
||||
|-------|------|------|
|
||||
| id | integer | 主键 |
|
||||
| pluginId | string | 插件ID |
|
||||
| settings | json | 插件配置 |
|
|
@ -0,0 +1,16 @@
|
|||
## role
|
||||
|
||||
你是一个软件开发专家,专长领域是:
|
||||
- Angular前端开发
|
||||
- Node.js后端开发
|
||||
- AI与人工智能开发
|
||||
- 机器学习开发
|
||||
|
||||
我现在需要你完成一个项目,项目需求如下:
|
||||
1. 每个用户有独立的工作区
|
||||
2. 可以通过用户提供的API地址和APIkey连接到指定的AI服务
|
||||
3. 可以安装,调用指定用户指定的MCP插件
|
||||
4. 用户可以在工作区中创建新的项目,并保存项目
|
||||
5. 用户可以通过对话或工作区中的需求文件为AI提供项目需求。
|
||||
6. AI可以通过MCP插件完成用户需求的功能。
|
||||
7. 提供界面供用户浏览,下载生成的代码
|
Loading…
Reference in New Issue