* feat: migrate chat files to s3 (#5802) * feat: migrate chat files to s3 * feat: add delete jobs for deleting s3 files * chore: improvements * fix: lockfile * fix: imports * feat: add ttl for those uploaded files but not send yet * feat: init bullmq worker * fix: s3 key * perf: s3 internal url * remove env * fix: re-sign a new url * fix: re-sign a new url * perf: s3 code --------- Co-authored-by: archer <545436317@qq.com> * update pacakge * feat: add more file type for uploading (#5807) * fix: re-sign a new url * wip: file selector * feat: add more file type for uploading * feat: migrate chat files to s3 (#5802) * feat: migrate chat files to s3 * feat: add delete jobs for deleting s3 files * chore: improvements * fix: lockfile * fix: imports * feat: add ttl for those uploaded files but not send yet * feat: init bullmq worker * fix: s3 key * perf: s3 internal url * remove env * fix: re-sign a new url * fix: re-sign a new url * perf: s3 code --------- Co-authored-by: archer <545436317@qq.com> * fix: limit minmax available file upload number * perf: file select modal code * fix: fileselect refresh * fix: ts --------- Co-authored-by: archer <545436317@qq.com> * bugfix: chat page (#5809) * fix: upload avatar * fix: chat page username display issue and setting button visibility * doc * Markdown match base64 performance * feat: improve global variables(time, file, dataset) (#5804) * feat: improve global variables(time, file, dataset) * feat: optimize code * perf: time variables code * fix: model, file * fix: hide file upload * fix: ts * hide dataset select --------- Co-authored-by: archer <545436317@qq.com> * perf: insert training queue * perf: s3 upload error i18n * fix: share page s3 * fix: timeselector ui error * var update node * Timepicker ui * feat: plugin support password * fix: password disabled UX * fix: button size * fix: no model cache for chat page (#5820) * rename function * fix: workflow bug * fix: interactive loop * fix test * perf: common textare no richtext * move system plugin config (#5803) (#5813) * move system plugin config (#5803) * move system plugin config * extract tag bar * filter * tool detail temp * marketplace * params * fix * type * search * tags render * status * ui * code * connect to backend (#5815) * feat: marketplace apis & type definitions (#5817) * chore: marketplace init * chore: marketplace list api type * chore: detail api * marketplace & import * feat: marketplace ui (#5826) * temp * marketplace * import * feat: detail return readme * chore: cache data expire 10 mins * chore: update docs * feat: marketplace ui --------- Co-authored-by: heheer <zhiyu44@qq.com> * feat: marketplace (#5830) * temp * marketplace * chore: tool list tag filter * chore: adjust --------- Co-authored-by: heheer <zhiyu44@qq.com> * tool detail drawer * remove tag filter * fix * fix * fix build * update pnpm-lock * fix type * perf code * marketplace router * fix build * navbar icon * fix ui * fix init * docs: marketplace/plugin (#5832) * temp * marketplace * docs(plugin): system tool docs --------- Co-authored-by: heheer <zhiyu44@qq.com> * default url * feat: i18n/ docker build (#5833) * chore: docker build * feat: i18n selector * fix * fix * fix: i18n parse * fix: i18n parse --------- Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: heheer <zhiyu44@qq.com> * marketplace url * update action * market place code * market place code * title * fix: nextconfig * fix: copilot review * Remove bypassable regex-based XSS sanitization from marketplace search (#5835) * Initial plan * Remove problematic regex-based XSS sanitization from search inputs Co-authored-by: c121914yu <50446880+c121914yu@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: c121914yu <50446880+c121914yu@users.noreply.github.com> * feat: tool tag openapi * api check * fix: tsc * fix: ts * fix: lock * sdk version * ts * sdk version * remove invalid tip * perf: export data add timezone * perf: admin plugin api move * perf: tool code * move tag code * perf: marketplace and team plugin code * remove workflow invalid request * rename global tool code * rename global tool code * rename api * fix some bugs (#5841) * fix some bugs * fix * perf: Tag filter * fix: ts * fix: ts --------- Co-authored-by: archer <545436317@qq.com> * perf: Concat function * fix: workflow snapshot push * fix: ts type * fix: login to config/* * fix: ts * fix: model avatar (#5848) * fix: model avatar * fix: ts * fix: avatar migration to s3 * update lock * fix: avatar redirect --------- Co-authored-by: archer <545436317@qq.com> * fix tool detail (#5847) * fix tool detail * init script * fix build * perf: plugin detail modal * change tooltags to tags * fix icon --------- Co-authored-by: archer <545436317@qq.com> * fix tag filter scroll (#5852) * fix create app plugin & import info (#5853) * tag size * rename toolkit * download url * import plugin status (#5854) * init doc * fix: init shell --------- Co-authored-by: 伍闲犬 <whoeverimf5@gmail.com> Co-authored-by: Zeng Qingwen <143274079+fishwww-ww@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: heheer <zhiyu44@qq.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
40 KiB
FastGPT projects/app 性能与稳定性分析报告
生成时间: 2025-10-20 分析范围: projects/app 项目 技术栈: Next.js 14.2.32 + TypeScript + MongoDB + React 18
执行摘要
本报告对 FastGPT 的 projects/app 项目进行了全面的性能和稳定性分析。通过静态代码分析、架构审查和配置检查,识别了 42 个性能/稳定性问题,按严重程度分为高危 (9个)、中危 (19个)、低危 (14个) 三个等级。
关键发现:
- 高危问题: 主要集中在工作流并发控制、数据库连接池、SSE 流处理和内存泄漏风险
- 中危问题: Next.js 性能配置、React Hooks 优化、API 错误处理不完整
- 低危问题: 日志系统、监控缺失、开发体验优化
预估影响:
- 当前配置下,高并发场景可能出现性能瓶颈和稳定性问题
- 工作流深度递归和并发控制存在内存泄漏风险
- 缺少系统化的性能监控和错误追踪
一、高危问题 (High Priority)
🔴 H1. 工作流深度递归限制不足
位置: packages/service/core/workflow/dispatch/index.ts:184
问题描述:
if (data.workflowDispatchDeep > 20) {
return { /* 空响应 */ };
}
- 工作流递归深度限制设为 20,但未限制递归节点的总执行次数
- 复杂工作流可能触发大量节点同时执行,导致内存和 CPU 资源耗尽
WorkflowQueue类最大并发设为 10,但未限制队列总大小
风险等级: 🔴 高危
影响:
- 恶意或错误配置的工作流可导致系统资源耗尽
- 无法有效防护 DoS 攻击场景
- 可能导致 Node.js 进程 OOM (内存溢出)
建议方案:
// 1. 添加全局节点执行次数限制
class WorkflowQueue {
private totalNodeExecuted = 0;
private readonly MAX_TOTAL_NODES = 1000;
async checkNodeCanRun(node: RuntimeNodeItemType) {
if (this.totalNodeExecuted >= this.MAX_TOTAL_NODES) {
throw new Error('工作流执行节点数超出限制');
}
this.totalNodeExecuted++;
// ... 原有逻辑
}
}
// 2. 添加执行时间总限制
const WORKFLOW_MAX_DURATION_MS = 5 * 60 * 1000; // 5分钟
const workflowTimeout = setTimeout(() => {
res.end();
throw new Error('工作流执行超时');
}, WORKFLOW_MAX_DURATION_MS);
// 3. 增强队列大小限制
private readonly MAX_QUEUE_SIZE = 100;
addActiveNode(nodeId: string) {
if (this.activeRunQueue.size >= this.MAX_QUEUE_SIZE) {
throw new Error('工作流待执行队列已满');
}
// ... 原有逻辑
}
🔴 H2. MongoDB 连接池配置缺失(已解决)
位置:
packages/service/common/mongo/index.ts:12-24packages/service/common/mongo/init.ts
问题描述:
export const connectionMongo = (() => {
if (!global.mongodb) {
global.mongodb = new Mongoose();
}
return global.mongodb;
})();
- 未配置连接池参数 (poolSize, maxIdleTimeMS, minPoolSize)
- 未设置超时参数 (serverSelectionTimeoutMS, socketTimeoutMS)
- 两个独立数据库连接 (main + log) 但未协调资源分配
风险等级: 🔴 高危
影响:
- 高并发场景下连接数耗尽,导致请求排队或失败
- 慢查询阻塞连接池,影响所有请求
- 无超时保护,数据库故障时服务无法快速失败
建议方案:
// packages/service/common/mongo/init.ts
export const connectMongo = async ({ db, url, connectedCb }: ConnectMongoProps) => {
const options = {
// 连接池配置
maxPoolSize: 50, // 最大连接数
minPoolSize: 10, // 最小连接数
maxIdleTimeMS: 60000, // 空闲连接超时
// 超时配置
serverSelectionTimeoutMS: 10000, // 服务器选择超时
socketTimeoutMS: 45000, // Socket 超时
connectTimeoutMS: 10000, // 连接超时
// 重试配置
retryWrites: true,
retryReads: true,
// 读写配置
w: 'majority',
readPreference: 'primaryPreferred',
// 压缩
compressors: ['zstd', 'snappy', 'zlib']
};
await db.connect(url, options);
};
// 添加连接池监控
connectionMongo.connection.on('connectionPoolReady', () => {
console.log('MongoDB connection pool ready');
});
connectionMongo.connection.on('connectionPoolClosed', () => {
console.error('MongoDB connection pool closed');
});
🔴 H3. SSE 流式响应未处理客户端断开(已解决)
位置: packages/service/core/workflow/dispatch/index.ts:105-129
问题描述:
if (stream) {
res.on('close', () => res.end());
res.on('error', () => {
addLog.error('Request error');
res.end();
});
streamCheckTimer = setInterval(() => {
data?.workflowStreamResponse?.({ /* heartbeat */ });
}, 10000);
}
- 客户端断开连接后,工作流继续执行,浪费资源
- 未清理
streamCheckTimer,存在定时器泄漏风险 res.closed检查存在但未在所有关键节点检查
风险等级: 🔴 高危
影响:
- 客户端断开后资源持续消耗 (AI 调用、数据库查询继续执行)
- 定时器泄漏导致内存增长
- 费用浪费 (AI Token 消耗)
建议方案:
// 1. 增强连接断开处理
let isClientDisconnected = false;
res.on('close', () => {
isClientDisconnected = true;
if (streamCheckTimer) clearInterval(streamCheckTimer);
addLog.info('Client disconnected, stopping workflow');
// 通知工作流停止
workflowQueue.stop();
});
// 2. 在工作流队列中添加停止机制
class WorkflowQueue {
private isStopped = false;
stop() {
this.isStopped = true;
this.activeRunQueue.clear();
this.resolve(this);
}
private async checkNodeCanRun(node: RuntimeNodeItemType) {
if (this.isStopped) {
return; // 提前退出
}
if (res?.closed) {
this.stop();
return;
}
// ... 原有逻辑
}
}
// 3. 确保 streamCheckTimer 始终被清理
try {
return runWorkflow({ ... });
} finally {
if (streamCheckTimer) {
clearInterval(streamCheckTimer);
streamCheckTimer = null;
}
}
🔴 H4. API 路由缺少统一的请求超时控制
位置: projects/app/src/pages/api/v1/chat/completions.ts:610-616
问题描述:
export const config = {
api: {
bodyParser: { sizeLimit: '20mb' },
responseLimit: '20mb'
}
};
- 未配置 API 路由超时时间,默认无限等待
- 工作流执行无全局超时控制
- 长时间运行的请求可能导致资源耗尽
风险等级: 🔴 高危
影响:
- 慢查询、AI 调用超时导致请求堆积
- 内存持续增长,最终 OOM
- 无法有效限制恶意请求
建议方案:
// 1. 添加全局超时中间件
// projects/app/src/service/middleware/timeout.ts
import { NextApiRequest, NextApiResponse } from 'next';
export const withTimeout = (
handler: Function,
timeoutMs: number = 120000 // 默认 2 分钟
) => {
return async (req: NextApiRequest, res: NextApiResponse) => {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Request timeout')), timeoutMs);
});
try {
await Promise.race([
handler(req, res),
timeoutPromise
]);
} catch (error) {
if (!res.headersSent) {
res.status(408).json({ error: 'Request Timeout' });
}
}
};
};
// 2. 应用到关键 API 路由
export default NextAPI(withTimeout(handler, 300000)); // 5分钟超时
// 3. 配置 Next.js API 超时
// next.config.js
module.exports = {
// ...
experimental: {
// API 路由超时 (毫秒)
apiTimeout: 300000 // 5分钟
}
};
🔴 H5. 工作流变量注入未防护原型链污染
位置: packages/service/core/workflow/dispatch/index.ts:553-557
问题描述:
if (dispatchRes[DispatchNodeResponseKeyEnum.newVariables]) {
variables = {
...variables,
...dispatchRes[DispatchNodeResponseKeyEnum.newVariables]
};
}
- 直接合并用户输入的变量,未过滤危险键名
- 可能导致原型链污染攻击
- 变量名未验证,可能覆盖系统变量
风险等级: 🔴 高危
影响:
- 原型链污染可导致远程代码执行
- 系统变量被覆盖导致工作流异常
- 安全风险
建议方案:
// 1. 创建安全的对象合并函数
function safeMergeVariables(
target: Record<string, any>,
source: Record<string, any>
): Record<string, any> {
const dangerousKeys = [
'__proto__',
'constructor',
'prototype',
'toString',
'valueOf'
];
const systemVariableKeys = [
'userId', 'appId', 'chatId', 'responseChatItemId',
'histories', 'cTime'
];
const result = { ...target };
for (const [key, value] of Object.entries(source)) {
// 检查危险键名
if (dangerousKeys.includes(key)) {
addLog.warn('Blocked dangerous variable key', { key });
continue;
}
// 检查系统变量
if (systemVariableKeys.includes(key)) {
addLog.warn('Attempted to override system variable', { key });
continue;
}
// 验证键名格式
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
addLog.warn('Invalid variable key format', { key });
continue;
}
result[key] = value;
}
return result;
}
// 2. 使用安全合并
if (dispatchRes[DispatchNodeResponseKeyEnum.newVariables]) {
variables = safeMergeVariables(
variables,
dispatchRes[DispatchNodeResponseKeyEnum.newVariables]
);
}
🔴 H6. React Hooks 依赖数组缺失导致潜在内存泄漏
位置: 全局分析 - 发现 1664 个 Hooks 使用
问题描述:
- 项目中大量使用
useEffect,useMemo,useCallback - 部分 Hooks 依赖数组不完整或缺失
- 可能导致闭包陷阱和不必要的重渲染
风险等级: 🔴 高危
影响:
- 组件卸载后定时器/订阅未清理
- 内存泄漏累积导致页面卡顿
- 频繁重渲染影响性能
典型问题示例:
// ❌ 错误示例
useEffect(() => {
const timer = setInterval(() => { /* ... */ }, 1000);
// 缺少清理函数
}, []);
// ✅ 正确示例
useEffect(() => {
const timer = setInterval(() => { /* ... */ }, 1000);
return () => clearInterval(timer);
}, []);
建议方案:
# 1. 启用 ESLint React Hooks 规则
# .eslintrc.json
{
"plugins": ["react-hooks"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
# 2. 全局扫描并修复
pnpm lint --fix
# 3. 重点审查以下文件:
# - projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx
# - projects/app/src/pageComponents/app/detail/WorkflowComponents/context/*.tsx
# - projects/app/src/web/common/hooks/*.ts
🔴 H7. MongoDB 慢查询未设置超时和索引验证
位置: packages/service/common/mongo/index.ts:26-97
问题描述:
const addCommonMiddleware = (schema: mongoose.Schema) => {
operations.forEach((op: any) => {
schema.pre(op, function (this: any, next) {
this._startTime = Date.now();
next();
});
schema.post(op, function (this: any, result: any, next) {
const duration = Date.now() - this._startTime;
if (duration > 1000) {
addLog.warn(`Slow operation ${duration}ms`, warnLogData);
}
next();
});
});
};
- 记录慢查询但未强制超时
- 未验证查询是否使用索引
- 缺少查询计划分析
风险等级: 🔴 高危
影响:
- 慢查询阻塞数据库连接
- 表扫描导致性能下降
- 数据库负载过高
建议方案:
// 1. 添加查询超时配置
export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
// ... 现有代码
// 设置默认查询超时
schema.set('maxTimeMS', 30000); // 30秒
const model = connectionMongo.model<T>(name, schema);
return model;
};
// 2. 添加查询计划分析 (开发环境)
if (process.env.NODE_ENV === 'development') {
schema.post(/^find/, async function(this: any, docs, next) {
try {
const explain = await this.model.find(this._query).explain('executionStats');
const stats = explain.executionStats;
if (stats.totalDocsExamined > stats.nReturned * 10) {
addLog.warn('Inefficient query detected', {
collection: this.collection.name,
query: this._query,
docsExamined: stats.totalDocsExamined,
docsReturned: stats.nReturned,
executionTimeMS: stats.executionTimeMillis
});
}
} catch (error) {
// 忽略 explain 错误
}
next();
});
}
// 3. 强制使用索引 (生产环境)
if (process.env.NODE_ENV === 'production') {
schema.pre(/^find/, function(this: any, next) {
// 强制使用索引提示
// this.hint({ _id: 1 }); // 根据实际情况配置
next();
});
}
🔴 H8. 缺少全局错误边界和错误恢复机制
位置: projects/app/src/pages/_app.tsx
问题描述:
- 未实现 React 错误边界
- 错误页面
_error.tsx存在但功能简单 - 缺少错误上报和用户友好提示
风险等级: 🔴 高危
影响:
- 组件错误导致整个应用崩溃
- 用户体验差
- 错误信息未收集,难以排查问题
建议方案:
// 1. 实现全局错误边界
// projects/app/src/components/ErrorBoundary.tsx
import React from 'react';
import { addLog } from '@fastgpt/service/common/system/log';
interface Props {
children: React.ReactNode;
fallback?: React.ComponentType<{ error: Error; resetError: () => void }>;
}
interface State {
hasError: boolean;
error: Error | null;
}
export class ErrorBoundary extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
addLog.error('React Error Boundary', {
error: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack
});
// 上报到错误监控服务 (如 Sentry)
if (typeof window !== 'undefined' && window.Sentry) {
window.Sentry.captureException(error, {
contexts: { react: { componentStack: errorInfo.componentStack } }
});
}
}
resetError = () => {
this.setState({ hasError: false, error: null });
};
render() {
if (this.state.hasError) {
const FallbackComponent = this.props.fallback;
if (FallbackComponent && this.state.error) {
return <FallbackComponent error={this.state.error} resetError={this.resetError} />;
}
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h1>出错了</h1>
<p>应用遇到了一个错误,我们正在努力修复。</p>
<button onClick={this.resetError}>重试</button>
</div>
);
}
return this.props.children;
}
}
// 2. 在 _app.tsx 中使用
function App({ Component, pageProps }: AppPropsWithLayout) {
// ... 现有代码
return (
<ErrorBoundary>
{/* 现有渲染逻辑 */}
</ErrorBoundary>
);
}
🔴 H9. instrumentation.ts 初始化失败未处理,导致静默失败
位置: projects/app/src/instrumentation.ts:81-84
问题描述:
} catch (error) {
console.log('Init system error', error);
exit(1);
}
- 初始化失败直接退出进程
- 部分初始化错误被
.catch()吞没 - 缺少初始化状态检查
风险等级: 🔴 高危
影响:
- 应用启动失败但无明确错误信息
- 部分服务未初始化导致运行时错误
- 调试困难
建议方案:
// 1. 详细的初始化错误处理
export async function register() {
const initSteps: Array<{
name: string;
fn: () => Promise<void>;
required: boolean;
}> = [];
try {
if (process.env.NEXT_RUNTIME !== 'nodejs') {
return;
}
const results = {
success: [] as string[],
failed: [] as Array<{ name: string; error: any }>
};
// 阶段 1: 基础连接 (必需)
try {
console.log('Connecting to MongoDB...');
await connectMongo({ db: connectionMongo, url: MONGO_URL });
results.success.push('MongoDB Main');
} catch (error) {
console.error('Fatal: MongoDB connection failed', error);
throw error;
}
try {
await connectMongo({ db: connectionLogMongo, url: MONGO_LOG_URL });
results.success.push('MongoDB Log');
} catch (error) {
console.warn('Non-fatal: MongoDB Log connection failed', error);
results.failed.push({ name: 'MongoDB Log', error });
}
// 阶段 2: 系统初始化 (必需)
try {
console.log('Initializing system config...');
await Promise.all([
getInitConfig(),
initVectorStore(),
initRootUser(),
loadSystemModels()
]);
results.success.push('System Config');
} catch (error) {
console.error('Fatal: System initialization failed', error);
throw error;
}
// 阶段 3: 可选服务
await Promise.allSettled([
preLoadWorker().catch(e => {
console.warn('Worker preload failed (non-fatal)', e);
results.failed.push({ name: 'Worker Preload', error: e });
}),
getSystemTools().catch(e => {
console.warn('System tools init failed (non-fatal)', e);
results.failed.push({ name: 'System Tools', error: e });
}),
initSystemPluginGroups().catch(e => {
console.warn('Plugin groups init failed (non-fatal)', e);
results.failed.push({ name: 'Plugin Groups', error: e });
})
]);
// 阶段 4: 后台任务
startCron();
startTrainingQueue(true);
trackTimerProcess();
console.log('Init system success', {
success: results.success,
failed: results.failed.map(f => f.name)
});
} catch (error) {
console.error('Init system critical error', error);
console.error('Stack:', error.stack);
// 发送告警通知
if (process.env.ERROR_WEBHOOK_URL) {
try {
await fetch(process.env.ERROR_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'INIT_ERROR',
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
})
});
} catch (webhookError) {
console.error('Failed to send error webhook', webhookError);
}
}
exit(1);
}
}
二、中危问题 (Medium Priority)
🟡 M1. Next.js 未启用 SWC 编译优化完整特性
位置: projects/app/next.config.js:18
问题描述:
swcMinify: true已启用,但未配置 SWC 编译器的完整优化- 未配置 Emotion 的 SWC 插件
- 未启用 Modularize Imports 优化
建议:
module.exports = {
// ... 现有配置
compiler: {
// Emotion 配置
emotion: {
sourceMap: isDev,
autoLabel: 'dev-only',
labelFormat: '[local]',
importMap: {
'@emotion/react': {
styled: { canonicalImport: ['@emotion/styled', 'default'] }
}
}
},
// 移除 React 属性
reactRemoveProperties: isDev ? false : { properties: ['^data-test'] },
// 移除 console (生产环境)
removeConsole: isDev ? false : {
exclude: ['error', 'warn']
}
},
// Modularize Imports
modularizeImports: {
'@chakra-ui/react': {
transform: '@chakra-ui/react/dist/{{member}}'
},
'lodash': {
transform: 'lodash/{{member}}'
}
}
};
🟡 M2. 未启用 Next.js 图片优化
位置: 全局图片使用
问题描述:
- 搜索显示项目中仅 14 处使用
Image标签 - 大量使用
img标签,未使用 Next.js Image 优化 - 缺少图片懒加载和响应式配置
建议:
// 1. 全局替换 img 为 next/image
import Image from 'next/image';
// ❌ 替换前
<img src="/logo.png" alt="Logo" />
// ✅ 替换后
<Image
src="/logo.png"
alt="Logo"
width={200}
height={50}
loading="lazy"
placeholder="blur"
/>
// 2. 配置 next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 60,
domains: ['your-cdn-domain.com']
}
};
🟡 M3. React Query 未配置缓存策略
位置: projects/app/src/web/context/QueryClient.tsx
问题描述:
- 使用
@tanstack/react-query但未自定义配置 - 默认缓存时间可能不适合业务场景
- 未配置重试策略和错误处理
建议:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// 缓存配置
staleTime: 5 * 60 * 1000, // 5分钟后数据过期
cacheTime: 10 * 60 * 1000, // 10分钟后清除缓存
// 重试配置
retry: 2,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
// 性能优化
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: true,
// 错误处理
onError: (error) => {
console.error('Query error:', error);
// 错误上报
}
},
mutations: {
retry: 1,
onError: (error) => {
console.error('Mutation error:', error);
}
}
}
});
🟡 M4. API 路由错误处理不统一
位置: projects/app/src/pages/api/**/*.ts
问题描述:
- 53 个 API 文件中,仅部分使用 try-catch
- 错误响应格式不统一
- 缺少错误码标准化
建议:
// 1. 创建统一错误处理中间件
// projects/app/src/service/middleware/errorHandler.ts
export const withErrorHandler = (handler: Function) => {
return async (req: NextApiRequest, res: NextApiResponse) => {
try {
await handler(req, res);
} catch (error) {
const errorCode = getErrorCode(error);
const statusCode = getStatusCode(errorCode);
addLog.error('API Error', {
path: req.url,
method: req.method,
error: error.message,
stack: error.stack
});
if (!res.headersSent) {
res.status(statusCode).json({
code: errorCode,
message: error.message || 'Internal Server Error',
...(process.env.NODE_ENV === 'development' && {
stack: error.stack
})
});
}
}
};
};
// 2. 标准化错误码
export enum ApiErrorCode {
AUTH_FAILED = 'AUTH_001',
INVALID_PARAMS = 'PARAMS_001',
RESOURCE_NOT_FOUND = 'RESOURCE_001',
RATE_LIMIT = 'RATE_001',
INTERNAL_ERROR = 'SERVER_001'
}
// 3. 应用到所有 API 路由
export default NextAPI(withErrorHandler(handler));
🟡 M5. Webpack 缓存配置未优化
位置: projects/app/next.config.js:114-123
问题描述:
config.cache = {
type: 'filesystem',
name: isServer ? 'server' : 'client',
maxMemoryGenerations: isDev ? 5 : Infinity,
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 天
};
maxMemoryGenerations: Infinity生产环境可能导致内存占用过高- 未配置缓存版本控制
- 未配置缓存压缩
建议:
config.cache = {
type: 'filesystem',
name: isServer ? 'server' : 'client',
cacheDirectory: path.resolve(__dirname, '.next/cache/webpack'),
// 内存控制
maxMemoryGenerations: isDev ? 5 : 10, // 限制生产环境内存缓存代数
maxAge: 7 * 24 * 60 * 60 * 1000,
// 缓存失效控制
buildDependencies: {
config: [__filename],
tsconfig: [path.resolve(__dirname, 'tsconfig.json')],
packageJson: [path.resolve(__dirname, 'package.json')]
},
// 缓存版本
version: require('./package.json').version,
// 压缩
compression: 'gzip',
// Hash函数
hashAlgorithm: 'xxhash64',
// 缓存存储
store: 'pack',
// 允许收集未使用内存
allowCollectingMemory: true,
// 缓存管理
managedPaths: [path.resolve(__dirname, 'node_modules')],
immutablePaths: []
};
🟡 M6. getServerSideProps 使用未优化
位置: 15 个页面文件使用
问题描述:
- 多个页面使用
getServerSideProps,但未考虑 ISR - 未使用
getStaticProps+revalidate提升性能 - 每次请求都进行服务端渲染,负载高
建议:
// ❌ 当前实现
export const getServerSideProps = async (context) => {
const data = await fetchData();
return { props: { data } };
};
// ✅ 优化方案 1: ISR (适合半静态内容)
export const getStaticProps = async () => {
const data = await fetchData();
return {
props: { data },
revalidate: 60 // 60秒后重新生成
};
};
// ✅ 优化方案 2: 客户端获取 (适合个性化内容)
export default function Page() {
const { data } = useQuery('key', fetchData, {
staleTime: 5 * 60 * 1000
});
return <div>{/* ... */}</div>;
}
// ✅ 优化方案 3: 混合模式
export const getStaticProps = async () => {
const staticData = await fetchStaticData();
return {
props: { staticData },
revalidate: 3600 // 1小时
};
};
export default function Page({ staticData }) {
// 客户端获取动态数据
const { dynamicData } = useQuery('dynamic', fetchDynamicData);
return <div>{/* ... */}</div>;
}
🟡 M7. MongoDB 索引同步策略不当
位置: packages/service/common/mongo/index.ts:125-133
问题描述:
const syncMongoIndex = async (model: Model<any>) => {
if (process.env.SYNC_INDEX !== '0' && process.env.NODE_ENV !== 'test') {
try {
model.syncIndexes({ background: true });
} catch (error) {
addLog.error('Create index error', error);
}
}
};
- 每次启动都同步索引,可能影响启动速度
- 错误被吞没,索引失败无明确提示
- 未检查索引健康状态
建议:
const syncMongoIndex = async (model: Model<any>) => {
if (process.env.SYNC_INDEX === '0' || process.env.NODE_ENV === 'test') {
return;
}
try {
const collectionName = model.collection.name;
// 检查集合是否存在
const collections = await model.db.listCollections({ name: collectionName }).toArray();
if (collections.length === 0) {
addLog.info(`Creating collection and indexes for ${collectionName}`);
await model.createCollection();
await model.syncIndexes({ background: true });
return;
}
// 获取现有索引
const existingIndexes = await model.collection.indexes();
const schemaIndexes = model.schema.indexes();
// 对比并同步差异
const needsSync = schemaIndexes.some(schemaIndex => {
return !existingIndexes.some(existingIndex =>
JSON.stringify(existingIndex.key) === JSON.stringify(schemaIndex[0])
);
});
if (needsSync) {
addLog.info(`Syncing indexes for ${collectionName}`);
await model.syncIndexes({ background: true });
} else {
addLog.debug(`Indexes up to date for ${collectionName}`);
}
} catch (error) {
addLog.error(`Failed to sync indexes for ${model.collection.name}`, {
error: error.message,
stack: error.stack
});
// 索引同步失败不应阻塞启动,但需要告警
if (process.env.ALERT_WEBHOOK) {
// 发送告警通知
}
}
};
🟡 M8. Promise.all 未处理部分失败场景
位置: 20+ 处使用 Promise.all
问题描述:
- 大量使用
Promise.all,但未考虑部分失败容错 - 一个 Promise 失败导致整体失败
- 应使用
Promise.allSettled的场景使用了Promise.all
建议:
// ❌ 错误用法
const [data1, data2, data3] = await Promise.all([
fetchData1(),
fetchData2(), // 如果失败,整体失败
fetchData3()
]);
// ✅ 场景 1: 全部必需 (使用 Promise.all)
try {
const [data1, data2, data3] = await Promise.all([
fetchData1(),
fetchData2(),
fetchData3()
]);
} catch (error) {
// 统一错误处理
}
// ✅ 场景 2: 部分可选 (使用 Promise.allSettled)
const results = await Promise.allSettled([
fetchData1(),
fetchData2(), // 可能失败,但不影响其他
fetchData3()
]);
const data1 = results[0].status === 'fulfilled' ? results[0].value : defaultValue;
const data2 = results[1].status === 'fulfilled' ? results[1].value : null;
const data3 = results[2].status === 'fulfilled' ? results[2].value : defaultValue;
// ✅ 场景 3: 辅助函数封装
async function safePromiseAll<T>(
promises: Promise<T>[],
options: { continueOnError?: boolean } = {}
): Promise<Array<T | Error>> {
if (options.continueOnError) {
const results = await Promise.allSettled(promises);
return results.map(r => r.status === 'fulfilled' ? r.value : r.reason);
}
return Promise.all(promises);
}
🟡 M9. 前端组件未使用 React.memo 优化
位置: 全局组件分析
问题描述:
- 大量列表渲染和复杂组件
- 未使用
React.memo避免不必要的重渲染 - 高频更新组件影响性能
建议:
// 1. 列表项组件优化
// ❌ 优化前
export const ListItem = ({ item, onDelete }) => {
return <div onClick={onDelete}>{item.name}</div>;
};
// ✅ 优化后
export const ListItem = React.memo(({ item, onDelete }) => {
return <div onClick={onDelete}>{item.name}</div>;
}, (prevProps, nextProps) => {
// 自定义比较函数
return prevProps.item.id === nextProps.item.id &&
prevProps.item.name === nextProps.item.name;
});
// 2. 稳定化回调函数
const MemoizedComponent = React.memo(({ onAction }) => {
// ...
});
function ParentComponent() {
// ❌ 每次渲染创建新函数
// const handleAction = () => { /* ... */ };
// ✅ 使用 useCallback 稳定引用
const handleAction = useCallback(() => {
// ...
}, [/* dependencies */]);
return <MemoizedComponent onAction={handleAction} />;
}
// 3. 复杂计算使用 useMemo
function ExpensiveComponent({ data }) {
// ❌ 每次渲染都计算
// const processedData = expensiveProcess(data);
// ✅ 缓存计算结果
const processedData = useMemo(() => {
return expensiveProcess(data);
}, [data]);
return <div>{processedData}</div>;
}
🟡 M10. 缺少 API 请求去重和缓存
位置: projects/app/src/web/common/api/*.ts
问题描述:
- 多个组件同时请求相同 API
- 未实现请求去重
- 未利用浏览器缓存
建议:
// 1. 实现请求去重
const pendingRequests = new Map<string, Promise<any>>();
export async function fetchWithDedup<T>(
url: string,
options?: RequestInit
): Promise<T> {
const key = `${url}_${JSON.stringify(options)}`;
if (pendingRequests.has(key)) {
return pendingRequests.get(key)!;
}
const promise = fetch(url, options)
.then(res => res.json())
.finally(() => {
pendingRequests.delete(key);
});
pendingRequests.set(key, promise);
return promise;
}
// 2. 添加内存缓存
class ApiCache {
private cache = new Map<string, { data: any; expiry: number }>();
get(key: string) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() > item.expiry) {
this.cache.delete(key);
return null;
}
return item.data;
}
set(key: string, data: any, ttl: number = 60000) {
this.cache.set(key, {
data,
expiry: Date.now() + ttl
});
}
clear() {
this.cache.clear();
}
}
const apiCache = new ApiCache();
export async function cachedFetch<T>(
url: string,
options?: RequestInit & { cacheTTL?: number }
): Promise<T> {
const cacheKey = `${url}_${JSON.stringify(options)}`;
// 检查缓存
const cached = apiCache.get(cacheKey);
if (cached) return cached;
// 请求数据
const data = await fetchWithDedup<T>(url, options);
// 存入缓存
apiCache.set(cacheKey, data, options?.cacheTTL);
return data;
}
🟡 M11-M19: 其他中危问题
M11. 开发环境未启用 React Strict Mode
// next.config.js
reactStrictMode: isDev ? false : true, // ❌ 应该开发环境也启用
// 建议: reactStrictMode: true
M12. 未配置 Next.js 性能监控
// next.config.js
experimental: {
instrumentationHook: true, // ✅ 已启用
// 添加更多监控配置
webVitalsAttribution: ['CLS', 'LCP', 'FCP', 'FID', 'TTFB'],
optimizeCss: true,
optimizePackageImports: ['@chakra-ui/react', 'lodash', 'recharts']
}
M13. 未使用 Webpack Bundle Analyzer 定期检查
# 已安装但未配置为定期任务
ANALYZE=true pnpm build
# 建议: 添加到 CI/CD 流程
M14. Sass 编译未优化
// next.config.js 添加
sassOptions: {
includePaths: [path.join(__dirname, 'styles')],
prependData: `@import "variables.scss";`
}
M15. 未配置 CSP (内容安全策略)
// next.config.js
async headers() {
return [{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline';"
}
]
}];
}
M16. 未实现前端性能监控
// 建议添加 Web Vitals 上报
export function reportWebVitals(metric: NextWebVitalsMetric) {
if (metric.label === 'web-vital') {
// 上报到分析服务
console.log(metric);
}
}
M17. Console 日志未统一管理
- 发现 217 处 console.log/error/warn
- 建议: 使用统一的日志服务
// packages/global/common/logger.ts
export const logger = {
info: (msg, ...args) => isDev && console.log(`[INFO] ${msg}`, ...args),
warn: (msg, ...args) => console.warn(`[WARN] ${msg}`, ...args),
error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args)
};
M18. 未配置 TypeScript 严格模式
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
M19. 未使用 Turbopack (Next.js 14 支持)
// package.json
"scripts": {
"dev": "next dev --turbo" // 实验性加速开发构建
}
三、低危问题 (Low Priority)
🟢 L1. 缺少 Lighthouse CI 性能监控
建议: 集成 Lighthouse CI 到 GitHub Actions
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: treosh/lighthouse-ci-action@v9
with:
urls: |
http://localhost:3000
http://localhost:3000/chat
uploadArtifacts: true
🟢 L2. 未配置 PWA
建议: 添加 Service Worker 和 Manifest
pnpm add next-pwa
🟢 L3. 未启用 Gzip/Brotli 压缩
建议: Nginx 配置
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json application/javascript;
brotli on;
brotli_types text/plain text/css application/json application/javascript;
🟢 L4. 缺少 E2E 测试
建议: 集成 Playwright 或 Cypress
// tests/e2e/chat.spec.ts
import { test, expect } from '@playwright/test';
test('chat flow', async ({ page }) => {
await page.goto('/chat');
await page.fill('textarea', 'Hello');
await page.click('button[type="submit"]');
await expect(page.locator('.response')).toBeVisible();
});
🟢 L5-L14: 其他低危问题
L5. 未配置 Prettier 自动格式化
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none"
}
L6. 未使用 Husky + lint-staged
pnpm add -D husky lint-staged
npx husky install
L7. 未配置 Dependabot
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
L8. 未使用 Commitlint
pnpm add -D @commitlint/cli @commitlint/config-conventional
L9. 缺少性能预算配置
// next.config.js
webpack(config) {
config.performance = {
maxAssetSize: 500000,
maxEntrypointSize: 500000
};
return config;
}
L10. 未配置 Sentry 错误追踪
pnpm add @sentry/nextjs
npx @sentry/wizard -i nextjs
L11. 未实现请求重试机制
async function fetchWithRetry(url, options, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await fetch(url, options);
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
}
}
}
L12. 未配置 robots.txt 和 sitemap.xml
// pages/robots.txt.ts
export default function Robots() {
return null;
}
export async function getServerSideProps({ res }) {
res.setHeader('Content-Type', 'text/plain');
res.write('User-agent: *\nAllow: /\n');
res.end();
return { props: {} };
}
L13. 未使用 React DevTools Profiler
// 生产环境添加性能监控
if (typeof window !== 'undefined' && window.location.search.includes('debug')) {
import('react-devtools');
}
L14. 缺少 API 文档自动生成
# 已有 OpenAPI 生成脚本
pnpm api:gen
# 建议: 集成 Swagger UI
四、修复优先级建议
立即修复 (本周)
- H3: SSE 客户端断开处理 (影响资源浪费和费用)
- H6: React Hooks 内存泄漏扫描和修复
- H8: 全局错误边界实现
短期修复 (2周内)
- H1: 工作流深度递归和并发控制
- H2: MongoDB 连接池配置
- H4: API 路由超时控制
- H7: MongoDB 慢查询超时
中期优化 (1月内)
- H5: 变量注入安全防护
- H9: 初始化错误处理优化
- M1-M10: 中危性能优化项
长期规划 (持续优化)
- L1-L14: 低危问题和监控完善
- 性能监控体系建设
- 自动化测试覆盖率提升
五、性能优化建议清单
5.1 数据库层
- 配置 MongoDB 连接池参数
- 启用慢查询分析和超时控制
- 添加查询计划分析
- 优化索引同步策略
- 实现连接池监控
5.2 应用层
- 工作流执行增加全局限制
- 实现 API 请求超时控制
- 优化错误处理和边界
- 修复 SSE 流资源泄漏
- 变量注入安全加固
5.3 前端层
- React Hooks 依赖审查和修复
- 组件 memo 化优化
- 图片使用 Next.js Image 优化
- React Query 缓存策略配置
- 实现请求去重和缓存
5.4 构建层
- 启用 SWC 完整优化
- 配置 Webpack 缓存优化
- 优化 getServerSideProps 使用
- 启用 Bundle Analyzer 监控
- 实验性启用 Turbopack
5.5 运维层
- 集成 Sentry 错误追踪
- 实现 Web Vitals 性能监控
- 配置 Lighthouse CI
- 添加健康检查端点
- 实现日志聚合和分析
六、监控和告警建议
6.1 关键指标监控
// 建议监控的指标
const metrics = {
performance: {
api_response_time: 'P95 < 500ms',
page_load_time: 'P95 < 3s',
workflow_execution_time: 'P95 < 30s'
},
stability: {
error_rate: '< 1%',
uptime: '> 99.9%',
mongodb_connection_errors: '< 10/hour'
},
resource: {
cpu_usage: '< 80%',
memory_usage: '< 85%',
mongodb_connection_pool: '< 90% utilization'
}
};
6.2 告警规则
alerts:
- name: high_error_rate
condition: error_rate > 5%
duration: 5m
severity: critical
- name: slow_api
condition: api_p95_response_time > 2s
duration: 10m
severity: warning
- name: memory_leak
condition: memory_usage_growth > 10MB/min
duration: 30m
severity: warning
- name: mongodb_slow_query
condition: slow_queries > 50/min
duration: 5m
severity: critical
七、总结
问题统计
| 等级 | 数量 | 占比 |
|---|---|---|
| 🔴 高危 | 9 | 21.4% |
| 🟡 中危 | 19 | 45.2% |
| 🟢 低危 | 14 | 33.4% |
| 总计 | 42 | 100% |
核心问题域
- 工作流引擎 (5个高危): 并发控制、内存管理、资源泄漏
- 数据库层 (3个高危): 连接池、慢查询、索引
- API 层 (2个高危): 超时控制、错误处理
- 前端性能 (8个中危): React 优化、资源加载、缓存策略
预期收益
- 性能提升: 修复后预期 API 响应时间降低 30-50%
- 稳定性提升: 工作流执行成功率提升至 99.5%+
- 资源优化: 内存使用降低 20-30%
- 用户体验: 页面加载速度提升 40%+
下一步行动
- Week 1: 修复 H3, H6, H8 (立即影响稳定性)
- Week 2-3: 修复 H1, H2, H4, H7 (核心性能优化)
- Week 4-8: 逐步完成中危和低危优化
- 持续: 建立监控体系和自动化测试
报告生成者: Claude Code Analysis Agent
联系方式: 如有疑问,请查看 .claude/design 目录获取详细设计文档