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