1. 应用启动入口(main.ts)
1.1 启动函数bootstrap
应用的启动入口位于 ,通过 bootstrap()
函数完成整个应用的初始化:
typescript
async function bootstrap() {
// const plugins = await loadAllPlugins();
// const dynamicAppModule = await AppModule.register(plugins);
const dynamicAppModule = await AppModule.register();
// 确保端口是数字类型
const port = process.env.SERVER_PORT ? parseInt(process.env.SERVER_PORT, 10) : 4090;
// 创建日志服务实例
const appLogger = LoggerModule.createLogger(appConfig.name);
const app = await NestFactory.create<NestExpressApplication>(dynamicAppModule, {
logger: appLogger,
});
bodyParserXml(bodyParser);
app.use(
bodyParser.xml({
limit: "1mb", // 请求体最大限制
xmlParseOptions: {
explicitArray: false, // 不把所有子节点解析成数组
},
}),
);
// 初始化全局容器
setGlobalContainer(app);
// 配置cookie解析器
app.use(cookieParser());
// 配置跨域
const corsEnabled = process.env.SERVER_CORS_ENABLED === "true";
if (corsEnabled) {
app.enableCors({
origin: process.env.SERVER_CORS_ORIGIN || "*",
credentials: true,
});
appLogger.log(
`已启用跨域(CORS),允许来源: ${process.env.SERVER_CORS_ORIGIN || "*"}`,
"Bootstrap",
);
}
// 设置静态资源目录
await setAssetsDir(app);
// 启用全局验证管道
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}),
);
// 注册全局响应拦截器
app.useGlobalInterceptors(
new TransformInterceptor(app.get(Reflector), app.get(FileService)),
new HttpLoggerInterceptor(appLogger),
);
// 注册全局异常过滤器
app.useGlobalFilters(new HttpExceptionFilter());
// 尝试监听端口,如果被占用则尝试其他端口(仅在开发环境下)
tryListen(app, port, 3, startTime).catch((err) => {
console.error("启动服务失败:", err);
process.exit(1);
});
}
1.2 全局配置设置
在应用创建后,进行以下全局配置:
- Cookie解析器配置 :
app.use(cookieParser())
- CORS跨域配置 :
app.enableCors()
- 静态资源目录设置 :调用
setAssetsDir(app)
配置静态文件服务 - 全局验证管道 :
app.useGlobalPipes(new ValidationPipe())
- 全局响应拦截器 :注册
TransformInterceptor
和HttpLoggerInterceptor
- 全局异常过滤器 :注册
HttpExceptionFilter
1.3 异常处理机制
应用启动时设置了全局异常处理:
typescript
process.on('uncaughtException', (error) => {
appLogger.error('Uncaught Exception:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
appLogger.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});
2. 环境变量加载(env.util.ts)
2.1 环境变量配置文件
负责加载环境变量:
typescript
import * as dotenv from "dotenv";
import * as path from "path";
dotenv.config({
path: path.resolve(
path.resolve(__dirname, `../../../../../.env.${process.env.NODE_ENV}.local`),
),
});
2.2 环境变量文件规则
根据 NODE_ENV
环境变量加载对应的配置文件:
- 开发环境:
.env.development.local
- 生产环境:
.env.production.local
- 测试环境:
.env.test.local
3. 应用配置(app.config.ts)
3.1 配置接口定义
定义了应用的核心配置结构:
typescript
export interface AppConfig {
name: string; // 应用名称
version: string; // 应用版本
database: { // 数据库配置
type: "postgres";
host: string;
port: number;
username: string;
password: string;
database: string;
synchronize: boolean;
logging: boolean;
namingStrategy: NamingStrategyInterface;
};
}
3.2 配置实例化
配置通过环境变量进行实例化:
typescript
export const appConfig: AppConfig = {
name: process.env.APP_NAME || "FastbuildAI",
version: process.env.APP_VERSION || "unknown",
database: {
type: process.env.DB_TYPE as "postgres",
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT),
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
synchronize: process.env.DB_SYNCHRONIZE === "true",
logging: process.env.DB_LOGGING === "true",
namingStrategy: new SnakeNamingStrategy(),
},
};
4. 模块注册(app.module.ts)
4.1 AppModule.register()方法
的 register()
方法动态配置应用模块:
typescript
static register(): DynamicModule {
const imports = [
// 核心模块
DatabaseModule,
CacheModule,
LoggerModule,
PluginsCoreModule,
// 业务模块
SystemModule,
AuthModule.forRoot(),
UserModule,
AiModule,
WebModule,
ConsoleModule,
PermissionModule,
HealthModule,
HandlersModule,
];
// 静态文件服务配置
const publicPath = path.join(process.cwd(), "public");
const webPath = path.join(publicPath, "web");
const indexPath = path.join(webPath, "index.html");
if (fse.existsSync(webPath) && fse.existsSync(indexPath)) {
imports.push(
ServeStaticModule.forRoot({
rootPath: webPath,
exclude: ["/api*", "/consoleapi*"],
}),
);
}
return {
module: AppModule,
imports,
providers: [
// 全局守卫
{ provide: APP_GUARD, useClass: AuthGuard },
{ provide: APP_GUARD, useClass: PermissionsGuard },
],
};
}
4.2 模块导入顺序
模块按以下顺序导入:
- 核心基础模块:数据库、缓存、日志、插件系统
- 认证授权模块:用户认证、权限管理
- 业务功能模块:AI服务、Web接口、控制台接口
- 系统服务模块:健康检查、异常处理
- 静态文件服务:前端资源服务
5. 数据库初始化(database.module.ts)
5.1 数据库模块配置
使用TypeORM配置PostgreSQL数据库:
typescript
TypeOrmModule.forRootAsync({
imports: [],
inject: [],
useFactory: async () => {
const config = {
type: appConfig.database.type,
host: appConfig.database.host,
port: appConfig.database.port,
username: appConfig.database.username,
password: appConfig.database.password,
database: appConfig.database.database,
entities: getEntityPaths(),
synchronize: appConfig.database.synchronize,
logging: appConfig.database.logging,
namingStrategy: appConfig.database.namingStrategy,
logger: new CustomLogger(),
};
return config;
},
})
5.2 数据库初始化服务
负责数据库的初始化工作:
- 安装状态检查:检查系统是否已安装
- 超级管理员创建:创建默认管理员账户
- 菜单数据初始化:初始化系统菜单结构
- AI提供商配置:初始化AI服务提供商
- 数据库扩展配置:配置pgvector和zhparser扩展
5.3 实体模块注册
系统自动扫描并注册所有实体模块:
typescript
const getEntityModule = (entityPath: string): string => {
const segments = entityPath.split(path.sep);
const moduleIndex = segments.findIndex(segment => segment === 'modules');
if (moduleIndex !== -1 && moduleIndex + 1 < segments.length) {
return segments[moduleIndex + 1];
}
return 'unknown';
};
6. 缓存系统初始化(cache.module.ts)
6.1 缓存模块配置
配置内存缓存系统:
typescript
CacheModule.registerAsync({
isGlobal: true,
imports: [],
inject: [],
useFactory: () => {
return {
ttl: Number(process.env.CACHE_TTL) || 60 * 60 * 24, // 默认24小时
max: Number(process.env.CACHE_MAX_ITEMS) || 100, // 最大缓存项数
};
},
})
6.2 缓存服务功能
提供以下缓存操作:
get<T>(key: string)
: 获取缓存set(key: string, value: any, ttl?: number)
: 设置缓存del(key: string)
: 删除缓存reset()
: 重置所有缓存
7. 插件系统初始化(plugins.module.ts)
7.1 插件目录扫描
在启动时扫描插件目录:
typescript
export function getPluginsPath(): string {
return path.resolve(process.cwd(), "dist", "plugins");
}
export function getPluginFolders(): string[] {
const pluginsPath = getPluginsPath();
if (!fse.existsSync(pluginsPath)) {
return [];
}
return fse.readdirSync(pluginsPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
}
7.2 插件加载流程
插件加载包含以下步骤:
- 插件目录检查:验证插件目录是否存在
- 插件配置读取:读取每个插件的package.json配置
- 插件状态验证:检查插件是否启用
- 插件模块导入:动态导入启用的插件模块
- 插件注册:将插件注册到插件注册表
- 缓存更新:更新插件列表缓存
7.3 插件验证机制
系统对插件进行严格验证:
typescript
// 检查插件配置文件
const configPath = path.join(pluginDir, "package.json");
if (!(await fse.pathExists(configPath))) {
// 跳过无效插件
continue;
}
// 检查插件模块文件
const pluginModulePath = path.join(pluginDir, "main.plugin.js");
if (!(await fse.pathExists(pluginModulePath))) {
// 记录错误并跳过
continue;
}
8. 全局容器设置(global-container.util.ts)
8.1 全局容器管理
提供全局依赖注入容器:
typescript
let globalContainer: INestApplicationContext;
export function setGlobalContainer(container: INestApplicationContext): void {
globalContainer = container;
}
export function getGlobalContainer(): INestApplicationContext {
if (!globalContainer) {
throw new Error("Global container not initialized. Call setGlobalContainer first.");
}
return globalContainer;
}
export function getService<T>(serviceClass: new (...args: any[]) => T): T {
return getGlobalContainer().get(serviceClass);
}
8.2 容器初始化时机
全局容器在应用创建后立即设置:
typescript
// main.ts中的设置
const app = await NestFactory.create<NestExpressApplication>(AppModule.register());
setGlobalContainer(app);
9. 中间件和拦截器配置
9.1 HTTP异常过滤器(HttpExceptionFilter)
提供全局异常处理:
- 异常捕获:捕获所有HTTP异常
- 响应格式化:统一异常响应格式
- 日志记录:记录详细的异常信息
- 状态码映射:将HTTP状态码映射为业务状态码
9.2 响应转换拦截器(TransformInterceptor)
统一响应格式:
typescript
// 统一响应格式
return {
code: BusinessCode.SUCCESS,
message: "ok",
data: await this.buildFileUrl(data, context),
timestamp: Date.now(),
};
9.3 HTTP日志拦截器(HttpLoggerInterceptor)
记录HTTP请求日志:
- 请求信息记录:方法、路径、IP地址、User-Agent
- 响应时间统计:计算请求处理时间
- 状态码记录:记录HTTP响应状态码
- 请求体大小统计:计算请求体大小
10. 静态文件服务配置
10.1 静态资源目录设置
中的 setAssetsDir
函数配置静态资源:
typescript
export const setAssetsDir = async (app: NestExpressApplication) => {
// 设置静态资源目录
app.useStaticAssets(path.join(process.cwd(), "public"), {
prefix: "/",
});
app.useStaticAssets(path.join(process.cwd(), "uploads"), {
prefix: "/uploads",
});
app.useStaticAssets(path.join(process.cwd(), "static"), {
prefix: "/static",
});
};
10.2 前端应用服务
如果存在前端构建文件,系统会自动配置前端应用服务:
typescript
const publicPath = path.join(process.cwd(), "public");
const webPath = path.join(publicPath, "web");
const indexPath = path.join(webPath, "index.html");
if (fse.existsSync(webPath) && fse.existsSync(indexPath)) {
imports.push(
ServeStaticModule.forRoot({
rootPath: webPath,
exclude: ["/api*", "/consoleapi*"], // 排除API路径
}),
);
}
11. 端口监听和启动日志
11.1 端口监听机制
中的 tryListen
函数处理端口监听:
typescript
export const tryListen = async (
app: INestApplication,
port: number,
maxRetries: number = 10,
): Promise<void> => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
await app.listen(port);
startLog(port, startTime);
return;
} catch (error) {
if (isDevelopment() && error.code === "EADDRINUSE") {
port++;
if (attempt < maxRetries) {
TerminalLogger.warn("", `Port ${port - 1} is busy, trying port ${port}...`);
continue;
}
}
throw error;
}
}
};
11.2 启动日志输出
startLog
函数输出详细的启动信息:
typescript
export const startLog = (currentPort?: number, startTime?: number) => {
const port = currentPort ?? process.env.SERVER_PORT ?? 4090;
const env = process.env.NODE_ENV;
const nets = networkInterfaces();
// 输出应用信息
console.log(`App Name: ${appConfig.name}`);
console.log(`App Version: ${appConfig.version}`);
console.log(`Environment: ${env}`);
console.log(`Node.js Version: ${process.version}`);
// 输出访问地址
console.log(`Local: http://localhost:${port}`);
// 输出网络地址
Object.keys(nets).forEach(key => {
const addresses = nets[key];
addresses?.forEach(address => {
if (address.family === 'IPv4' && !address.internal) {
console.log(`Network: http://${address.address}:${port}`);
}
});
});
// 输出启动时间
if (startTime) {
const duration = Date.now() - startTime;
console.log(`Startup Time: ${duration}ms`);
}
};
12. 启动流程总结
FastbuildAI后端服务的完整启动流程如下:
- 环境准备:加载环境变量配置文件
- 应用创建:创建NestJS应用实例
- 全局容器设置:设置依赖注入容器
- 模块注册:按顺序注册所有功能模块
- 数据库初始化:连接数据库并初始化数据
- 缓存系统启动:初始化内存缓存服务
- 插件系统加载:扫描并加载所有启用的插件
- 中间件配置:注册全局拦截器和异常过滤器
- 静态文件服务:配置静态资源和前端应用服务
- 端口监听:启动HTTP服务器监听指定端口
- 启动日志:输出启动成功信息和访问地址
整个启动过程采用模块化设计,各个组件独立初始化,确保系统的可维护性和扩展性。通过详细的日志记录和异常处理,保证了启动过程的可观测性和稳定性。