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