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