Files
mp-vue/src/lib/port-strategy.ts
赵忠林 7052ccce61 feat(port): 实现智能端口管理系统
- 新增端口管理器类,支持端口分配、验证和缓存管理
- 实现环境优先级策略,根据环境自动选择合适的端口范围
- 集成租户识别系统,为每个租户分配独立端口
- 添加端口分配结果统计和历史记录查询功能
- 优化端口缓存机制,自动清理过期绑定
2025-09-03 18:52:39 +08:00

416 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 环境端口策略配置
* 类似租户识别系统的环境优先级策略
*/
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: '生产环境 - 最低优先级,注重安全性'
}
];