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