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"))]
20use br_db::types::TableOptions;
21use json::{array, object, JsonValue};
22use lazy_static::lazy_static;
23use log::info;
24use std::collections::HashMap;
25use std::path::PathBuf;
26use std::sync::Mutex;
27use std::{fs, io};
28
29lazy_static! {
30 static ref PLUGIN_TOOLS: Mutex<HashMap<String,Tools>> =Mutex::new(HashMap::new());
32}
33
34pub trait Plugin {
36 fn addon(name: &str) -> Result<Box<dyn Addon>, String>;
38 fn module(name: &str) -> Result<Box<dyn Module>, String> {
40 let res = name.split(".").collect::<Vec<&str>>();
41 if res.len() < 2 {
42 return Err("模型格式不正确".to_string());
43 }
44 Self::addon(res[0])?.module(res[1])
45 }
46 fn action(name: &str) -> Result<Box<dyn Action>, String> {
48 let res = name.split(".").collect::<Vec<&str>>();
49 if res.len() < 3 {
50 return Err("动作格式不正确".to_string());
51 }
52 Self::addon(res[0])?.module(res[1])?.action(res[2])
53 }
54 fn api_run(name: &str, request: Request) -> Result<JsonValue, String> {
56 let res = name.split(".").collect::<Vec<&str>>();
57 if res.len() != 3 {
58 return Err("action格式不正确".to_string());
59 }
60 match Self::addon(res[0])?
61 .module(res[1])?
62 .action(res[2])?
63 .run(request)
64 {
65 Ok(e) => Ok(e.data),
66 Err(e) => Err(e.message),
67 }
68 }
69 fn api(name: &str) -> Result<Box<dyn Action>, String> {
71 let res = name.split(".").collect::<Vec<&str>>();
72 if res.len() != 3 {
73 return Err("api 格式不正确".to_string());
74 }
75 Self::addon(res[0])?.module(res[1])?.action(res[2])
76 }
77 fn load_tools(config: ToolsConfig) -> Result<(), String> {
80 if PLUGIN_TOOLS.lock().unwrap().get("tools").is_none() {
81 let res = Tools::new(config)?;
82 PLUGIN_TOOLS
83 .lock()
84 .unwrap()
85 .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 #[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql"))]
99 fn init_db(file_path: PathBuf) -> Result<Vec<String>, String> {
100 info!("=============数据库更新开始=============");
101 let list = match fs::read_to_string(file_path.clone()) {
102 Ok(e) => json::parse(&e).unwrap_or(array![]),
103 Err(e) => return Err(format!("加载API清单失败: {}", e)),
104 };
105 let mut api_list = vec![];
106 let mut table_name_list = vec![];
107 for api_name in list.members() {
108 let api_info = api_name
109 .as_str()
110 .unwrap_or("")
111 .split(".")
112 .collect::<Vec<&str>>();
113 let mut addon = match Self::addon(api_info[0]) {
114 Ok(e) => e,
115 Err(_) => {
116 continue;
117 }
118 };
119 let mut module = match addon.module(api_info[1]) {
120 Ok(e) => e,
121 Err(_) => {
122 continue;
123 }
124 };
125 if !module.table() {
126 continue;
127 }
128 api_list.push(api_name.to_string().clone());
129
130 let mut opt = TableOptions::default();
131
132 let unique = module
133 .table_unique()
134 .iter()
135 .map(|x| x.to_string())
136 .collect::<Vec<String>>();
137 let unique = unique.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
138
139 let index = module
140 .table_index()
141 .iter()
142 .map(|x| x.iter().map(|&y| y.to_string()).collect::<Vec<String>>())
143 .collect::<Vec<Vec<String>>>();
144 let index = index
145 .iter()
146 .map(|x| x.iter().map(|y| y.as_str()).collect::<Vec<&str>>())
147 .collect::<Vec<Vec<&str>>>();
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);
154 opt.set_table_index(index);
155 opt.set_table_partition(module.table_partition());
156 opt.set_table_partition_columns(module.table_partition_columns());
157
158 if table_name_list.contains(&module._table_name()) {
159 continue;
160 }
161 table_name_list.push(module._table_name());
162 if Self::get_tools().db.table_is_exist(module._table_name()) {
163 let res = Self::get_tools().db.table_update_new(opt);
164 match res.as_i32().unwrap() {
165 -1 => {}
166 0 => {
167 info!("数据库更新情况: {} 失败", module._table_name());
168 }
169 1 => {
170 info!("数据库更新情况: {} 成功", module._table_name());
171 }
172 _ => {}
173 }
174 } else {
175 let res = Self::get_tools().db.table_create_new(opt);
176 info!("安装完成情况: {} {}", module._table_name(), res);
177 }
178 }
179 info!("=============数据库更新完成=============");
180 info!("=============API数量: {} 条=============", api_list.len());
181 fs::write(file_path, JsonValue::from(api_list.clone()).to_string()).unwrap();
182 Ok(api_list)
183 }
184 fn swagger(
186 title: &str,
187 description: &str,
188 version: &str,
189 uaturl: &str,
190 produrl: &str,
191 tags: JsonValue,
192 paths: JsonValue,
193 ) -> JsonValue {
194 let info = object! {
195 openapi:"3.0.0",
196 info:{
197 title:title,
198 description:description,
199 version:version
200 },
201 components: {
202 securitySchemes: {
203 BearerToken: {
204 "type": "http",
205 "scheme": "bearer",
206 "bearerFormat": "Token"
207 }
208 }
209 },
210 tags:tags,
211 security: [
212 {
213 "BearerToken": []
214 }
215 ],
216 servers:[
217 {
218 "url":uaturl,
219 "description": "测试地址"
220 },
221 {
222 "url":produrl,
223 "description": "正式地址"
224 }
225 ],
226 paths:paths
227 };
228 info
229 }
230 fn generate_api_list(apipath: PathBuf, path: PathBuf, index: usize) -> io::Result<Vec<String>> {
232 #[cfg(debug_assertions)]
233 {
234 let mut plugin_list = vec![];
235 if path.is_dir() {
236 let res = fs::read_dir(path);
237 match res {
238 Ok(entries) => {
239 for entry in entries {
240 let entry = entry.unwrap();
241 let path = entry.path();
242 if path.is_dir() {
243 let res = Self::generate_api_list(
244 apipath.clone(),
245 path.to_str().unwrap().parse().unwrap(),
246 index + 1,
247 )?;
248 plugin_list.extend(res);
249 } else if path.is_file() {
250 if path.to_str().unwrap().ends_with("mod.rs") {
251 continue;
252 }
253 let addon = path
254 .parent()
255 .unwrap()
256 .parent()
257 .unwrap()
258 .file_name()
259 .unwrap()
260 .to_str()
261 .unwrap();
262 let model = path
263 .parent()
264 .unwrap()
265 .file_name()
266 .unwrap()
267 .to_str()
268 .unwrap();
269 let action = path
270 .file_name()
271 .unwrap()
272 .to_str()
273 .unwrap()
274 .trim_end_matches(".rs");
275 plugin_list.push(format!("{}.{}.{}", addon, model, action));
276 }
277 }
278 }
279 Err(e) => {
280 return Err(std::io::Error::new(
281 std::io::ErrorKind::Other,
282 e.to_string(),
283 ))
284 }
285 }
286 }
287 if index == 0 {
288 fs::create_dir_all(apipath.clone().parent().unwrap()).unwrap();
289 fs::write(apipath, JsonValue::from(plugin_list.clone()).to_string()).unwrap();
290 }
291 Ok(plugin_list)
292 }
293 #[cfg(not(debug_assertions))]
294 {
295 let apis = fs::read_to_string(apipath).unwrap();
296 let apis = json::parse(&apis).unwrap();
297 let mut apis = apis
298 .members()
299 .map(|x| x.as_str().unwrap().to_string())
300 .collect::<Vec<String>>();
301 Ok(apis)
302 }
303 }
304}
305#[derive(Debug, Clone)]
307pub struct ApiResponse {
308 pub types: ApiType,
309 pub code: i32,
310 pub message: String,
311 pub data: JsonValue,
312 pub success: bool,
313}
314impl ApiResponse {
315 pub fn json(self) -> JsonValue {
316 match self.types {
317 ApiType::Json => object! {
318 code: self.code,
319 message: self.message,
320 data: self.data,
321 success: self.success
322 },
323 ApiType::Redirect => self.data,
324 ApiType::Download => self.data,
325 ApiType::Preview => self.data,
326 ApiType::Txt => self.data,
327 }
328 }
329 pub fn swagger(&mut self) -> JsonValue {
330 let mut content = object! {};
331 content[self.types.str().as_str()] = object! {};
332 content[self.types.str().as_str()]["schema"]["type"] = if self.data.is_array() {
333 "array"
334 } else {
335 "object"
336 }
337 .into();
338 content[self.types.str().as_str()]["schema"]["properties"] = self.data.clone();
339
340 content[self.types.str().as_str()]["schema"]["type"] = match content
341 [self.types.str().as_str()]["schema"]["type"]
342 .as_str()
343 .unwrap()
344 {
345 "int" => "integer".into(),
346 _ => content[self.types.str().as_str()]["schema"]["type"].clone(),
347 };
348 let data = object! {
349 "description":self.message.clone(),
350 "content":content
351 };
352 data
353 }
354 pub fn success(data: JsonValue, mut message: &str) -> Self {
355 if message.is_empty() {
356 message = "success";
357 }
358 Self {
359 success: true,
360 types: ApiType::Json,
361 code: 0,
362 message: message.to_string(),
363 data,
364 }
365 }
366 pub fn fail(code: i32, message: &str) -> Self {
367 Self {
368 types: ApiType::Json,
369 code,
370 message: message.to_string(),
371 data: JsonValue::Null,
372 success: false,
373 }
374 }
375 pub fn error(data: JsonValue, message: &str) -> Self {
376 Self {
377 types: ApiType::Json,
378 code: -1,
379 message: message.to_string(),
380 data,
381 success: false,
382 }
383 }
384 pub fn redirect(url: &str) -> Self {
386 Self {
387 types: ApiType::Redirect,
388 code: 0,
389 message: "".to_string(),
390 data: url.into(),
391 success: true,
392 }
393 }
394 pub fn download(filename: &str) -> Self {
396 Self {
397 types: ApiType::Download,
398 code: 0,
399 message: "".to_string(),
400 data: filename.into(),
401 success: true,
402 }
403 }
404 pub fn preview(filename: &str) -> Self {
406 Self {
407 types: ApiType::Preview,
408 code: 0,
409 message: "".to_string(),
410 data: filename.into(),
411 success: true,
412 }
413 }
414 pub fn txt(txt: &str) -> Self {
416 Self {
417 types: ApiType::Txt,
418 code: 0,
419 message: "".to_string(),
420 data: txt.into(),
421 success: true,
422 }
423 }
424}
425impl Default for ApiResponse {
426 fn default() -> Self {
427 Self {
428 types: ApiType::Json,
429 code: 0,
430 message: "".to_string(),
431 data: JsonValue::Null,
432 success: false,
433 }
434 }
435}
436#[derive(Debug, Clone)]
438pub enum ApiType {
439 Json,
441 Redirect,
444 Download,
447 Preview,
450 Txt,
452}
453impl ApiType {
454 pub fn str(&mut self) -> String {
455 match self {
456 ApiType::Json => "application/json",
457 ApiType::Redirect | ApiType::Download | ApiType::Preview => "text/html",
458 ApiType::Txt => "text/plain",
459 }
460 .to_string()
461 }
462}