chimes_utils/utils/
mod.rs

1use chrono::{DateTime, Local};
2use log::LevelFilter;
3use rbatis::DriverType;
4use std::collections::HashMap;
5use std::fmt::Debug;
6use std::fs::File;
7use std::io::Read;
8use std::mem::MaybeUninit;
9use std::str::FromStr;
10use std::sync::{Mutex, Once};
11use std::time::SystemTime;
12use std::time::UNIX_EPOCH;
13// use openssl::hash::{DigestBytes, MessageDigest};
14use openssl::sha::Sha256;
15use rbatis::log::LogPlugin;
16use rbatis::rbatis::Rbatis;
17use serde_derive::{Deserialize, Serialize};
18
19mod rbatis_dynamical;
20pub use rbatis_dynamical::*;
21
22mod redis_client;
23pub use redis_client::*;
24mod r2d2_pool;
25
26mod actix_client;
27pub use actix_client::*;
28
29mod script_engine;
30pub use script_engine::*;
31
32mod gpt_sdk;
33pub use gpt_sdk::*;
34
35mod common_search;
36pub use common_search::*;
37
38#[cfg(windows)]
39mod windows_service_utils;
40
41#[cfg(windows)]
42mod windows_performance;
43
44use serde_json::Value;
45#[cfg(windows)]
46pub use windows_service_utils::*;
47
48#[cfg(not(target_os = "windows"))]
49mod linux_performance;
50
51mod performance;
52pub use performance::*;
53
54#[allow(dead_code)]
55pub fn get_local_timestamp() -> u64 {
56    let now = SystemTime::now();
57    let date: DateTime<Local> = now.into();
58    date.timestamp_millis() as u64
59}
60
61#[allow(dead_code)]
62pub fn get_multiple_rbatis(url: &str) -> &'static Rbatis {
63    // 使用MaybeUninit延迟初始化
64    static mut STATIC_MULTI_RB: MaybeUninit<HashMap<String, Rbatis>> = MaybeUninit::uninit();
65    // Once带锁保证只进行一次初始化
66    static ONCE_HASH: Once = Once::new();
67
68    ONCE_HASH.call_once(|| unsafe {
69        STATIC_MULTI_RB.as_mut_ptr().write(HashMap::new());
70    });
71
72    unsafe {
73        if (*STATIC_MULTI_RB.as_mut_ptr()).contains_key(url) {
74            let hashmap = &*STATIC_MULTI_RB.as_mut_ptr();
75            &hashmap[url]
76        } else {
77            async_std::task::block_on(async {
78                log::info!("Call the block on to create the sql connection.");
79                let mut rb = Rbatis::new();
80                match rb.link(url).await {
81                    Ok(_) => {
82                        log::info!(
83                            "Database {} was connected. Rbatis was initialized successfully.",
84                            url
85                        );
86                    }
87                    Err(err) => {
88                        log::warn!("Error: {}", err);
89                    }
90                };
91                rb.add_sql_intercept(MultipleDatabaseIntercepter());
92                (*STATIC_MULTI_RB.as_mut_ptr()).insert(url.to_string(), rb);
93            });
94            let hashmap = &*STATIC_MULTI_RB.as_mut_ptr();
95            &hashmap[url]
96        }
97    }
98}
99
100#[allow(dead_code)]
101pub async fn get_multiple_rbatis_async(url: &str) -> &'static Rbatis {
102    // 使用MaybeUninit延迟初始化
103    static mut STATIC_MULTI_RB: MaybeUninit<HashMap<String, Rbatis>> = MaybeUninit::uninit();
104    // Once带锁保证只进行一次初始化
105    static ONCE_HASH: Once = Once::new();
106
107    ONCE_HASH.call_once(|| unsafe {
108        STATIC_MULTI_RB.as_mut_ptr().write(HashMap::new());
109    });
110
111    unsafe {
112        if (*STATIC_MULTI_RB.as_mut_ptr()).contains_key(url) {
113            let hashmap = &*STATIC_MULTI_RB.as_mut_ptr();
114            &hashmap[url]
115        } else {
116            log::info!("Call the block on to create the sql connection.");
117            let mut rb = Rbatis::new();
118            match rb.link(url).await {
119                Ok(_) => {
120                    log::info!(
121                        "Database {} was connected. Rbatis was initialized successfully.",
122                        url
123                    );
124                }
125                Err(err) => {
126                    log::warn!("Error: {}", err);
127                }
128            };
129            rb.add_sql_intercept(MultipleDatabaseIntercepter());
130            (*STATIC_MULTI_RB.as_mut_ptr()).insert(url.to_string(), rb);
131            let hashmap = &*STATIC_MULTI_RB.as_mut_ptr();
132            &hashmap[url]
133        }
134    }
135}
136
137#[allow(dead_code)]
138pub fn get_rbatis() -> &'static Rbatis {
139    // 使用MaybeUninit延迟初始化
140    static mut STATIC_RB: MaybeUninit<Rbatis> = MaybeUninit::uninit();
141    // Once带锁保证只进行一次初始化
142    static ONCE: Once = Once::new();
143
144    ONCE.call_once(|| unsafe {
145        // CONF = 1u64;
146        let conf = AppConfig::get().lock().unwrap().to_owned();
147        let url = conf.db_conf.url.clone();
148
149        async_std::task::block_on(async {
150            let mut rb = Rbatis::new();
151            match rb.link(&url).await {
152                Ok(_) => {
153                    log::info!("Database was connected. Rbatis was initialized successfully.");
154                }
155                Err(err) => {
156                    log::warn!("Error: {}", err);
157                }
158            };
159            let rb_log = rbatis::log::RbatisLogPlugin::default();
160            rb_log.set_level_filter(conf.db_conf.logger_level);
161            rb.set_log_plugin(rb_log);
162            rb.add_sql_intercept(MultipleDatabaseIntercepter());
163            STATIC_RB.as_mut_ptr().write(rb);
164        });
165    });
166    unsafe { &*STATIC_RB.as_ptr() }
167}
168
169#[derive(Debug, Clone)]
170pub struct MultipleDatabaseIntercepter();
171
172impl rbatis::intercept::SqlIntercept for MultipleDatabaseIntercepter {
173    fn do_intercept(
174        &self,
175        rb: &Rbatis,
176        sql: &mut String,
177        _args: &mut Vec<rbson::Bson>,
178        _is_prepared_sql: bool,
179    ) -> Result<(), rbatis::core::Error> {
180        log::debug!("Origal: {}", sql.clone());
181        let m_sql = rbatis_compatible_sql(rb, sql.as_str());
182        log::debug!("Modify: {}", m_sql.clone());
183        *sql = m_sql;
184        Ok(())
185    }
186}
187
188/**
189 * 根据数据库类型来产生,对应的兼容的SQL查询语句
190 * 原始参考SQL是MySQL
191 * 如目标数据库类型是PostgreSQL,就将其转换成为$1这样的参数
192 */
193pub fn rbatis_compatible_sql(rb: &Rbatis, sql: &str) -> String {
194    match rb.driver_type() {
195        Ok(driver_type) => match driver_type {
196            DriverType::Postgres => {
197                let mut sql_out = String::new();
198                let mut spl = sql.split('?');
199                let mut i = 1;
200                let count = spl.clone().count();
201                loop {
202                    let t = spl.next();
203                    if t.is_some() {
204                        sql_out.push_str(t.unwrap());
205                        if i < count {
206                            sql_out.push_str(format!("${}", i).as_str());
207                        }
208                    } else {
209                        break;
210                    }
211                    i += 1;
212                }
213                sql_out
214            }
215            _ => sql.to_owned(),
216        },
217        Err(_) => sql.to_owned(),
218    }
219}
220
221#[derive(Debug, Clone)]
222pub struct AppConfig {
223    pub db_conf: DatabaseConfig,
224    pub webserver_conf: WebServerConfig,
225    pub email_conf: EmailServerConfig,
226    pub redis_conf: Option<RedisConfig>,
227    pub gateway_address: Option<String>,
228    pub app_id: Option<String>,
229    pub app_secret: Option<String>,
230    pub logger_level: LevelFilter,
231}
232
233impl Default for AppConfig {
234    fn default() -> Self {
235        Self {
236            db_conf: Default::default(),
237            webserver_conf: Default::default(),
238            email_conf: Default::default(),
239            redis_conf: Default::default(),
240            gateway_address: Default::default(),
241            app_id: Default::default(),
242            app_secret: Default::default(),
243            logger_level: LevelFilter::Info,
244        }
245    }
246}
247
248#[derive(Debug, Clone, Default)]
249pub struct RedisPoolConfig {
250    pub connection_timeout: u64,
251    pub max_size: u32,
252    pub mini_idel: u32,
253}
254
255#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)]
256#[serde(rename_all = "lowercase")]
257pub enum InstanceType {
258    Single,
259    Cluster,
260}
261
262#[derive(Debug, Clone, Default)]
263pub struct RedisConfig {
264    pub urls: Vec<String>,
265    pub database: i64,
266    pub username: Option<String>,
267    pub password: Option<String>,
268    pub instance_type: Option<InstanceType>,
269    pub pool: RedisPoolConfig,
270}
271
272impl RedisConfig {
273    #[allow(dead_code)]
274    fn instance_type_default() -> InstanceType {
275        InstanceType::Single
276    }
277}
278
279#[derive(Debug, Clone, Default)]
280pub struct WebServerConfig {
281    pub port: i64,
282    pub rsa_key: String,
283    pub rsa_cert: String,
284    pub rsa_password_private_key: String,
285    pub rsa_password_public_key: String,
286    pub upload_store_path: String,
287    pub resources_path: String,
288    pub upload_temp_path: String,
289    pub access_url_prefix: String,
290}
291
292#[derive(Debug, Clone)]
293pub struct DatabaseConfig {
294    pub url: String,
295    pub logger_level: LevelFilter,
296}
297
298impl Default for DatabaseConfig {
299    fn default() -> Self {
300        Self {
301            url: Default::default(),
302            logger_level: LevelFilter::Info,
303        }
304    }
305}
306
307#[derive(Debug, Clone, Default, Deserialize, Serialize)]
308pub struct FaceDbConfig {
309    pub url: String,
310}
311
312impl AppConfig {
313    pub fn get() -> &'static Mutex<AppConfig> {
314        // 使用MaybeUninit延迟初始化
315        static mut CONF: MaybeUninit<Mutex<AppConfig>> = MaybeUninit::uninit();
316        // Once带锁保证只进行一次初始化
317        static ONCE: Once = Once::new();
318
319        ONCE.call_once(|| unsafe {
320            CONF.as_mut_ptr().write(Mutex::new(AppConfig {
321                db_conf: DatabaseConfig {
322                    url: "".to_string(),
323                    logger_level: LevelFilter::Info,
324                },
325                webserver_conf: WebServerConfig {
326                    port: 10089i64,
327                    rsa_cert: String::new(),
328                    rsa_key: String::new(),
329                    rsa_password_private_key: String::new(),
330                    rsa_password_public_key: String::new(),
331                    upload_store_path: String::new(),
332                    upload_temp_path: String::new(),
333                    access_url_prefix: String::new(),
334                    resources_path: String::new(),
335                },
336                email_conf: EmailServerConfig::default(),
337                redis_conf: None,
338                gateway_address: None,
339                app_id: None,
340                app_secret: None,
341                logger_level: LevelFilter::Info,
342            }));
343        });
344        unsafe { &*CONF.as_ptr() }
345    }
346
347    pub fn init(conf_path: &str) {
348        AppConfig::get().lock().unwrap().load_yaml(conf_path);
349    }
350
351    pub fn load_yaml(&mut self, conf_path: &str) {
352        use yaml_rust::yaml;
353        // open file
354        let mut f = match File::open(conf_path) {
355            Ok(f) => f,
356            Err(_) => return,
357        };
358        let mut s = String::new();
359        let _ = f.read_to_string(&mut s).is_ok();
360        // f.read_to_string(&mut s).unwrap(); // read file content to s
361        // load string to yaml loader
362        let docs = yaml::YamlLoader::load_from_str(&s).unwrap();
363        // get first yaml hash doc
364        // get server value
365        // let server = yaml_doc["weapp"].clone();
366        let doc = &docs[0];
367        let db = &doc["database"];
368        let web = &doc["webserver"];
369        let email = &doc["email"];
370        let redis = &doc["redis"];
371        let reg = &doc["registry"];
372
373        let log_level = doc["log-level"]
374            .as_str()
375            .map(|s| match LevelFilter::from_str(s) {
376                Ok(ll) => ll,
377                Err(_) => LevelFilter::Info,
378            })
379            .unwrap_or_else(|| LevelFilter::Info);
380
381        let gatewaddr = reg["gateway_address"].as_str().map(|s| s.to_string());
382        let app_id = reg["app_id"].as_str().map(|s| s.to_string());
383        let app_secret = reg["app_secret"].as_str().map(|s| s.to_string());
384
385        let dbconf = DatabaseConfig {
386            url: db["url"].as_str().map(|s| s.to_owned()).unwrap_or_default(),
387            logger_level: if let Some(l) = db["log-level"].as_str() {
388                match LevelFilter::from_str(l) {
389                    Ok(level) => level,
390                    Err(_) => LevelFilter::Info,
391                }
392            } else {
393                LevelFilter::Info
394            },
395        };
396
397        let webconf = WebServerConfig {
398            port: web["port"].as_i64().unwrap_or(10089i64),
399            rsa_key: web["rsa_key"]
400                .as_str()
401                .map(|s| s.to_owned())
402                .unwrap_or_default(),
403            rsa_cert: web["rsa_cert"]
404                .as_str()
405                .map(|s| s.to_owned())
406                .unwrap_or_default(),
407            rsa_password_private_key: web["rsa_password_private_key"]
408                .as_str()
409                .map(|s| s.to_owned())
410                .unwrap_or_default(),
411            rsa_password_public_key: web["rsa_password_public_key"]
412                .as_str()
413                .map(|s| s.to_owned())
414                .unwrap_or_default(),
415            upload_store_path: web["upload-store-path"]
416                .as_str()
417                .map(|s| s.to_owned())
418                .unwrap_or_default(),
419            upload_temp_path: web["upload-temp-path"]
420                .as_str()
421                .map(|s| s.to_owned())
422                .unwrap_or_default(),
423            resources_path: web["resources-path"]
424                .as_str()
425                .map(|s| s.to_owned())
426                .unwrap_or_default(),
427            access_url_prefix: web["access-url-prefix"]
428                .as_str()
429                .map(|s| s.to_owned())
430                .unwrap_or_default(),
431        };
432        let emailconf = EmailServerConfig {
433            smtp_server: email["smtp-server"]
434                .as_str()
435                .map(|s| s.to_owned())
436                .unwrap_or_default(),
437            mine_email: email["account"]
438                .as_str()
439                .map(|s| s.to_owned())
440                .unwrap_or_default(),
441            password: email["password"]
442                .as_str()
443                .map(|s| s.to_owned())
444                .unwrap_or_default(),
445            port: email["port"]
446                .as_str()
447                .map(|s| s.to_owned())
448                .unwrap_or_default(),
449            ssl: email["ssl"].as_bool().unwrap_or_default(),
450        };
451
452        let redis_conf = if !redis.is_null() {
453            let pool = &redis["pool"];
454            Some(RedisConfig {
455                urls: if let Some(s) = redis["urls"].as_vec() {
456                    let mut u = vec![];
457                    for ps in s.clone() {
458                        if let Some(st) = ps.as_str() {
459                            u.push(st.to_string());
460                        }
461                    }
462                    u
463                } else {
464                    vec![]
465                },
466                database: redis["database"].as_i64().unwrap_or_default(),
467                username: redis["username"].as_str().map(|s| s.to_owned()),
468                password: redis["password"].as_str().map(|s| s.to_owned()),
469                instance_type: if let Some(s) = redis["instance-type"].as_str() {
470                    if s == "cluster" {
471                        Some(InstanceType::Cluster)
472                    } else {
473                        Some(InstanceType::Single)
474                    }
475                } else {
476                    Some(InstanceType::Single)
477                },
478                pool: RedisPoolConfig {
479                    connection_timeout: pool["connection_timeout"].as_i64().unwrap_or_default()
480                        as u64,
481                    max_size: pool["max_size"].as_i64().unwrap_or_default() as u32,
482                    mini_idel: pool["mini_idel"].as_i64().unwrap_or_default() as u32,
483                },
484            })
485        } else {
486            None
487        };
488
489        self.db_conf = dbconf;
490        self.webserver_conf = webconf;
491        self.email_conf = emailconf;
492        self.redis_conf = redis_conf;
493        self.gateway_address = gatewaddr;
494        self.app_id = app_id;
495        self.app_secret = app_secret;
496        self.logger_level = log_level;
497    }
498}
499
500#[cfg(unix)]
501pub fn set_file_permission(fcp: &str, mode: u32) {
502    use std::os::unix::fs::PermissionsExt;
503    let permissions = std::fs::Permissions::from_mode(mode);
504    match std::fs::set_permissions(fcp.clone(), permissions) {
505        Ok(_) => {}
506        Err(err) => {
507            log::warn!("Error on set permission: {}", err);
508        }
509    };
510}
511
512#[cfg(windows)]
513pub fn set_file_permission(fcp: &str, mode: u32) {
514    log::debug!(
515        "Updating the permission for {} to {} is ignored.",
516        fcp,
517        mode
518    );
519}
520
521/// 获取当前时间戮
522pub fn current_timestamp_secs() -> u64 {
523    SystemTime::now()
524        .duration_since(UNIX_EPOCH)
525        .unwrap()
526        .as_secs()
527}
528
529pub fn current_timestamp() -> u128 {
530    SystemTime::now()
531        .duration_since(UNIX_EPOCH)
532        .unwrap()
533        .as_millis()
534}
535
536#[allow(dead_code)]
537pub fn text_insert_br(oldtext: Option<String>) -> Option<String> {
538    if oldtext.is_none() {
539        None
540    } else {
541        let text = oldtext.unwrap();
542        let mut newtext = String::new();
543        let lst = text.lines().collect::<Vec<&str>>();
544        for stx in 0..lst.len() {
545            let sxtr = lst[stx];
546            newtext.push_str(sxtr);
547            if stx < lst.len() - 1 {
548                newtext.push_str("<br />");
549            }
550        }
551        Some(newtext)
552    }
553}
554
555pub fn file_size_format(len: usize) -> String {
556    let t = len as f64 / (1024f64 * 1024f64 * 1024f64 * 1024f64);
557    if t >= 1.00f64 {
558        return format!("{:.02}TB", t);
559    }
560    let g = len as f64 / (1024f64 * 1024f64 * 1024f64);
561    if g >= 1.00f64 {
562        return format!("{:.02}GB", g);
563    }
564    let m = len as f64 / (1024f64 * 1024f64);
565    if m >= 1.00f64 {
566        return format!("{:.02}MB", m);
567    }
568    let k = len as f64 / 1024f64;
569    if k >= 1.00f64 {
570        return format!("{:.02}KB", k);
571    }
572
573    format!("{} B", len)
574}
575
576mod mail;
577pub use mail::*;
578
579mod queue;
580pub use queue::*;
581
582mod global_data;
583pub use global_data::*;
584
585#[allow(dead_code)]
586pub fn parse_query(query_string: &str) -> HashMap<String, String> {
587    if query_string.is_empty() {
588        return HashMap::new();
589    }
590    let q_a: Vec<&str> = query_string.split('&').collect();
591    let mut res: HashMap<String, String> = HashMap::new();
592    use percent_encoding::percent_decode;
593    for s in q_a {
594        // let ss: &str = s;
595        let kv: Vec<&str> = s.split('=').collect();
596        let kvalue = percent_decode(kv[1].as_bytes()).decode_utf8().unwrap();
597        res.insert(kv[0].to_string(), kvalue.to_string());
598    }
599    res
600}
601
602#[allow(dead_code)]
603pub fn parse_query_as_value(query_string: &str) -> Value {
604    if query_string.is_empty() {
605        return Value::Null;
606    }
607    let q_a: Vec<&str> = query_string.split('&').collect();
608    let mut res: HashMap<String, String> = HashMap::new();
609    use percent_encoding::percent_decode;
610    for s in q_a {
611        // let ss: &str = s;
612        let kv: Vec<&str> = s.split('=').collect();
613        let kvalue = percent_decode(kv[1].as_bytes()).decode_utf8().unwrap();
614        res.insert(kv[0].to_string(), kvalue.to_string());
615    }
616
617    match serde_json::to_value(res) {
618        Ok(tv) => tv,
619        Err(_) => Value::Null,
620    }
621}
622
623#[allow(dead_code)]
624pub fn get_hash_value(query_params: &HashMap<String, String>, key: &str) -> String {
625    match query_params.get(key) {
626        Some(val) => val.clone(),
627        None => "".to_owned(),
628    }
629}
630
631#[allow(dead_code)]
632pub fn calc_file_hash(filename: &str) -> Option<String> {
633    let mut file = match File::open(filename) {
634        Ok(file) => file,
635        Err(err) => {
636            log::info!("error for open file {} with err {}", filename, err);
637            return None;
638        }
639    };
640
641    let mut hasher = Sha256::new();
642    let mut buffer = [0; 1024 * 1024];
643    loop {
644        match file.read(&mut buffer) {
645            Ok(ts) => {
646                if ts == 1024 * 1024 {
647                    hasher.update(&buffer);
648                } else if ts > 0 {
649                    let lesbuf = &buffer[0..ts];
650                    hasher.update(lesbuf);
651                } else {
652                    break;
653                }
654            }
655            Err(err) => {
656                log::info!("read file {} with err {}", filename, err);
657                return None;
658            }
659        };
660    }
661
662    let result = hasher.finish();
663    Some(hex::encode(result))
664}
665
666pub fn datetime_to_date(dt: &Option<rbatis::DateTimeNative>) -> Option<rbatis::DateNative> {
667    dt.as_ref().map(|t| rbatis::DateNative { inner: t.date() })
668}
669
670pub fn pure_datetime_to_date(dt: &rbatis::DateTimeNative) -> rbatis::DateNative {
671    rbatis::DateNative { inner: dt.date() }
672}
673
674pub fn datetime_diff(last: &rbatis::DateTimeNative, prev: &rbatis::DateTimeNative) -> i64 {
675    last.and_utc().timestamp() - prev.and_utc().timestamp()
676}