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