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