@@ -913,6 +913,9 @@ impl AppCore {
913913 if enabled && git:: git_version ( ) . is_none ( ) {
914914 return Err ( "未检测到系统 Git" . into ( ) ) ;
915915 }
916+ if enabled && settings. consent_acknowledged_at . is_none ( ) {
917+ return Err ( "首次开启加速前,请在 GitBoost 窗口中确认隐私提示" . into ( ) ) ;
918+ }
916919 if enabled && settings. route_scope == RouteScope :: Allowlist && self . routes ( ) ?. is_empty ( ) {
917920 return Err ( "仅加速清单为空,请先加入至少一个公开仓库" . into ( ) ) ;
918921 }
@@ -930,6 +933,14 @@ impl AppCore {
930933 self . snapshot ( )
931934 }
932935
936+ pub fn acknowledge_consent ( & self ) -> Result < AppSnapshot , String > {
937+ let _guard = self . lock . lock ( ) ;
938+ let mut settings = self . settings ( ) ?;
939+ settings. consent_acknowledged_at = Some ( Utc :: now ( ) ) ;
940+ atomic_write_json ( & self . paths . settings , & settings) ?;
941+ self . snapshot ( )
942+ }
943+
933944 fn select_current (
934945 & self ,
935946 settings : & mut Settings ,
@@ -1144,6 +1155,12 @@ impl AppCore {
11441155 let registered = git:: include_registered ( & self . paths . gitconfig ) ;
11451156 let mut settings = self . settings ( ) ?;
11461157 let routes = self . routes ( ) ?;
1158+ let repaired_missing_consent =
1159+ settings. acceleration_enabled && settings. consent_acknowledged_at . is_none ( ) ;
1160+ if repaired_missing_consent {
1161+ settings. acceleration_enabled = false ;
1162+ settings. current_node_id = None ;
1163+ }
11471164 let repaired_empty_allowlist = settings. acceleration_enabled
11481165 && settings. route_scope == RouteScope :: Allowlist
11491166 && routes. is_empty ( ) ;
@@ -1157,7 +1174,7 @@ impl AppCore {
11571174 settings. current_node_id = None ;
11581175 }
11591176 if !registered && !settings. acceleration_enabled {
1160- if repaired_empty_allowlist || repaired_disabled_state {
1177+ if repaired_missing_consent || repaired_empty_allowlist || repaired_disabled_state {
11611178 atomic_write_json ( & self . paths . settings , & settings) ?;
11621179 }
11631180 return Ok ( ( ) ) ;
@@ -1998,6 +2015,7 @@ mod tests {
19982015 let settings = Settings {
19992016 acceleration_enabled : true ,
20002017 current_node_id : Some ( replacement. id . clone ( ) ) ,
2018+ consent_acknowledged_at : Some ( Utc :: now ( ) ) ,
20012019 ..Settings :: default ( )
20022020 } ;
20032021 atomic_write_json ( & core. paths . routes , & vec ! [ route. clone( ) ] ) . unwrap ( ) ;
@@ -2093,6 +2111,7 @@ mod tests {
20932111 let settings = Settings {
20942112 acceleration_enabled : true ,
20952113 current_node_id : Some ( FASTGIT_REWRITE_BASE . into ( ) ) ,
2114+ consent_acknowledged_at : Some ( Utc :: now ( ) ) ,
20962115 ..Settings :: default ( )
20972116 } ;
20982117 atomic_write_json ( & core. paths . settings , & settings) . unwrap ( ) ;
@@ -2783,6 +2802,7 @@ mod tests {
27832802 let directory = tempfile:: tempdir ( ) . unwrap ( ) ;
27842803 let core = AppCore :: new ( directory. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
27852804 core. add_route ( "openai/codex" ) . unwrap ( ) ;
2805+ core. acknowledge_consent ( ) . unwrap ( ) ;
27862806
27872807 assert_eq ! (
27882808 core. set_acceleration( true ) . unwrap_err( ) ,
@@ -2831,6 +2851,38 @@ mod tests {
28312851 assert ! ( restored_allowlist. settings. current_node_id. is_none( ) ) ;
28322852 }
28332853
2854+ #[ test]
2855+ fn acceleration_requires_privacy_consent_once ( ) {
2856+ const MARKER : & str = "GITBOOST_CONSENT_GATE_STATE_CHILD" ;
2857+ if rerun_with_isolated_git (
2858+ "core::tests::acceleration_requires_privacy_consent_once" ,
2859+ MARKER ,
2860+ ) {
2861+ return ;
2862+ }
2863+ let directory = tempfile:: tempdir ( ) . unwrap ( ) ;
2864+ let core = AppCore :: new ( directory. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
2865+ // 常规配置操作(添加路由)不能影响同意状态
2866+ core. add_route ( "openai/codex" ) . unwrap ( ) ;
2867+ assert ! ( core. settings( ) . unwrap( ) . consent_acknowledged_at. is_none( ) ) ;
2868+
2869+ assert_eq ! (
2870+ core. set_acceleration( true ) . unwrap_err( ) ,
2871+ "首次开启加速前,请在 GitBoost 窗口中确认隐私提示"
2872+ ) ;
2873+ assert ! ( !core. settings( ) . unwrap( ) . acceleration_enabled) ;
2874+
2875+ let acknowledged = core. acknowledge_consent ( ) . unwrap ( ) ;
2876+ assert ! ( acknowledged. settings. consent_acknowledged_at. is_some( ) ) ;
2877+ assert ! ( !acknowledged. settings. acceleration_enabled) ;
2878+
2879+ // 同意后不再要求确认,错误回到线路本身
2880+ assert_eq ! (
2881+ core. set_acceleration( true ) . unwrap_err( ) ,
2882+ "没有通过真实 Git 检测的可用节点"
2883+ ) ;
2884+ }
2885+
28342886 #[ test]
28352887 fn startup_clears_a_stale_selection_while_disabled ( ) {
28362888 let directory = tempfile:: tempdir ( ) . unwrap ( ) ;
@@ -3452,6 +3504,16 @@ mod tests {
34523504 ) ;
34533505
34543506 core. add_route ( "openai/codex" ) . unwrap ( ) ;
3507+ assert_eq ! (
3508+ core. set_acceleration( true ) . unwrap_err( ) ,
3509+ "首次开启加速前,请在 GitBoost 窗口中确认隐私提示"
3510+ ) ;
3511+ core. acknowledge_consent ( ) . unwrap ( ) ;
3512+ drop ( core) ;
3513+
3514+ // 真实文件持久化和重新加载后,同意状态仍然有效,不依赖进程内 mock 状态。
3515+ let core = AppCore :: new ( root. clone ( ) ) . unwrap ( ) ;
3516+ assert ! ( core. settings( ) . unwrap( ) . consent_acknowledged_at. is_some( ) ) ;
34553517 let enabled = core. set_acceleration ( true ) . unwrap ( ) ;
34563518 assert ! ( enabled. settings. acceleration_enabled) ;
34573519 let node_id = enabled
0 commit comments