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