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 for api in GLOBAL_MODULE.wait() {
202 let mut addon_name = match Self::addon(api.as_str()) {
203 Ok(e) => e,
204 Err(e) => {
205 error!("插件: {api} 加载错误 {e}");
206 continue;
207 }
208 };
209 if map.get(addon_name.name()).is_none() {
210 map.insert(addon_name.name().to_string(), addon_name.name().to_string());
211 thread::spawn(move || addon_name.handle());
212 }
213 let mut module_name = match Self::module(api.as_str()) {
214 Ok(e) => e,
215 Err(e) => {
216 error!("插件: {api} 加载错误 {e}");
217 continue;
218 }
219 };
220 if map.get(module_name.module_name()).is_none() {
221 map.insert(module_name.module_name().to_string(), module_name.module_name().to_string());
222 thread::spawn(move || module_name.handle());
223 }
224 }
225 }
226 fn swagger(
228 title: &str,
229 description: &str,
230 version: &str,
231 uaturl: &str,
232 produrl: &str,
233 tags: JsonValue,
234 paths: JsonValue,
235 ) -> JsonValue {
236 let info = object! {
237 openapi:"3.0.0",
238 info:{
239 title:title,
240 description:description,
241 version:version
242 },
243 components: {
244 securitySchemes: {
245 BearerToken: {
246 "type": "http",
247 "scheme": "bearer",
248 "bearerFormat": "Token"
249 }
250 }
251 },
252 tags:tags,
253 security: [
254 {
255 "BearerToken": []
256 }
257 ],
258 servers:[
259 {
260 "url":uaturl,
261 "description": "测试地址"
262 },
263 {
264 "url":produrl,
265 "description": "正式地址"
266 }
267 ],
268 paths:paths
269 };
270 info
271 }
272 fn generate_api_list(apipath: PathBuf, path: PathBuf, index: usize) -> Result<Vec<String>, String> {
274 #[cfg(debug_assertions)]
275 {
276 let mut plugin_list = Vec::new();
277 if path.is_dir() {
278 let res = fs::read_dir(path);
279 match res {
280 Ok(entries) => {
281 for entry in entries {
282 let entry = match entry {
283 Ok(e) => e,
284 Err(e) => {
285 return Err(e.to_string())
286 }
287 };
288 let path = entry.path();
289 if path.is_dir() {
290 let res = Self::generate_api_list(
291 apipath.clone(),
292 path.to_str().unwrap().parse().unwrap(),
293 index + 1,
294 )?;
295 plugin_list.extend(res);
296 } else if path.is_file() {
297 if path.to_str().unwrap().ends_with("mod.rs") {
298 continue;
299 }
300 let addon = path.parent().unwrap().parent().unwrap().file_name().unwrap().to_str().unwrap();
301 let model = path.parent().unwrap().file_name().unwrap().to_str().unwrap();
302 let action = path.file_name().unwrap().to_str().unwrap().trim_end_matches(".rs");
303 let api = format!("{addon}.{model}.{action}");
304 match Self::action(api.as_str()) {
305 Ok(e) => plugin_list.push(e.api()),
306 Err(_) => continue
307 }
308 }
309 }
310 }
311 Err(e) => return Err(e.to_string())
312 }
313 }
314 if index == 0 {
315 fs::create_dir_all(apipath.clone().parent().unwrap()).unwrap();
316 fs::write(apipath, JsonValue::from(plugin_list.clone()).to_string()).unwrap();
317 info!("=============API数量: {} 条=============", plugin_list.len());
318 Self::_load_apis(plugin_list.clone())?;
319 }
320 Ok(plugin_list)
321 }
322 #[cfg(not(debug_assertions))]
323 {
324 let apis = fs::read_to_string(apipath).unwrap();
325 let apis = json::parse(&apis).unwrap();
326 let apis = apis.members().map(|x| x.as_str().unwrap().to_string()).collect::<Vec<String>>();
327 info!("=============API数量: {} 条=============", apis.len());
328 Self::_load_apis(apis.clone())?;
329 Ok(apis)
330 }
331 }
332 fn _load_apis(apis: Vec<String>) -> Result<(), String> {
334 let mut action_list = vec![];
335 let mut module_list = vec![];
336 let mut addons_list = vec![];
337 for api in apis {
338 let action = Self::action(api.as_str())?;
339 action_list.push(action.api());
340 if !module_list.contains(&action.module_name()) {
341 module_list.push(action.module_name());
342 }
343 if !addons_list.contains(&action.addon_name()) {
344 addons_list.push(action.addon_name());
345 }
346 }
347 let _ = GLOBAL_ACTION.set(action_list);
348 let _ = GLOBAL_MODULE.set(module_list);
349 let _ = GLOBAL_ADDONS.set(addons_list);
350 Ok(())
351 }
352 fn set_global_data(key: &str, value: JsonValue) {
354 GLOBAL_DATA.with(|data| {
355 data.borrow_mut()[key] = value;
356 });
357 }
358 fn get_global_data() -> JsonValue {
360 GLOBAL_DATA.with(|data| {
361 data.borrow().clone()
362 })
363 }
364 fn get_global_data_key(key: &str) -> JsonValue {
366 GLOBAL_DATA.with(|data| {
367 data.borrow()[key].clone()
368 })
369 }
370 #[inline]
372 fn split2(name: &str) -> Result<(&str, &str), String> {
373 let t = name.split(".").collect::<Vec<&str>>();
374 if t.len() < 2 {
375 return Err(format!("模型格式不正确: {name}"));
376 }
377 Ok((t[0], t[1]))
378 }
379
380 #[inline]
382 fn split3(name: &str) -> Result<(&str, &str, &str), String> {
383 if let Some((a, rest)) = name.split_once('.') {
384 if let Some((b, c)) = rest.split_once('.') {
385 if !a.is_empty() && !b.is_empty() && !c.is_empty() {
386 Ok((a, b, c))
387 } else {
388 Err("动作格式不正确".to_string())
389 }
390 } else {
391 Err("动作格式不正确".to_string())
392 }
393 } else {
394 Err("动作格式不正确".to_string())
395 }
396 }
397}
398#[derive(Debug, Clone)]
400pub struct ApiResponse {
401 pub types: ApiType,
402 pub code: i32,
403 pub message: String,
404 pub data: JsonValue,
405 pub success: bool,
406 pub timestamp: i64,
407}
408impl ApiResponse {
409 pub fn json(self) -> JsonValue {
410 match self.types {
411 ApiType::Json => object! {
412 code: self.code,
413 message: self.message,
414 data: self.data,
415 success: self.success
416 },
417 ApiType::Redirect => self.data,
418 ApiType::Download => self.data,
419 ApiType::Preview => self.data,
420 ApiType::Txt => self.data,
421 ApiType::Html => self.data,
422 }
423 }
424 pub fn swagger(&mut self) -> JsonValue {
425 let mut content = object! {};
426 content[self.types.str().as_str()] = object! {};
427 content[self.types.str().as_str()]["schema"]["type"] = if self.data.is_array() {
428 "array"
429 } else {
430 "object"
431 }.into();
432 content[self.types.str().as_str()]["schema"]["properties"] = self.data.clone();
433
434 content[self.types.str().as_str()]["schema"]["type"] = match content[self.types.str().as_str()]["schema"]["type"].as_str().unwrap() {
435 "int" => "integer".into(),
436 _ => content[self.types.str().as_str()]["schema"]["type"].clone(),
437 };
438 let data = object! {
439 "description":self.message.clone(),
440 "content":content
441 };
442 data
443 }
444 pub fn success(data: JsonValue, mut message: &str) -> Self {
445 if message.is_empty() {
446 message = "success";
447 }
448 Self {
449 success: true,
450 types: ApiType::Json,
451 code: 0,
452 message: message.to_string(),
453 data,
454 timestamp: br_fields::datetime::Timestamp::timestamp(),
455 }
456 }
457 pub fn fail(code: i32, message: &str) -> Self {
458 Self {
459 types: ApiType::Json,
460 code,
461 message: message.to_string(),
462 data: JsonValue::Null,
463 success: false,
464 timestamp: br_fields::datetime::Timestamp::timestamp(),
465 }
466 }
467 pub fn error(data: JsonValue, message: &str) -> Self {
468 Self {
469 types: ApiType::Json,
470 code: -1,
471 message: message.to_string(),
472 data,
473 success: false,
474 timestamp: br_fields::datetime::Timestamp::timestamp(),
475 }
476 }
477 pub fn redirect(url: &str) -> Self {
479 Self {
480 types: ApiType::Redirect,
481 code: 0,
482 message: "".to_string(),
483 data: url.into(),
484 success: true,
485 timestamp: br_fields::datetime::Timestamp::timestamp(),
486 }
487 }
488 pub fn download(filename: &str) -> Self {
490 Self {
491 types: ApiType::Download,
492 code: 0,
493 message: "".to_string(),
494 data: filename.into(),
495 success: true,
496 timestamp: br_fields::datetime::Timestamp::timestamp(),
497 }
498 }
499 pub fn preview(filename: &str) -> Self {
501 Self {
502 types: ApiType::Preview,
503 code: 0,
504 message: "".to_string(),
505 data: filename.into(),
506 success: true,
507 timestamp: br_fields::datetime::Timestamp::timestamp(),
508 }
509 }
510 pub fn txt(txt: &str) -> Self {
512 Self {
513 types: ApiType::Txt,
514 code: 0,
515 message: "".to_string(),
516 data: txt.into(),
517 success: true,
518 timestamp: br_fields::datetime::Timestamp::timestamp(),
519 }
520 }
521 pub fn html(data: &str) -> Self {
522 Self {
523 types: ApiType::Html,
524 code: 0,
525 message: "".to_string(),
526 data: data.into(),
527 success: true,
528 timestamp: br_fields::datetime::Timestamp::timestamp(),
529 }
530 }
531}
532impl Default for ApiResponse {
533 fn default() -> Self {
534 Self {
535 types: ApiType::Json,
536 code: 0,
537 message: "".to_string(),
538 data: JsonValue::Null,
539 success: false,
540 timestamp: br_fields::datetime::Timestamp::timestamp(),
541 }
542 }
543}
544#[derive(Debug, Clone)]
546pub enum ApiType {
547 Json,
549 Redirect,
552 Download,
555 Preview,
558 Txt,
560 Html,
562}
563impl ApiType {
564 pub fn str(&mut self) -> String {
565 match self {
566 ApiType::Json => "application/json",
567 ApiType::Redirect | ApiType::Download | ApiType::Preview => "text/html",
568 ApiType::Txt => "text/plain",
569 ApiType::Html => "text/html",
570 }.to_string()
571 }
572}