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