feat(project): add workspace directory configuration and management
- Add projectDir field to Project model for workspace directory management - Implement workspace directory creation, validation and Git initialization - Add workspace status query endpoint with directory info and Git status - Create GitManager for Git repository operations (clone, branch, commit info) - Add PathValidator for secure path validation and traversal attack prevention - Implement execution queue with concurrency control for build tasks - Refactor project list UI to remove edit/delete actions from cards - Add project settings tab in detail page with edit/delete functionality - Add icons to all tabs (History, Code, Settings) - Implement time formatting with dayjs in YYYY-MM-DD HH:mm:ss format - Display all timestamps using browser's local timezone - Update PipelineRunner to use workspace directory for command execution - Add workspace status card showing directory path, size, Git info - Enhance CreateProjectModal with repository URL validation
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { projectDirSchema } from '../../libs/path-validator.js';
|
||||
|
||||
/**
|
||||
* 创建项目验证架构
|
||||
@@ -15,6 +16,8 @@ export const createProjectSchema = z.object({
|
||||
repository: z.string({
|
||||
message: '仓库地址必须是字符串',
|
||||
}).url({ message: '请输入有效的仓库地址' }).min(1, { message: '仓库地址不能为空' }),
|
||||
|
||||
projectDir: projectDirSchema,
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { Context } from 'koa';
|
||||
import {prisma} from '../../libs/prisma.ts';
|
||||
import { prisma } from '../../libs/prisma.ts';
|
||||
import { log } from '../../libs/logger.ts';
|
||||
import { BusinessError } from '../../middlewares/exception.ts';
|
||||
import { Controller, Get, Post, Put, Delete } from '../../decorators/route.ts';
|
||||
import { GitManager } from '../../libs/git-manager.ts';
|
||||
import {
|
||||
createProjectSchema,
|
||||
updateProjectSchema,
|
||||
@@ -37,7 +38,7 @@ export class ProjectController {
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
})
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
@@ -47,7 +48,7 @@ export class ProjectController {
|
||||
limit: query?.limit || 10,
|
||||
total,
|
||||
totalPages: Math.ceil(total / (query?.limit || 10)),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,7 +68,48 @@ export class ProjectController {
|
||||
throw new BusinessError('项目不存在', 1002, 404);
|
||||
}
|
||||
|
||||
return project;
|
||||
// 获取工作目录状态信息
|
||||
let workspaceStatus = null;
|
||||
if (project.projectDir) {
|
||||
try {
|
||||
const status = await GitManager.checkWorkspaceStatus(
|
||||
project.projectDir,
|
||||
);
|
||||
let size = 0;
|
||||
let gitInfo = null;
|
||||
|
||||
if (status.exists && !status.isEmpty) {
|
||||
size = await GitManager.getDirectorySize(project.projectDir);
|
||||
}
|
||||
|
||||
if (status.hasGit) {
|
||||
gitInfo = await GitManager.getGitInfo(project.projectDir);
|
||||
}
|
||||
|
||||
workspaceStatus = {
|
||||
...status,
|
||||
size,
|
||||
gitInfo,
|
||||
};
|
||||
} catch (error) {
|
||||
log.error(
|
||||
'project',
|
||||
'Failed to get workspace status for project %s: %s',
|
||||
project.name,
|
||||
(error as Error).message,
|
||||
);
|
||||
// 即使获取状态失败,也返回项目信息
|
||||
workspaceStatus = {
|
||||
status: 'error',
|
||||
error: (error as Error).message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...project,
|
||||
workspaceStatus,
|
||||
};
|
||||
}
|
||||
|
||||
// POST /api/projects - 创建项目
|
||||
@@ -75,18 +117,36 @@ export class ProjectController {
|
||||
async create(ctx: Context) {
|
||||
const validatedData = createProjectSchema.parse(ctx.request.body);
|
||||
|
||||
// 检查工作目录是否已被其他项目使用
|
||||
const existingProject = await prisma.project.findFirst({
|
||||
where: {
|
||||
projectDir: validatedData.projectDir,
|
||||
valid: 1,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingProject) {
|
||||
throw new BusinessError('该工作目录已被其他项目使用', 1003, 400);
|
||||
}
|
||||
|
||||
const project = await prisma.project.create({
|
||||
data: {
|
||||
name: validatedData.name,
|
||||
description: validatedData.description || '',
|
||||
repository: validatedData.repository,
|
||||
projectDir: validatedData.projectDir,
|
||||
createdBy: 'system',
|
||||
updatedBy: 'system',
|
||||
valid: 1,
|
||||
},
|
||||
});
|
||||
|
||||
log.info('project', 'Created new project: %s', project.name);
|
||||
log.info(
|
||||
'project',
|
||||
'Created new project: %s with projectDir: %s',
|
||||
project.name,
|
||||
project.projectDir,
|
||||
);
|
||||
return project;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user