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