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