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;
29use std::time::Instant;
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"))]
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
112 for api_name in list.members() {
113 let api_info = api_name.as_str().unwrap_or("").split(".").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 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 if !tables.contains_key(module._table_name()) {
130 tables.insert(module._table_name(), module);
131 }
132 }
133
134 for (_, module) in tables.iter_mut() {
135 let start = Instant::now();
136
137 let mut opt = TableOptions::default();
138
139 let unique = module.table_unique().iter().map(|x| (*x).to_string()).collect::<Vec<String>>();
140 let unique = unique.iter().map(|x| x.as_str()).collect::<Vec<&str>>();
141
142 let index = module.table_index().iter().map(|x| x.iter().map(|&y| y.to_string()).collect::<Vec<String>>()).collect::<Vec<Vec<String>>>();
143 let index = index.iter().map(|x| x.iter().map(|y| y.as_str()).collect::<Vec<&str>>()).collect::<Vec<Vec<&str>>>();
144
145 opt.set_table_name(module._table_name());
146 opt.set_table_title(module.title());
147 opt.set_table_key(module.table_key());
148 opt.set_table_fields(module.fields().clone());
149 opt.set_table_unique(unique);
150 opt.set_table_index(index);
151 opt.set_table_partition(module.table_partition());
152 opt.set_table_partition_columns(module.table_partition_columns());
153
154 if Self::get_tools().db.table_is_exist(module._table_name()) {
155 println!("list耗时: {:?}", start.elapsed());
156 let res = Self::get_tools().db.table_update(opt);
157 match res.as_i32().unwrap() {
158 -1 => {}
159 0 => {
160 info!("数据库更新情况: {} 失败", module._table_name());
161 }
162 1 => {
163 info!("数据库更新情况: {} 成功", module._table_name());
164 }
165 _ => {}
166 }
167 } else {
168 let res = Self::get_tools().db.table_create(opt);
169 info!("安装完成情况: {} {}", module._table_name(), res);
170 }
171 }
172 info!("=============数据库更新完成=============");
173 Ok(())
174 }
175 fn swagger(
177 title: &str,
178 description: &str,
179 version: &str,
180 uaturl: &str,
181 produrl: &str,
182 tags: JsonValue,
183 paths: JsonValue,
184 ) -> JsonValue {
185 let info = object! {
186 openapi:"3.0.0",
187 info:{
188 title:title,
189 description:description,
190 version:version
191 },
192 components: {
193 securitySchemes: {
194 BearerToken: {
195 "type": "http",
196 "scheme": "bearer",
197 "bearerFormat": "Token"
198 }
199 }
200 },
201 tags:tags,
202 security: [
203 {
204 "BearerToken": []
205 }
206 ],
207 servers:[
208 {
209 "url":uaturl,
210 "description": "测试地址"
211 },
212 {
213 "url":produrl,
214 "description": "正式地址"
215 }
216 ],
217 paths:paths
218 };
219 info
220 }
221 fn generate_api_list(apipath: PathBuf, path: PathBuf, index: usize) -> io::Result<Vec<String>> {
223 #[cfg(debug_assertions)]
224 {
225 let mut plugin_list = Vec::new();
226 if path.is_dir() {
227 let res = fs::read_dir(path);
228 match res {
229 Ok(entries) => {
230 for entry in entries {
231 let entry = entry.unwrap();
232 let path = entry.path();
233 if path.is_dir() {
234 let res = Self::generate_api_list(
235 apipath.clone(),
236 path.to_str().unwrap().parse().unwrap(),
237 index + 1,
238 )?;
239 plugin_list.extend(res);
240 } else if path.is_file() {
241 if path.to_str().unwrap().ends_with("mod.rs") {
242 continue;
243 }
244 let addon = path.parent().unwrap().parent().unwrap().file_name().unwrap().to_str().unwrap();
245 let model = path.parent().unwrap().file_name().unwrap().to_str().unwrap();
246 let action = path.file_name().unwrap().to_str().unwrap().trim_end_matches(".rs");
247 let api = format!("{}.{}.{}", addon, model, action);
248 match Self::api(api.as_str()) {
249 Ok(e) => plugin_list.push(e.api()),
250 Err(_) => continue
251 }
252 }
253 }
254 }
255 Err(e) => return Err(io::Error::other(e.to_string()))
256 }
257 }
258 if index == 0 {
259 fs::create_dir_all(apipath.clone().parent().unwrap()).unwrap();
260 fs::write(apipath, JsonValue::from(plugin_list.clone()).to_string()).unwrap();
261 info!("=============API数量: {} 条=============", plugin_list.len());
262 }
263 Ok(plugin_list)
264 }
265 #[cfg(not(debug_assertions))]
266 {
267 let apis = fs::read_to_string(apipath).unwrap();
268 let apis = json::parse(&apis).unwrap();
269 let apis = apis.members().map(|x| x.as_str().unwrap().to_string()).collect::<Vec<String>>();
270 info!("=============API数量: {} 条=============", apis.len());
271 Ok(apis)
272 }
273 }
274
275 fn set_global_data(key: &str, value: JsonValue) {
277 GLOBAL_DATA.with(|data| {
278 data.borrow_mut()[key] = value;
279 });
280 }
281 fn get_global_data() -> JsonValue {
283 GLOBAL_DATA.with(|data| {
284 data.borrow().clone()
285 })
286 }
287 fn get_global_data_key(key: &str) -> JsonValue {
289 GLOBAL_DATA.with(|data| {
290 data.borrow()[key].clone()
291 })
292 }
293}
294#[derive(Debug, Clone)]
296pub struct ApiResponse {
297 pub types: ApiType,
298 pub code: i32,
299 pub message: String,
300 pub data: JsonValue,
301 pub success: bool,
302}
303impl ApiResponse {
304 pub fn json(self) -> JsonValue {
305 match self.types {
306 ApiType::Json => object! {
307 code: self.code,
308 message: self.message,
309 data: self.data,
310 success: self.success
311 },
312 ApiType::Redirect => self.data,
313 ApiType::Download => self.data,
314 ApiType::Preview => self.data,
315 ApiType::Txt => self.data,
316 }
317 }
318 pub fn swagger(&mut self) -> JsonValue {
319 let mut content = object! {};
320 content[self.types.str().as_str()] = object! {};
321 content[self.types.str().as_str()]["schema"]["type"] = if self.data.is_array() {
322 "array"
323 } else {
324 "object"
325 }.into();
326 content[self.types.str().as_str()]["schema"]["properties"] = self.data.clone();
327
328 content[self.types.str().as_str()]["schema"]["type"] = match content[self.types.str().as_str()]["schema"]["type"].as_str().unwrap() {
329 "int" => "integer".into(),
330 _ => content[self.types.str().as_str()]["schema"]["type"].clone(),
331 };
332 let data = object! {
333 "description":self.message.clone(),
334 "content":content
335 };
336 data
337 }
338 pub fn success(data: JsonValue, mut message: &str) -> Self {
339 if message.is_empty() {
340 message = "success";
341 }
342 Self {
343 success: true,
344 types: ApiType::Json,
345 code: 0,
346 message: message.to_string(),
347 data,
348 }
349 }
350 pub fn fail(code: i32, message: &str) -> Self {
351 Self {
352 types: ApiType::Json,
353 code,
354 message: message.to_string(),
355 data: JsonValue::Null,
356 success: false,
357 }
358 }
359 pub fn error(data: JsonValue, message: &str) -> Self {
360 Self {
361 types: ApiType::Json,
362 code: -1,
363 message: message.to_string(),
364 data,
365 success: false,
366 }
367 }
368 pub fn redirect(url: &str) -> Self {
370 Self {
371 types: ApiType::Redirect,
372 code: 0,
373 message: "".to_string(),
374 data: url.into(),
375 success: true,
376 }
377 }
378 pub fn download(filename: &str) -> Self {
380 Self {
381 types: ApiType::Download,
382 code: 0,
383 message: "".to_string(),
384 data: filename.into(),
385 success: true,
386 }
387 }
388 pub fn preview(filename: &str) -> Self {
390 Self {
391 types: ApiType::Preview,
392 code: 0,
393 message: "".to_string(),
394 data: filename.into(),
395 success: true,
396 }
397 }
398 pub fn txt(txt: &str) -> Self {
400 Self {
401 types: ApiType::Txt,
402 code: 0,
403 message: "".to_string(),
404 data: txt.into(),
405 success: true,
406 }
407 }
408}
409impl Default for ApiResponse {
410 fn default() -> Self {
411 Self {
412 types: ApiType::Json,
413 code: 0,
414 message: "".to_string(),
415 data: JsonValue::Null,
416 success: false,
417 }
418 }
419}
420#[derive(Debug, Clone)]
422pub enum ApiType {
423 Json,
425 Redirect,
428 Download,
431 Preview,
434 Txt,
436}
437impl ApiType {
438 pub fn str(&mut self) -> String {
439 match self {
440 ApiType::Json => "application/json",
441 ApiType::Redirect | ApiType::Download | ApiType::Preview => "text/html",
442 ApiType::Txt => "text/plain",
443 }.to_string()
444 }
445}