@@ -9,6 +9,7 @@ use chrono::{DateTime, Duration, NaiveDate, NaiveTime, Utc};
99use chrono_tz:: Tz ;
1010use sea_orm:: { ColumnTrait , EntityTrait , QueryFilter , QueryOrder , QuerySelect } ;
1111use std:: collections:: HashMap ;
12+ use std:: path:: PathBuf ;
1213use std:: sync:: { Arc , RwLock } ;
1314use tokio:: time;
1415use tracing:: { error, info} ;
@@ -39,12 +40,95 @@ pub struct ArchiveAddon {
3940 state : ArchiveState ,
4041}
4142
43+ fn archive_config_path ( config_path : & Option < String > ) -> PathBuf {
44+ config_path
45+ . as_deref ( )
46+ . and_then ( |path| std:: path:: Path :: new ( path) . parent ( ) )
47+ . map ( |dir| dir. join ( "archive.toml" ) )
48+ . unwrap_or_else ( || PathBuf :: from ( "archive.toml" ) )
49+ }
50+
4251impl Default for ArchiveAddon {
4352 fn default ( ) -> Self {
4453 Self :: new ( )
4554 }
4655}
4756
57+ #[ cfg( test) ]
58+ mod tests {
59+ use super :: * ;
60+
61+ #[ test]
62+ fn archive_config_path_uses_main_config_directory ( ) {
63+ let path = Some ( "config/rustpbx.toml" . to_string ( ) ) ;
64+
65+ assert_eq ! (
66+ archive_config_path( & path) ,
67+ std:: path:: PathBuf :: from( "config/archive.toml" )
68+ ) ;
69+ }
70+
71+ #[ tokio:: test]
72+ async fn load_config_reads_archive_toml_next_to_main_config ( ) {
73+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
74+ let main_config = dir. path ( ) . join ( "rustpbx.toml" ) ;
75+ let archive_config = dir. path ( ) . join ( "archive.toml" ) ;
76+ std:: fs:: write ( & main_config, "[proxy]\n addons = [\" archive\" ]\n " ) . unwrap ( ) ;
77+ std:: fs:: write (
78+ & archive_config,
79+ r#"enabled = true
80+ archive_time = "03:00"
81+ timezone = "Asia/Shanghai"
82+ retention_days = 30
83+ archive_after_days = 7
84+ archive_dir = "/tmp/rustpbx-archive"
85+ "# ,
86+ )
87+ . unwrap ( ) ;
88+
89+ let loaded =
90+ ArchiveAddon :: load_config ( & Some ( main_config. to_string_lossy ( ) . to_string ( ) ) ) . await ;
91+ let loaded = loaded. expect ( "archive config should load" ) ;
92+
93+ assert ! ( loaded. enabled) ;
94+ assert_eq ! ( loaded. archive_time, "03:00" ) ;
95+ assert_eq ! ( loaded. timezone. as_deref( ) , Some ( "Asia/Shanghai" ) ) ;
96+ assert_eq ! ( loaded. retention_days, 30 ) ;
97+ assert_eq ! ( loaded. archive_after_days, 7 ) ;
98+ assert_eq ! ( loaded. archive_dir. as_deref( ) , Some ( "/tmp/rustpbx-archive" ) ) ;
99+ }
100+
101+ #[ tokio:: test]
102+ async fn load_config_falls_back_to_archive_table_in_main_config ( ) {
103+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
104+ let main_config = dir. path ( ) . join ( "rustpbx.toml" ) ;
105+ std:: fs:: write (
106+ & main_config,
107+ r#"[proxy]
108+ addons = ["archive"]
109+
110+ [archive]
111+ enabled = true
112+ archive_time = "04:00"
113+ timezone = "UTC"
114+ retention_days = 14
115+ archive_after_days = 3
116+ "# ,
117+ )
118+ . unwrap ( ) ;
119+
120+ let loaded =
121+ ArchiveAddon :: load_config ( & Some ( main_config. to_string_lossy ( ) . to_string ( ) ) ) . await ;
122+ let loaded = loaded. expect ( "archive config should load from main config" ) ;
123+
124+ assert ! ( loaded. enabled) ;
125+ assert_eq ! ( loaded. archive_time, "04:00" ) ;
126+ assert_eq ! ( loaded. timezone. as_deref( ) , Some ( "UTC" ) ) ;
127+ assert_eq ! ( loaded. retention_days, 14 ) ;
128+ assert_eq ! ( loaded. archive_after_days, 3 ) ;
129+ }
130+ }
131+
48132impl ArchiveAddon {
49133 pub fn new ( ) -> Self {
50134 Self {
@@ -58,30 +142,63 @@ impl ArchiveAddon {
58142
59143 /// Load configuration from addon-specific config file.
60144 pub async fn load_config ( config_path : & Option < String > ) -> Option < ArchiveConfig > {
145+ let addon_config_path = archive_config_path ( config_path) ;
146+ if addon_config_path. exists ( ) {
147+ match tokio:: fs:: read_to_string ( & addon_config_path) . await {
148+ Ok ( content) => match toml:: from_str :: < ArchiveConfig > ( & content) {
149+ Ok ( config) => {
150+ tracing:: info!(
151+ "Archive config loaded from {}" ,
152+ addon_config_path. display( )
153+ ) ;
154+ return Some ( config) ;
155+ }
156+ Err ( e) => {
157+ tracing:: warn!( "Failed to parse archive.toml: {}" , e) ;
158+ }
159+ } ,
160+ Err ( e) => {
161+ tracing:: warn!( "Failed to read archive.toml: {}" , e) ;
162+ }
163+ }
164+ }
165+
61166 if let Some ( path) = config_path {
62- let config_dir = std:: path:: Path :: new ( path) . parent ( ) ?;
63- let addon_config_path = config_dir. join ( "archive.toml" ) ;
64- if addon_config_path. exists ( ) {
65- match tokio:: fs:: read_to_string ( & addon_config_path) . await {
66- Ok ( content) => match toml:: from_str :: < ArchiveConfig > ( & content) {
67- Ok ( config) => {
68- tracing:: info!(
69- "Archive config loaded from {}" ,
70- addon_config_path. display( )
71- ) ;
72- return Some ( config) ;
73- }
74- Err ( e) => {
75- tracing:: warn!( "Failed to parse archive.toml: {}" , e) ;
167+ match tokio:: fs:: read_to_string ( path) . await {
168+ Ok ( content) => match toml:: from_str :: < toml:: Value > ( & content) {
169+ Ok ( value) => {
170+ if let Some ( archive_value) = value. get ( "archive" ) {
171+ match archive_value. clone ( ) . try_into :: < ArchiveConfig > ( ) {
172+ Ok ( config) => {
173+ tracing:: info!(
174+ "Archive config loaded from [archive] in {}" ,
175+ path
176+ ) ;
177+ return Some ( config) ;
178+ }
179+ Err ( e) => {
180+ tracing:: warn!(
181+ "Failed to parse [archive] from {}: {}" ,
182+ path,
183+ e
184+ ) ;
185+ }
186+ }
76187 }
77- } ,
188+ }
78189 Err ( e) => {
79- tracing:: warn!( "Failed to read archive.toml : {}" , e) ;
190+ tracing:: warn!( "Failed to parse {} while checking [ archive] : {}" , path , e) ;
80191 }
192+ } ,
193+ Err ( e) => {
194+ tracing:: warn!( "Failed to read {} while checking [archive]: {}" , path, e) ;
81195 }
82196 }
83197 }
84- tracing:: info!( "Archive using default configuration (no archive.toml found)" ) ;
198+
199+ tracing:: info!(
200+ "Archive using default configuration (no archive.toml or [archive] config found)"
201+ ) ;
85202 None
86203 }
87204
0 commit comments