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