diff --git a/server-rs/crates/spacetime-module/src/error_report.rs b/server-rs/crates/spacetime-module/src/error_report.rs index 29cad88c7..fff94a34e 100644 --- a/server-rs/crates/spacetime-module/src/error_report.rs +++ b/server-rs/crates/spacetime-module/src/error_report.rs @@ -2,6 +2,8 @@ use crate::*; const MAX_FIELD_CHARS: usize = 512; const MAX_NOTE_CHARS: usize = 2_000; +const MAX_REPORTS_PER_IDENTITY_PER_HOUR: usize = 100; +const REPORT_QUOTA_WINDOW_MICROS: i64 = 60 * 60 * 1_000_000; #[spacetimedb::table( accessor = error_report, @@ -142,8 +144,12 @@ pub fn create_error_report_and_return( input: ErrorReportCreateInput, ) -> ErrorReportProcedureResult { let caller_user_id = ctx.sender().to_hex().to_string(); + let caller = ctx.sender(); let now = ctx.timestamp; match ctx.try_with_tx(|tx| { + crate::editor_project_storage::require_editor_generation_runtime_service_identity( + tx, caller, + )?; let batch_id = validate_text(&input.batch_id, "batch_id")?; let user_id = validate_text(&caller_user_id, "user_id")?; let submission_id = validate_text(&input.submission_id, "submission_id")?; @@ -171,6 +177,21 @@ pub fn create_error_report_and_return( if tx.db.error_report().batch_id().find(&batch_id).is_some() { return Err("错误报告 batch_id 已存在".to_string()); } + let cutoff_micros = now + .to_micros_since_unix_epoch() + .saturating_sub(REPORT_QUOTA_WINDOW_MICROS); + let recent_count = tx + .db + .error_report() + .iter() + .filter(|row| { + row.user_id == user_id + && row.created_at.to_micros_since_unix_epoch() >= cutoff_micros + }) + .count(); + if recent_count >= MAX_REPORTS_PER_IDENTITY_PER_HOUR { + return Err("错误报告提交频率超出限制,请稍后再试".to_string()); + } tx.db.error_report().insert(ErrorReport { batch_id: batch_id.clone(), user_id,