1pub mod action;
3pub mod addon;
5pub mod module;
7pub mod request;
9pub mod swagger;
11#[allow(clippy::too_many_arguments)]
12#[cfg(any(
13 feature = "sqlite",
14 feature = "mssql",
15 feature = "mysql",
16 feature = "pgsql"
17))]
18pub mod tables;
20pub mod tools;
22
23use crate::action::Action;
24use crate::addon::Addon;
25use crate::module::Module;
26use crate::request::Request;
27use crate::tools::{Tools, ToolsConfig};
28#[cfg(any(
29 feature = "mysql",
30 feature = "sqlite",
31 feature = "mssql",
32 feature = "pgsql"
33))]
34use br_db::types::TableOptions;
35use json::{array, object, JsonValue};
36use lazy_static::lazy_static;
37use log::{error, info};
38use std::cell::RefCell;
39use std::collections::HashMap;
40use std::path::PathBuf;
41use std::sync::LazyLock;
42use std::sync::{Mutex, OnceLock};
43use std::{fs, thread};
44
45lazy_static! {
46 static ref CONFIG: Mutex<HashMap<String, JsonValue>> = Mutex::new(HashMap::new());
47}
48static PLUGIN_TOOLS: OnceLock<Tools> = OnceLock::new();
50static GLOBAL_HANDLE: LazyLock<Mutex<HashMap<String, String>>> =
52 LazyLock::new(|| Mutex::new(HashMap::new()));
53pub static GLOBAL_ADDONS: OnceLock<Vec<String>> = OnceLock::new();
55pub static GLOBAL_MODULE: OnceLock<Vec<String>> = OnceLock::new();
57pub static GLOBAL_ACTION: OnceLock<Vec<String>> = OnceLock::new();
59
60thread_local! {
61 static GLOBAL_DATA: RefCell<JsonValue> = RefCell::new(object!{});
63}
64pub trait Plugin {
66 fn addon(name: &str) -> Result<Box<dyn Addon>, String>;
68 fn module(name: &str) -> Result<Box<dyn Module>, String> {
70 let (addon_name, module_name) = Self::split2(name)?;
71 Self::addon(addon_name)?.module(module_name)
72 }
73 fn action(name: &str) -> Result<Box<dyn Action>, String> {
75 let (addon_name, module_name, action_name) = Self::split3(name)?;
76 Self::addon(addon_name)?
77 .module(module_name)?
78 .action(action_name)
79 }
80 fn api_run(name: &str, request: Request) -> Result<JsonValue, String> {
82 let (addon_name, module_name, action_name) = Self::split3(name)?;
83 match Self::addon(addon_name)?
84 .module(module_name)?
85 .action(action_name)?
86 .run(request)
87 {
88 Ok(e) => Ok(e.data),
89 Err(e) => Err(e.message),
90 }
91 }
92 fn load_tools(config: ToolsConfig) -> Result<(), String> {
95 if PLUGIN_TOOLS.get().is_some() {
96 return Ok(());
97 }
98 let tools = Tools::new(config)?;
99 let _ = PLUGIN_TOOLS.set(tools);
100 Ok(())
101 }
102 fn get_tools() -> Tools {
104 PLUGIN_TOOLS.get().expect("tools not initialized").clone()
105 }
106 fn load_config(name: &str, config: JsonValue) {
108 if let Ok(mut cfg) = CONFIG.lock() {
109 cfg.insert(name.into(), config.clone());
110 }
111 }
112 #[cfg(any(
116 feature = "mysql",
117 feature = "sqlite",
118 feature = "mssql",
119 feature = "pgsql"
120 ))]
121 fn init_db() -> Result<(), String> {
122 info!("=============数据库更新开始=============");
123 let mut tables = HashMap::new();
124 let sql = PathBuf::from("sql");
126
127 if let Some(sql_path) = sql.to_str() {
129 match fs::remove_dir_all(sql_path) {
130 Ok(_) => {}
131 Err(e) => {
132 #[cfg(any(
133 feature = "mysql",
134 feature = "sqlite",
135 feature = "mssql",
136 feature = "pgsql"
137 ))]
138 error!("目录删除失败: {e}");
139 }
140 }
141 }
142
143 if let Some(sql_path) = sql.to_str() {
145 match fs::create_dir_all(sql_path) {
146 Ok(_) => {}
147 Err(e) => error!("目录创建失败: {e}"),
148 }
149 }
150
151 let sql_file = sql.join("sql.json");
152 let mut db_install = array![];
153
154 for api_name in GLOBAL_MODULE.wait() {
155 let module = match Self::module(api_name) {
156 Ok(e) => e,
157 Err(e) => {
158 error!("加载模型错误: {}", e);
159 continue;
160 }
161 };
162 if !module.table() {
163 continue;
164 }
165 if !tables.contains_key(module._table_name()) {
166 tables.insert(module._table_name(), module);
167 }
168 }
169
170 for (_, module) in tables.iter_mut() {
171 let mut opt = TableOptions::default();
172
173 let unique = module
174 .table_unique()
175 .iter()
176 .map(|x| (*x).to_string())
177 .collect::<Vec<String>>();
178 let unique = unique.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
179
180 let index = module
181 .table_index()
182 .iter()
183 .map(|x| x.iter().map(|&y| y.to_string()).collect::<Vec<String>>())
184 .collect::<Vec<Vec<String>>>();
185 let index = index
186 .iter()
187 .map(|x| x.iter().map(|y| y.as_str()).collect::<Vec<&str>>())
188 .collect::<Vec<Vec<&str>>>();
189
190 opt.set_table_name(module._table_name());
191 opt.set_table_title(module.title());
192 opt.set_table_key(module.table_key());
193 opt.set_table_fields(module.fields().clone());
194 opt.set_table_unique(unique.clone());
195 opt.set_table_index(index.clone());
196 opt.set_table_partition(module.table_partition());
197 opt.set_table_partition_columns(module.table_partition_columns());
198 let mut tools = Self::get_tools();
199 let sql = tools
200 .db
201 .table(module._table_name())
202 .fetch_sql()
203 .table_create(opt.clone());
204
205 let mut exec_tools = Self::get_tools();
206 let table_exists = exec_tools.db.table_is_exist(module._table_name());
207
208 let update_sql = if table_exists {
209 tools
210 .db
211 .table(module._table_name())
212 .fetch_sql()
213 .table_update(opt.clone())
214 } else {
215 JsonValue::Null
216 };
217 db_install
218 .push(object! {
219 table:module._table_name(),
220 field:module.fields().clone(),
221 key:module.table_key(),
222 index:index.clone(),
223 unique:unique.clone(),
224 sql:sql,
225 update_sql:update_sql
226 })
227 .ok();
228 if let Err(e) = fs::write(sql_file.clone(), db_install.to_string()) {
229 error!("写入SQL文件失败: {e}");
230 }
231
232 if table_exists {
233 let res = exec_tools.db.table_update(opt);
234 match res.as_i32().unwrap_or(-1) {
235 -1 => {}
236 0 => {
237 info!("数据库更新情况: {} 失败", module._table_name());
238 }
239 1 => {
240 info!("数据库更新情况: {} 成功", module._table_name());
241 }
242 _ => {}
243 }
244 } else {
245 let res = exec_tools.db.table_create(opt);
246 info!("安装完成情况: {} {}", module._table_name(), res);
247 }
248 }
249 info!("=============数据库更新完成=============");
250 Ok(())
251 }
252 #[cfg(any(
253 feature = "mysql",
254 feature = "sqlite",
255 feature = "mssql",
256 feature = "pgsql"
257 ))]
258 fn init_data() -> Result<(), String> {
259 let mut exec_tools = Self::get_tools();
260 for api_name in GLOBAL_MODULE.wait() {
261 let mut module = match Self::module(api_name) {
262 Ok(e) => e,
263 Err(e) => {
264 error!("加载模型错误: {}", e);
265 continue;
266 }
267 };
268 if !module.table() {
269 continue;
270 }
271 let init_data = module.init_data();
272 if !init_data.is_empty() {
273 let count = exec_tools.db.table(module._table_name()).count();
274 if count.is_empty() {
275 let r = exec_tools.db
276 .table(module._table_name())
277 .insert_all(init_data);
278 info!("初始化【{}】数据 {} 条", module._table_name(), r.len());
279 }
280 }
281 }
282 info!("所有模块数据初始化完成");
283 Ok(())
284 }
285
286 fn handles() {
288 let mut map = match GLOBAL_HANDLE.lock() {
289 Ok(m) => m,
290 Err(_) => return,
291 };
292
293 for api in GLOBAL_ADDONS.wait() {
294 let mut addon_name = match Self::addon(api.as_str()) {
295 Ok(e) => e,
296 Err(e) => {
297 error!("插件: {api} 加载错误 {e}");
298 continue;
299 }
300 };
301 if map.get(addon_name.name()).is_none() {
302 map.insert(addon_name.name().to_string(), addon_name.name().to_string());
303 thread::spawn(move || addon_name.handle());
304 }
305 }
306 for api in GLOBAL_MODULE.wait() {
307 let mut module_name = match Self::module(api.as_str()) {
308 Ok(e) => e,
309 Err(e) => {
310 error!("插件: {api} 加载错误 {e}");
311 continue;
312 }
313 };
314 if map.get(module_name.module_name()).is_none() {
315 map.insert(
316 module_name.module_name().to_string(),
317 module_name.module_name().to_string(),
318 );
319 thread::spawn(move || module_name.handle());
320 }
321 }
322 }
323 fn swagger(
325 title: &str,
326 description: &str,
327 version: &str,
328 uaturl: &str,
329 produrl: &str,
330 tags: JsonValue,
331 paths: JsonValue,
332 ) -> JsonValue {
333 let info = object! {
334 openapi:"3.0.0",
335 info:{
336 title:title,
337 description:description,
338 version:version
339 },
340 components: {
341 securitySchemes: {
342 BearerToken: {
343 "type": "http",
344 "scheme": "bearer",
345 "bearerFormat": "Token"
346 }
347 }
348 },
349 tags:tags,
350 security: [
351 {
352 "BearerToken": []
353 }
354 ],
355 servers:[
356 {
357 "url":uaturl,
358 "description": "测试地址"
359 },
360 {
361 "url":produrl,
362 "description": "正式地址"
363 }
364 ],
365 paths:paths
366 };
367 info
368 }
369 fn generate_api_list(
371 apipath: PathBuf,
372 path: PathBuf,
373 index: usize,
374 ) -> Result<Vec<String>, String> {
375 #[cfg(debug_assertions)]
376 {
377 let mut plugin_list = Vec::new();
378 if path.is_dir() {
379 let res = fs::read_dir(path);
380 match res {
381 Ok(entries) => {
382 for entry in entries {
383 let entry = match entry {
384 Ok(e) => e,
385 Err(e) => return Err(e.to_string()),
386 };
387 let path = entry.path();
388 if path.is_dir() {
389 let path_str = match path.to_str() {
390 Some(s) => s,
391 None => continue,
392 };
393 let res = Self::generate_api_list(
394 apipath.clone(),
395 path_str.parse().unwrap_or_default(),
396 index + 1,
397 )?;
398 plugin_list.extend(res);
399 } else if path.is_file() {
400 let path_str = match path.to_str() {
401 Some(s) => s,
402 None => continue,
403 };
404 if path_str.ends_with("mod.rs") {
405 continue;
406 }
407 let addon = path
408 .parent()
409 .and_then(|p| p.parent())
410 .and_then(|p| p.file_name())
411 .and_then(|n| n.to_str())
412 .unwrap_or_default();
413 let model = path
414 .parent()
415 .and_then(|p| p.file_name())
416 .and_then(|n| n.to_str())
417 .unwrap_or_default();
418 let action = path
419 .file_name()
420 .and_then(|n| n.to_str())
421 .unwrap_or_default()
422 .trim_end_matches(".rs");
423 let api = format!("{addon}.{model}.{action}");
424 match Self::action(api.as_str()) {
425 Ok(e) => plugin_list.push(e.api()),
426 Err(_) => continue,
427 }
428 }
429 }
430 }
431 Err(e) => return Err(e.to_string()),
432 }
433 }
434 if index == 0 {
435 if let Some(parent) = apipath.clone().parent() {
436 let _ = fs::create_dir_all(parent);
437 }
438 let _ = fs::write(&apipath, JsonValue::from(plugin_list.clone()).to_string());
439 info!(
440 "=============API数量: {} 条=============",
441 plugin_list.len()
442 );
443 Self::_load_apis(plugin_list.clone())?;
444 }
445 Ok(plugin_list)
446 }
447 #[cfg(not(debug_assertions))]
448 {
449 let apis = fs::read_to_string(&apipath).unwrap_or_default();
450 let apis = json::parse(&apis).unwrap_or(array![]);
451 let apis = apis
452 .members()
453 .map(|x| x.as_str().unwrap_or_default().to_string())
454 .collect::<Vec<String>>();
455 info!("=============API数量: {} 条=============", apis.len());
456 Self::_load_apis(apis.clone())?;
457 Ok(apis)
458 }
459 }
460 fn _load_apis(apis: Vec<String>) -> Result<(), String> {
462 let mut action_list = vec![];
463 let mut module_list = vec![];
464 let mut addons_list = vec![];
465 for api in apis {
466 let action = Self::action(api.as_str())?;
467 action_list.push(action.api());
468 if !module_list.contains(&action.module_name()) {
469 module_list.push(action.module_name());
470 }
471 if !addons_list.contains(&action.addon_name()) {
472 addons_list.push(action.addon_name());
473 }
474 }
475 let _ = GLOBAL_ACTION.set(action_list);
476 let _ = GLOBAL_MODULE.set(module_list);
477 let _ = GLOBAL_ADDONS.set(addons_list);
478 Ok(())
479 }
480 fn set_global_data(key: &str, value: JsonValue) {
482 GLOBAL_DATA.with(|data| {
483 data.borrow_mut()[key] = value;
484 });
485 }
486 fn get_global_data() -> JsonValue {
488 GLOBAL_DATA.with(|data| data.borrow().clone())
489 }
490 fn get_global_data_key(key: &str) -> JsonValue {
492 GLOBAL_DATA.with(|data| data.borrow()[key].clone())
493 }
494 #[inline]
495 fn split2(name: &str) -> Result<(&str, &str), String> {
496 let t = name.split('.').collect::<Vec<&str>>();
497 if t.len() < 2 {
498 return Err(format!("模型格式不正确: {name}"));
499 }
500 Ok((t[0], t[1]))
501 }
502
503 #[inline]
504 fn split3(name: &str) -> Result<(&str, &str, &str), String> {
505 if let Some((a, rest)) = name.split_once('.') {
506 if let Some((b, c)) = rest.split_once('.') {
507 if !a.is_empty() && !b.is_empty() && !c.is_empty() {
508 Ok((a, b, c))
509 } else {
510 Err("动作格式不正确".to_string())
511 }
512 } else {
513 Err("动作格式不正确".to_string())
514 }
515 } else {
516 Err("动作格式不正确".to_string())
517 }
518 }
519}
520#[derive(Debug, Clone)]
522pub struct ApiResponse {
523 pub types: ApiType,
524 pub code: i32,
525 pub message: String,
526 pub data: JsonValue,
527 pub success: bool,
528 pub timestamp: i64,
529}
530impl ApiResponse {
531 #[must_use]
532 pub fn json(&self) -> JsonValue {
533 match self.types {
534 ApiType::Json => object! {
535 code: self.code,
536 message: self.message.clone(),
537 data: self.data.clone(),
538 success: self.success
539 },
540 ApiType::Redirect
541 | ApiType::Download
542 | ApiType::Preview
543 | ApiType::Txt
544 | ApiType::Html => self.data.clone(),
545 }
546 }
547 pub fn swagger(&mut self) -> JsonValue {
548 let type_str = self.types.str();
549 let mut content = object! {};
550 content[type_str] = object! {};
551 content[type_str]["schema"]["type"] = if self.data.is_array() {
552 "array"
553 } else {
554 "object"
555 }
556 .into();
557 content[type_str]["schema"]["properties"] = self.data.clone();
558
559 content[type_str]["schema"]["type"] = match content[type_str]["schema"]["type"]
560 .as_str()
561 .unwrap_or("object")
562 {
563 "int" => "integer".into(),
564 _ => content[type_str]["schema"]["type"].clone(),
565 };
566 object! {
567 "description":self.message.clone(),
568 "content":content
569 }
570 }
571 pub fn success(data: JsonValue, mut message: &str) -> Self {
572 if message.is_empty() {
573 message = "success";
574 }
575 Self {
576 success: true,
577 types: ApiType::Json,
578 code: 0,
579 message: message.to_string(),
580 data,
581 timestamp: br_fields::datetime::Timestamp::timestamp(),
582 }
583 }
584 pub fn fail(code: i32, message: &str) -> Self {
585 Self {
586 types: ApiType::Json,
587 code,
588 message: message.to_string(),
589 data: JsonValue::Null,
590 success: false,
591 timestamp: br_fields::datetime::Timestamp::timestamp(),
592 }
593 }
594 pub fn error(data: JsonValue, message: &str) -> Self {
595 Self {
596 types: ApiType::Json,
597 code: -1,
598 message: message.to_string(),
599 data,
600 success: false,
601 timestamp: br_fields::datetime::Timestamp::timestamp(),
602 }
603 }
604 pub fn redirect(url: &str) -> Self {
606 Self {
607 types: ApiType::Redirect,
608 code: 0,
609 message: "".to_string(),
610 data: url.into(),
611 success: true,
612 timestamp: br_fields::datetime::Timestamp::timestamp(),
613 }
614 }
615 pub fn download(filename: &str) -> Self {
617 Self {
618 types: ApiType::Download,
619 code: 0,
620 message: "".to_string(),
621 data: filename.into(),
622 success: true,
623 timestamp: br_fields::datetime::Timestamp::timestamp(),
624 }
625 }
626 pub fn preview(filename: &str) -> Self {
628 Self {
629 types: ApiType::Preview,
630 code: 0,
631 message: "".to_string(),
632 data: filename.into(),
633 success: true,
634 timestamp: br_fields::datetime::Timestamp::timestamp(),
635 }
636 }
637 pub fn txt(txt: &str) -> Self {
639 Self {
640 types: ApiType::Txt,
641 code: 0,
642 message: "".to_string(),
643 data: txt.into(),
644 success: true,
645 timestamp: br_fields::datetime::Timestamp::timestamp(),
646 }
647 }
648 pub fn html(data: &str) -> Self {
649 Self {
650 types: ApiType::Html,
651 code: 0,
652 message: "".to_string(),
653 data: data.into(),
654 success: true,
655 timestamp: br_fields::datetime::Timestamp::timestamp(),
656 }
657 }
658}
659impl Default for ApiResponse {
660 fn default() -> Self {
661 Self {
662 types: ApiType::Json,
663 code: 0,
664 message: "".to_string(),
665 data: JsonValue::Null,
666 success: false,
667 timestamp: br_fields::datetime::Timestamp::timestamp(),
668 }
669 }
670}
671#[derive(Debug, Clone)]
673pub enum ApiType {
674 Json,
676 Redirect,
679 Download,
682 Preview,
685 Txt,
687 Html,
689}
690impl ApiType {
691 #[must_use]
692 pub fn str(&self) -> &'static str {
693 match self {
694 Self::Json => "application/json",
695 Self::Redirect | Self::Download | Self::Preview => "text/html",
696 Self::Txt => "text/plain",
697 Self::Html => "text/html",
698 }
699 }
700}