use crate::error::{PluginError, PluginResult}; use crate::manifest::{parse_manifest, PluginManifest}; /// 插件上传时校验报告 #[derive(Debug, Clone, serde::Serialize)] pub struct ValidationReport { pub valid: bool, pub errors: Vec, pub warnings: Vec, pub metrics: PluginMetrics, } /// 插件质量指标 #[derive(Debug, Clone, Default, serde::Serialize)] pub struct PluginMetrics { pub entity_count: usize, pub field_count: usize, pub page_count: usize, pub permission_count: usize, pub relation_count: usize, pub has_import_export: bool, pub has_settings: bool, pub has_numbering: bool, pub has_trigger_events: bool, pub wasm_size_bytes: usize, pub complexity_score: f64, } /// 运行时监控指标 #[derive(Debug, Clone, Default, serde::Serialize)] pub struct RuntimeMetrics { pub error_count: u64, pub total_invocations: u64, pub avg_response_ms: f64, pub fuel_consumption_avg: f64, pub memory_peak_bytes: u64, pub last_error: Option, pub last_error_at: Option>, } impl RuntimeMetrics { pub fn error_rate(&self) -> f64 { if self.total_invocations == 0 { return 0.0; } self.error_count as f64 / self.total_invocations as f64 } } /// 上传时安全扫描 pub fn validate_plugin_security(manifest: &PluginManifest, wasm_size: usize) -> PluginResult { let mut errors = Vec::new(); let mut warnings = Vec::new(); // 1. WASM 大小检查(上限 10MB) if wasm_size > 10 * 1024 * 1024 { errors.push(format!("WASM 文件过大: {} bytes (上限 10MB)", wasm_size)); } else if wasm_size > 5 * 1024 * 1024 { warnings.push(format!("WASM 文件较大: {} bytes (>5MB)", wasm_size)); } // 2. 实体数量检查(上限 20) if let Some(schema) = &manifest.schema { if schema.entities.len() > 20 { errors.push(format!("实体数量过多: {} (上限 20)", schema.entities.len())); } for entity in &schema.entities { // 字段数量检查 if entity.fields.len() > 50 { errors.push(format!( "实体 '{}' 字段数量过多: {} (上限 50)", entity.name, entity.fields.len() )); } // 索引数量检查 if entity.indexes.len() > 10 { warnings.push(format!( "实体 '{}' 索引数量较多: {} (>10 可能影响写入性能)", entity.name, entity.indexes.len() )); } // 检查字段中有无潜在 SQL 注入风险的字段名 for field in &entity.fields { if field.name.len() > 64 { errors.push(format!( "字段名过长: '{}.{}' (上限 64 字符)", entity.name, field.name )); } if !field.name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { errors.push(format!( "字段名包含非法字符: '{}.{}' (只允许字母、数字、下划线)", entity.name, field.name )); } } } } // 3. 权限码命名规范检查 if let Some(permissions) = &manifest.permissions { for perm in permissions { if !perm.code.contains('.') { warnings.push(format!( "权限码 '{}' 建议使用 'entity.action' 格式", perm.code )); } } } // 4. 依赖检查 if manifest.metadata.dependencies.len() > 5 { warnings.push(format!( "依赖数量较多: {} (>5 可能增加安装复杂度)", manifest.metadata.dependencies.len() )); } // 5. 计算复杂度分数 let mut metrics = collect_metrics(manifest, wasm_size); metrics.complexity_score = calculate_complexity_score(&metrics); if metrics.complexity_score > 80.0 { warnings.push(format!( "插件复杂度较高: {:.1} (>80 建议拆分)", metrics.complexity_score )); } let valid = errors.is_empty(); Ok(ValidationReport { valid, errors, warnings, metrics, }) } /// 收集插件指标 fn collect_metrics(manifest: &PluginManifest, wasm_size: usize) -> PluginMetrics { let mut metrics = PluginMetrics { wasm_size_bytes: wasm_size, ..Default::default() }; if let Some(schema) = &manifest.schema { metrics.entity_count = schema.entities.len(); for entity in &schema.entities { metrics.field_count += entity.fields.len(); metrics.relation_count += entity.relations.len(); if entity.importable == Some(true) || entity.exportable == Some(true) { metrics.has_import_export = true; } } } if let Some(ui) = &manifest.ui { metrics.page_count = count_pages(&ui.pages); } if let Some(permissions) = &manifest.permissions { metrics.permission_count = permissions.len(); } metrics.has_settings = manifest.settings.is_some(); metrics.has_numbering = manifest.numbering.as_ref().map_or(false, |n| !n.is_empty()); metrics.has_trigger_events = manifest.trigger_events.as_ref().map_or(false, |t| !t.is_empty()); metrics } fn count_pages(pages: &[crate::manifest::PluginPageType]) -> usize { let mut count = 0; for page in pages { count += 1; if let crate::manifest::PluginPageType::Tabs { tabs, .. } = page { count += count_pages(tabs); } } count } /// 计算复杂度分数(0-100) fn calculate_complexity_score(metrics: &PluginMetrics) -> f64 { let entity_score = (metrics.entity_count as f64 / 20.0) * 30.0; let field_score = (metrics.field_count as f64 / 100.0) * 20.0; let page_score = (metrics.page_count as f64 / 20.0) * 15.0; let relation_score = (metrics.relation_count as f64 / 30.0) * 15.0; let size_score = (metrics.wasm_size_bytes as f64 / (10.0 * 1024.0 * 1024.0)) * 20.0; (entity_score + field_score + page_score + relation_score + size_score).min(100.0) } /// 性能基准测试结果 #[derive(Debug, Clone, serde::Serialize)] pub struct BenchmarkResult { pub create_avg_ms: f64, pub read_avg_ms: f64, pub update_avg_ms: f64, pub delete_avg_ms: f64, pub list_avg_ms: f64, pub passed: bool, pub details: String, } impl BenchmarkResult { /// 创建操作的阈值: 500ms pub const CREATE_THRESHOLD_MS: f64 = 500.0; /// 读取操作的阈值: 200ms pub const READ_THRESHOLD_MS: f64 = 200.0; /// 列表查询的阈值: 1000ms pub const LIST_THRESHOLD_MS: f64 = 1000.0; pub fn check(&self) -> bool { self.create_avg_ms <= Self::CREATE_THRESHOLD_MS && self.read_avg_ms <= Self::READ_THRESHOLD_MS && self.list_avg_ms <= Self::LIST_THRESHOLD_MS } } #[cfg(test)] mod tests { use super::*; #[test] fn validate_security_basic() { let toml = r#" [metadata] id = "test" name = "Test" version = "0.1.0" [schema] [[schema.entities]] name = "product" display_name = "商品" [[schema.entities.fields]] name = "sku" field_type = "string" required = true "#; let manifest = parse_manifest(toml).unwrap(); let report = validate_plugin_security(&manifest, 1024).unwrap(); assert!(report.valid); assert!(report.errors.is_empty()); } #[test] fn reject_oversized_wasm() { let toml = r#" [metadata] id = "test" name = "Test" version = "0.1.0" "#; let manifest = parse_manifest(toml).unwrap(); let report = validate_plugin_security(&manifest, 15 * 1024 * 1024).unwrap(); assert!(!report.valid); assert!(report.errors.iter().any(|e| e.contains("WASM 文件过大"))); } #[test] fn complexity_score_calculation() { let metrics = PluginMetrics { entity_count: 5, field_count: 30, page_count: 5, relation_count: 3, wasm_size_bytes: 500_000, ..Default::default() }; let score = calculate_complexity_score(&metrics); assert!(score > 0.0 && score < 50.0, "score = {}", score); } #[test] fn runtime_metrics_error_rate() { let metrics = RuntimeMetrics { error_count: 5, total_invocations: 100, ..Default::default() }; assert!((metrics.error_rate() - 0.05).abs() < 0.001); } #[test] fn benchmark_threshold_check() { let result = BenchmarkResult { create_avg_ms: 300.0, read_avg_ms: 100.0, update_avg_ms: 200.0, delete_avg_ms: 150.0, list_avg_ms: 800.0, passed: true, details: String::new(), }; assert!(result.check()); } }