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