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