- 新增端口管理器类,支持端口分配、验证和缓存管理 - 实现环境优先级策略,根据环境自动选择合适的端口范围 - 集成租户识别系统,为每个租户分配独立端口 - 添加端口分配结果统计和历史记录查询功能 - 优化端口缓存机制,自动清理过期绑定
416 lines
11 KiB
TypeScript
416 lines
11 KiB
TypeScript
/**
|
||
* 环境端口策略配置
|
||
* 类似租户识别系统的环境优先级策略
|
||
*/
|
||
|
||
import type { PortStrategy } from './port-manager';
|
||
|
||
// 环境类型
|
||
export type Environment = 'development' | 'test' | 'staging' | 'production';
|
||
|
||
// 端口策略优先级
|
||
export interface PortPriority {
|
||
environment: Environment;
|
||
priority: number; // 数字越小优先级越高
|
||
description: string;
|
||
}
|
||
|
||
// 环境端口策略配置
|
||
export interface EnvironmentPortStrategy extends PortStrategy {
|
||
environment: Environment;
|
||
priority: number;
|
||
autoDetect: boolean;
|
||
fallbackStrategy?: EnvironmentPortStrategy;
|
||
restrictions: {
|
||
allowedHosts: string[];
|
||
blockedPorts: number[];
|
||
requireHttps: boolean;
|
||
};
|
||
}
|
||
|
||
// 端口分配模式
|
||
export enum PortAllocationMode {
|
||
TENANT_BASED = 'tenant-based', // 基于租户分配
|
||
SEQUENTIAL = 'sequential', // 顺序分配
|
||
RANDOM = 'random', // 随机分配
|
||
HASH_BASED = 'hash-based' // 基于哈希分配
|
||
}
|
||
|
||
// 环境检测器
|
||
export class EnvironmentDetector {
|
||
/**
|
||
* 检测当前环境
|
||
*/
|
||
static detectEnvironment(): Environment {
|
||
// 1. 检查环境变量
|
||
const nodeEnv = process.env.NODE_ENV;
|
||
if (nodeEnv) {
|
||
switch (nodeEnv.toLowerCase()) {
|
||
case 'development':
|
||
case 'dev':
|
||
return 'development';
|
||
case 'test':
|
||
case 'testing':
|
||
return 'test';
|
||
case 'staging':
|
||
case 'stage':
|
||
return 'staging';
|
||
case 'production':
|
||
case 'prod':
|
||
return 'production';
|
||
}
|
||
}
|
||
|
||
// 2. 检查域名
|
||
const hostname = window.location.hostname;
|
||
if (hostname.includes('localhost') || hostname.includes('127.0.0.1')) {
|
||
return 'development';
|
||
}
|
||
if (hostname.includes('test') || hostname.includes('staging')) {
|
||
return 'test';
|
||
}
|
||
if (hostname.includes('prod') || hostname.includes('www')) {
|
||
return 'production';
|
||
}
|
||
|
||
// 3. 检查端口
|
||
const port = window.location.port;
|
||
if (port && parseInt(port) < 4000) {
|
||
return 'development';
|
||
}
|
||
|
||
// 默认返回开发环境
|
||
return 'development';
|
||
}
|
||
|
||
/**
|
||
* 获取环境建议
|
||
*/
|
||
static getEnvironmentRecommendation(): {
|
||
detected: Environment;
|
||
confidence: number;
|
||
reasons: string[];
|
||
suggestions: string[];
|
||
} {
|
||
const reasons: string[] = [];
|
||
const suggestions: string[] = [];
|
||
let confidence = 0;
|
||
|
||
const nodeEnv = process.env.NODE_ENV;
|
||
const hostname = window.location.hostname;
|
||
const port = window.location.port;
|
||
const protocol = window.location.protocol;
|
||
|
||
// 分析环境变量
|
||
if (nodeEnv) {
|
||
reasons.push(`NODE_ENV: ${nodeEnv}`);
|
||
confidence += 40;
|
||
} else {
|
||
suggestions.push('建议设置 NODE_ENV 环境变量');
|
||
}
|
||
|
||
// 分析域名
|
||
if (hostname.includes('localhost')) {
|
||
reasons.push('域名包含 localhost');
|
||
confidence += 30;
|
||
} else if (hostname.includes('test')) {
|
||
reasons.push('域名包含 test');
|
||
confidence += 25;
|
||
} else if (hostname.includes('prod')) {
|
||
reasons.push('域名包含 prod');
|
||
confidence += 35;
|
||
}
|
||
|
||
// 分析协议
|
||
if (protocol === 'https:') {
|
||
reasons.push('使用 HTTPS 协议');
|
||
confidence += 10;
|
||
} else {
|
||
suggestions.push('生产环境建议使用 HTTPS');
|
||
}
|
||
|
||
// 分析端口
|
||
if (port) {
|
||
const portNum = parseInt(port);
|
||
if (portNum >= 3000 && portNum < 4000) {
|
||
reasons.push('使用开发端口范围');
|
||
confidence += 15;
|
||
}
|
||
}
|
||
|
||
return {
|
||
detected: this.detectEnvironment(),
|
||
confidence: Math.min(confidence, 100),
|
||
reasons,
|
||
suggestions
|
||
};
|
||
}
|
||
}
|
||
|
||
// 端口策略管理器
|
||
export class PortStrategyManager {
|
||
private strategies: Map<Environment, EnvironmentPortStrategy>;
|
||
private currentEnvironment: Environment;
|
||
|
||
constructor() {
|
||
this.currentEnvironment = EnvironmentDetector.detectEnvironment();
|
||
this.strategies = new Map();
|
||
this.initializeDefaultStrategies();
|
||
}
|
||
|
||
/**
|
||
* 初始化默认策略
|
||
*/
|
||
private initializeDefaultStrategies(): void {
|
||
// 开发环境策略
|
||
this.strategies.set('development', {
|
||
environment: 'development',
|
||
priority: 1,
|
||
basePort: 3000,
|
||
portRange: [3000, 3999],
|
||
tenantOffset: 10,
|
||
environmentOffset: 0,
|
||
maxRetries: 50,
|
||
autoDetect: true,
|
||
restrictions: {
|
||
allowedHosts: ['localhost', '127.0.0.1', '0.0.0.0'],
|
||
blockedPorts: [],
|
||
requireHttps: false
|
||
}
|
||
});
|
||
|
||
// 测试环境策略
|
||
this.strategies.set('test', {
|
||
environment: 'test',
|
||
priority: 2,
|
||
basePort: 4000,
|
||
portRange: [4000, 4999],
|
||
tenantOffset: 5,
|
||
environmentOffset: 1000,
|
||
maxRetries: 30,
|
||
autoDetect: true,
|
||
restrictions: {
|
||
allowedHosts: ['localhost', '127.0.0.1', 'test.local'],
|
||
blockedPorts: [4444, 4567], // 避免与其他测试工具冲突
|
||
requireHttps: false
|
||
}
|
||
});
|
||
|
||
// 预发布环境策略
|
||
this.strategies.set('staging', {
|
||
environment: 'staging',
|
||
priority: 3,
|
||
basePort: 5000,
|
||
portRange: [5000, 5999],
|
||
tenantOffset: 3,
|
||
environmentOffset: 2000,
|
||
maxRetries: 20,
|
||
autoDetect: true,
|
||
restrictions: {
|
||
allowedHosts: ['staging.local', 'stage.example.com'],
|
||
blockedPorts: [],
|
||
requireHttps: true
|
||
}
|
||
});
|
||
|
||
// 生产环境策略
|
||
this.strategies.set('production', {
|
||
environment: 'production',
|
||
priority: 4,
|
||
basePort: 8080,
|
||
portRange: [8080, 8999],
|
||
tenantOffset: 1,
|
||
environmentOffset: 5000,
|
||
maxRetries: 10,
|
||
autoDetect: false, // 生产环境不自动检测
|
||
restrictions: {
|
||
allowedHosts: ['0.0.0.0'], // 生产环境通常绑定所有接口
|
||
blockedPorts: [8080, 8443], // 避免与常用服务冲突
|
||
requireHttps: true
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取当前环境策略
|
||
*/
|
||
getCurrentStrategy(): EnvironmentPortStrategy {
|
||
const strategy = this.strategies.get(this.currentEnvironment);
|
||
if (!strategy) {
|
||
console.warn(`未找到环境 ${this.currentEnvironment} 的策略,使用开发环境策略`);
|
||
return this.strategies.get('development')!;
|
||
}
|
||
return strategy;
|
||
}
|
||
|
||
/**
|
||
* 获取指定环境策略
|
||
*/
|
||
getStrategy(environment: Environment): EnvironmentPortStrategy | undefined {
|
||
return this.strategies.get(environment);
|
||
}
|
||
|
||
/**
|
||
* 设置环境策略
|
||
*/
|
||
setStrategy(environment: Environment, strategy: EnvironmentPortStrategy): void {
|
||
this.strategies.set(environment, strategy);
|
||
console.log(`✅ 已更新 ${environment} 环境的端口策略`);
|
||
}
|
||
|
||
/**
|
||
* 获取推荐策略(基于环境优先级)
|
||
*/
|
||
getRecommendedStrategy(): {
|
||
primary: EnvironmentPortStrategy;
|
||
fallback: EnvironmentPortStrategy[];
|
||
reasoning: string[];
|
||
} {
|
||
const current = this.getCurrentStrategy();
|
||
const reasoning: string[] = [];
|
||
const fallback: EnvironmentPortStrategy[] = [];
|
||
|
||
reasoning.push(`当前环境: ${this.currentEnvironment}`);
|
||
reasoning.push(`优先级: ${current.priority}`);
|
||
|
||
// 获取备选策略(按优先级排序)
|
||
const allStrategies = Array.from(this.strategies.values())
|
||
.filter(s => s.environment !== this.currentEnvironment)
|
||
.sort((a, b) => a.priority - b.priority);
|
||
|
||
fallback.push(...allStrategies);
|
||
|
||
// 环境特定的推理
|
||
switch (this.currentEnvironment) {
|
||
case 'development':
|
||
reasoning.push('开发环境优先考虑端口可用性和调试便利性');
|
||
break;
|
||
case 'test':
|
||
reasoning.push('测试环境需要隔离性和可重复性');
|
||
break;
|
||
case 'staging':
|
||
reasoning.push('预发布环境模拟生产环境配置');
|
||
break;
|
||
case 'production':
|
||
reasoning.push('生产环境优先考虑安全性和稳定性');
|
||
break;
|
||
}
|
||
|
||
return { primary: current, fallback, reasoning };
|
||
}
|
||
|
||
/**
|
||
* 验证端口策略
|
||
*/
|
||
validateStrategy(strategy: EnvironmentPortStrategy): {
|
||
isValid: boolean;
|
||
errors: string[];
|
||
warnings: string[];
|
||
} {
|
||
const errors: string[] = [];
|
||
const warnings: string[] = [];
|
||
|
||
// 检查端口范围
|
||
if (strategy.portRange[0] >= strategy.portRange[1]) {
|
||
errors.push('端口范围无效:起始端口必须小于结束端口');
|
||
}
|
||
|
||
if (strategy.portRange[0] < 1024 && strategy.environment === 'production') {
|
||
warnings.push('生产环境使用系统端口(<1024)可能需要管理员权限');
|
||
}
|
||
|
||
// 检查基础端口
|
||
if (strategy.basePort < strategy.portRange[0] || strategy.basePort > strategy.portRange[1]) {
|
||
errors.push('基础端口不在允许的端口范围内');
|
||
}
|
||
|
||
// 检查租户偏移
|
||
if (strategy.tenantOffset <= 0) {
|
||
warnings.push('租户偏移为0可能导致端口冲突');
|
||
}
|
||
|
||
// 检查环境特定规则
|
||
if (strategy.environment === 'production' && !strategy.restrictions.requireHttps) {
|
||
warnings.push('生产环境建议启用 HTTPS');
|
||
}
|
||
|
||
if (strategy.environment === 'development' && strategy.restrictions.requireHttps) {
|
||
warnings.push('开发环境启用 HTTPS 可能增加配置复杂度');
|
||
}
|
||
|
||
return {
|
||
isValid: errors.length === 0,
|
||
errors,
|
||
warnings
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 获取环境统计信息
|
||
*/
|
||
getEnvironmentStats(): {
|
||
current: Environment;
|
||
available: Environment[];
|
||
strategies: Array<{
|
||
environment: Environment;
|
||
priority: number;
|
||
portRange: [number, number];
|
||
isValid: boolean;
|
||
}>;
|
||
} {
|
||
const strategies = Array.from(this.strategies.entries()).map(([env, strategy]) => ({
|
||
environment: env,
|
||
priority: strategy.priority,
|
||
portRange: strategy.portRange,
|
||
isValid: this.validateStrategy(strategy).isValid
|
||
}));
|
||
|
||
return {
|
||
current: this.currentEnvironment,
|
||
available: Array.from(this.strategies.keys()),
|
||
strategies: strategies.sort((a, b) => a.priority - b.priority)
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 切换环境
|
||
*/
|
||
switchEnvironment(environment: Environment): boolean {
|
||
if (!this.strategies.has(environment)) {
|
||
console.error(`环境 ${environment} 不存在`);
|
||
return false;
|
||
}
|
||
|
||
this.currentEnvironment = environment;
|
||
console.log(`🔄 已切换到 ${environment} 环境`);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// 导出默认实例
|
||
export const portStrategyManager = new PortStrategyManager();
|
||
|
||
// 导出环境优先级配置
|
||
export const ENVIRONMENT_PRIORITIES: PortPriority[] = [
|
||
{
|
||
environment: 'development',
|
||
priority: 1,
|
||
description: '开发环境 - 最高优先级,注重便利性'
|
||
},
|
||
{
|
||
environment: 'test',
|
||
priority: 2,
|
||
description: '测试环境 - 高优先级,注重隔离性'
|
||
},
|
||
{
|
||
environment: 'staging',
|
||
priority: 3,
|
||
description: '预发布环境 - 中等优先级,模拟生产'
|
||
},
|
||
{
|
||
environment: 'production',
|
||
priority: 4,
|
||
description: '生产环境 - 最低优先级,注重安全性'
|
||
}
|
||
];
|