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 let init_data = module.init_data();
249 if !init_data.is_empty() {
250 let count = exec_tools.db.table(module._table_name()).count();
251 if count.is_empty() {
252 let r = exec_tools
253 .db
254 .table(module._table_name())
255 .insert_all(init_data);
256 info!("初始化【{}】数据 {} 条", module._table_name(), r.len());
257 }
258 }
259 }
260 info!("=============数据库更新完成=============");
261 Ok(())
262 }
263
264 fn handles() {
266 let mut map = match GLOBAL_HANDLE.lock() {
267 Ok(m) => m,
268 Err(_) => return,
269 };
270
271 for api in GLOBAL_ADDONS.wait() {
272 let mut addon_name = match Self::addon(api.as_str()) {
273 Ok(e) => e,
274 Err(e) => {
275 error!("插件: {api} 加载错误 {e}");
276 continue;
277 }
278 };
279 if map.get(addon_name.name()).is_none() {
280 map.insert(addon_name.name().to_string(), addon_name.name().to_string());
281 thread::spawn(move || addon_name.handle());
282 }
283 }
284 for api in GLOBAL_MODULE.wait() {
285 let mut module_name = match Self::module(api.as_str()) {
286 Ok(e) => e,
287 Err(e) => {
288 error!("插件: {api} 加载错误 {e}");
289 continue;
290 }
291 };
292 if map.get(module_name.module_name()).is_none() {
293 map.insert(
294 module_name.module_name().to_string(),
295 module_name.module_name().to_string(),
296 );
297 thread::spawn(move || module_name.handle());
298 }
299 }
300 }
301 fn swagger(
303 title: &str,
304 description: &str,
305 version: &str,
306 uaturl: &str,
307 produrl: &str,
308 tags: JsonValue,
309 paths: JsonValue,
310 ) -> JsonValue {
311 let info = object! {
312 openapi:"3.0.0",
313 info:{
314 title:title,
315 description:description,
316 version:version
317 },
318 components: {
319 securitySchemes: {
320 BearerToken: {
321 "type": "http",
322 "scheme": "bearer",
323 "bearerFormat": "Token"
324 }
325 }
326 },
327 tags:tags,
328 security: [
329 {
330 "BearerToken": []
331 }
332 ],
333 servers:[
334 {
335 "url":uaturl,
336 "description": "测试地址"
337 },
338 {
339 "url":produrl,
340 "description": "正式地址"
341 }
342 ],
343 paths:paths
344 };
345 info
346 }
347 fn generate_api_list(
349 apipath: PathBuf,
350 path: PathBuf,
351 index: usize,
352 ) -> Result<Vec<String>, String> {
353 #[cfg(debug_assertions)]
354 {
355 let mut plugin_list = Vec::new();
356 if path.is_dir() {
357 let res = fs::read_dir(path);
358 match res {
359 Ok(entries) => {
360 for entry in entries {
361 let entry = match entry {
362 Ok(e) => e,
363 Err(e) => return Err(e.to_string()),
364 };
365 let path = entry.path();
366 if path.is_dir() {
367 let path_str = match path.to_str() {
368 Some(s) => s,
369 None => continue,
370 };
371 let res = Self::generate_api_list(
372 apipath.clone(),
373 path_str.parse().unwrap_or_default(),
374 index + 1,
375 )?;
376 plugin_list.extend(res);
377 } else if path.is_file() {
378 let path_str = match path.to_str() {
379 Some(s) => s,
380 None => continue,
381 };
382 if path_str.ends_with("mod.rs") {
383 continue;
384 }
385 let addon = path
386 .parent()
387 .and_then(|p| p.parent())
388 .and_then(|p| p.file_name())
389 .and_then(|n| n.to_str())
390 .unwrap_or_default();
391 let model = path
392 .parent()
393 .and_then(|p| p.file_name())
394 .and_then(|n| n.to_str())
395 .unwrap_or_default();
396 let action = path
397 .file_name()
398 .and_then(|n| n.to_str())
399 .unwrap_or_default()
400 .trim_end_matches(".rs");
401 let api = format!("{addon}.{model}.{action}");
402 match Self::action(api.as_str()) {
403 Ok(e) => plugin_list.push(e.api()),
404 Err(_) => continue,
405 }
406 }
407 }
408 }
409 Err(e) => return Err(e.to_string()),
410 }
411 }
412 if index == 0 {
413 if let Some(parent) = apipath.clone().parent() {
414 let _ = fs::create_dir_all(parent);
415 }
416 let _ = fs::write(&apipath, JsonValue::from(plugin_list.clone()).to_string());
417 info!(
418 "=============API数量: {} 条=============",
419 plugin_list.len()
420 );
421 Self::_load_apis(plugin_list.clone())?;
422 }
423 Ok(plugin_list)
424 }
425 #[cfg(not(debug_assertions))]
426 {
427 let apis = fs::read_to_string(&apipath).unwrap_or_default();
428 let apis = json::parse(&apis).unwrap_or(array![]);
429 let apis = apis
430 .members()
431 .map(|x| x.as_str().unwrap_or_default().to_string())
432 .collect::<Vec<String>>();
433 info!("=============API数量: {} 条=============", apis.len());
434 Self::_load_apis(apis.clone())?;
435 Ok(apis)
436 }
437 }
438 fn _load_apis(apis: Vec<String>) -> Result<(), String> {
440 let mut action_list = vec![];
441 let mut module_list = vec![];
442 let mut addons_list = vec![];
443 for api in apis {
444 let action = Self::action(api.as_str())?;
445 action_list.push(action.api());
446 if !module_list.contains(&action.module_name()) {
447 module_list.push(action.module_name());
448 }
449 if !addons_list.contains(&action.addon_name()) {
450 addons_list.push(action.addon_name());
451 }
452 }
453 let _ = GLOBAL_ACTION.set(action_list);
454 let _ = GLOBAL_MODULE.set(module_list);
455 let _ = GLOBAL_ADDONS.set(addons_list);
456 Ok(())
457 }
458 fn set_global_data(key: &str, value: JsonValue) {
460 GLOBAL_DATA.with(|data| {
461 data.borrow_mut()[key] = value;
462 });
463 }
464 fn get_global_data() -> JsonValue {
466 GLOBAL_DATA.with(|data| data.borrow().clone())
467 }
468 fn get_global_data_key(key: &str) -> JsonValue {
470 GLOBAL_DATA.with(|data| data.borrow()[key].clone())
471 }
472 #[inline]
473 fn split2(name: &str) -> Result<(&str, &str), String> {
474 let t = name.split('.').collect::<Vec<&str>>();
475 if t.len() < 2 {
476 return Err(format!("模型格式不正确: {name}"));
477 }
478 Ok((t[0], t[1]))
479 }
480
481 #[inline]
482 fn split3(name: &str) -> Result<(&str, &str, &str), String> {
483 if let Some((a, rest)) = name.split_once('.') {
484 if let Some((b, c)) = rest.split_once('.') {
485 if !a.is_empty() && !b.is_empty() && !c.is_empty() {
486 Ok((a, b, c))
487 } else {
488 Err("动作格式不正确".to_string())
489 }
490 } else {
491 Err("动作格式不正确".to_string())
492 }
493 } else {
494 Err("动作格式不正确".to_string())
495 }
496 }
497}
498#[derive(Debug, Clone)]
500pub struct ApiResponse {
501 pub types: ApiType,
502 pub code: i32,
503 pub message: String,
504 pub data: JsonValue,
505 pub success: bool,
506 pub timestamp: i64,
507}
508impl ApiResponse {
509 #[must_use]
510 pub fn json(&self) -> JsonValue {
511 match self.types {
512 ApiType::Json => object! {
513 code: self.code,
514 message: self.message.clone(),
515 data: self.data.clone(),
516 success: self.success
517 },
518 ApiType::Redirect
519 | ApiType::Download
520 | ApiType::Preview
521 | ApiType::Txt
522 | ApiType::Html => self.data.clone(),
523 }
524 }
525 pub fn swagger(&mut self) -> JsonValue {
526 let type_str = self.types.str();
527 let mut content = object! {};
528 content[type_str] = object! {};
529 content[type_str]["schema"]["type"] = if self.data.is_array() {
530 "array"
531 } else {
532 "object"
533 }
534 .into();
535 content[type_str]["schema"]["properties"] = self.data.clone();
536
537 content[type_str]["schema"]["type"] = match content[type_str]["schema"]["type"]
538 .as_str()
539 .unwrap_or("object")
540 {
541 "int" => "integer".into(),
542 _ => content[type_str]["schema"]["type"].clone(),
543 };
544 object! {
545 "description":self.message.clone(),
546 "content":content
547 }
548 }
549 pub fn success(data: JsonValue, mut message: &str) -> Self {
550 if message.is_empty() {
551 message = "success";
552 }
553 Self {
554 success: true,
555 types: ApiType::Json,
556 code: 0,
557 message: message.to_string(),
558 data,
559 timestamp: br_fields::datetime::Timestamp::timestamp(),
560 }
561 }
562 pub fn fail(code: i32, message: &str) -> Self {
563 Self {
564 types: ApiType::Json,
565 code,
566 message: message.to_string(),
567 data: JsonValue::Null,
568 success: false,
569 timestamp: br_fields::datetime::Timestamp::timestamp(),
570 }
571 }
572 pub fn error(data: JsonValue, message: &str) -> Self {
573 Self {
574 types: ApiType::Json,
575 code: -1,
576 message: message.to_string(),
577 data,
578 success: false,
579 timestamp: br_fields::datetime::Timestamp::timestamp(),
580 }
581 }
582 pub fn redirect(url: &str) -> Self {
584 Self {
585 types: ApiType::Redirect,
586 code: 0,
587 message: "".to_string(),
588 data: url.into(),
589 success: true,
590 timestamp: br_fields::datetime::Timestamp::timestamp(),
591 }
592 }
593 pub fn download(filename: &str) -> Self {
595 Self {
596 types: ApiType::Download,
597 code: 0,
598 message: "".to_string(),
599 data: filename.into(),
600 success: true,
601 timestamp: br_fields::datetime::Timestamp::timestamp(),
602 }
603 }
604 pub fn preview(filename: &str) -> Self {
606 Self {
607 types: ApiType::Preview,
608 code: 0,
609 message: "".to_string(),
610 data: filename.into(),
611 success: true,
612 timestamp: br_fields::datetime::Timestamp::timestamp(),
613 }
614 }
615 pub fn txt(txt: &str) -> Self {
617 Self {
618 types: ApiType::Txt,
619 code: 0,
620 message: "".to_string(),
621 data: txt.into(),
622 success: true,
623 timestamp: br_fields::datetime::Timestamp::timestamp(),
624 }
625 }
626 pub fn html(data: &str) -> Self {
627 Self {
628 types: ApiType::Html,
629 code: 0,
630 message: "".to_string(),
631 data: data.into(),
632 success: true,
633 timestamp: br_fields::datetime::Timestamp::timestamp(),
634 }
635 }
636}
637impl Default for ApiResponse {
638 fn default() -> Self {
639 Self {
640 types: ApiType::Json,
641 code: 0,
642 message: "".to_string(),
643 data: JsonValue::Null,
644 success: false,
645 timestamp: br_fields::datetime::Timestamp::timestamp(),
646 }
647 }
648}
649#[derive(Debug, Clone)]
651pub enum ApiType {
652 Json,
654 Redirect,
657 Download,
660 Preview,
663 Txt,
665 Html,
667}
668impl ApiType {
669 #[must_use]
670 pub fn str(&self) -> &'static str {
671 match self {
672 Self::Json => "application/json",
673 Self::Redirect | Self::Download | Self::Preview => "text/html",
674 Self::Txt => "text/plain",
675 Self::Html => "text/html",
676 }
677 }
678}