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