tiny_web/sys/
action.rs

1use percent_encoding::percent_decode_str;
2use std::{
3    borrow::Cow,
4    collections::{BTreeMap, HashMap},
5    future::Future,
6    path::PathBuf,
7    pin::Pin,
8    str::Utf8Error,
9    sync::Arc,
10};
11use tiny_web_macro::fnv1a_64;
12
13use tokio::{
14    fs::remove_file,
15    sync::{mpsc::Sender, Mutex},
16    task::{yield_now, JoinHandle},
17};
18
19#[cfg(debug_assertions)]
20use tokio::sync::RwLock;
21
22use crate::{fnv1a_64, StrOrI64};
23
24use super::{
25    cache::{Cache, CacheSys},
26    data::Data,
27    dbs::adapter::DB,
28    html::{Html, Nodes},
29    init::Addr,
30    lang::{Lang, LangItem},
31    log::Log,
32    mail::{Mail, MailMessage},
33    request::{Request, WebFile},
34    response::{Redirect, Response},
35    route::Route,
36    session::{Flash, Session},
37    tool::Tool,
38    worker::{MessageWrite, Worker},
39};
40
41/// Type of one controler. Use in engine.
42pub type Act = fn(&mut Action) -> Pin<Box<dyn Future<Output = Answer> + Send + '_>>;
43
44/// List all controllers. Use in engine.
45///
46/// # Index
47///
48/// * 1 - Module ID
49/// * 2 - Class ID
50/// * 3 - Action ID
51pub type ActMap = BTreeMap<i64, BTreeMap<i64, BTreeMap<i64, Act>>>;
52
53/// Type of Answer
54///
55/// # Values
56///
57/// * `None` - Without answer.
58/// * `String(String)` - Answer in the form of text.
59/// * `Raw(Vec<u8>)` - Answer in binary data.
60#[derive(Debug)]
61pub enum Answer {
62    /// Without answer
63    None,
64    /// Answer in the form of text.
65    String(String),
66    /// Answer in binary data
67    Raw(Vec<u8>),
68}
69
70/// Data to run Action (Main controler)
71#[derive(Debug)]
72pub(crate) struct ActionData {
73    /// Engine - binary tree of controller functions.
74    pub engine: Arc<ActMap>,
75    /// I18n system.
76    #[cfg(not(debug_assertions))]
77    pub lang: Arc<Lang>,
78    /// I18n system.
79    #[cfg(debug_assertions)]
80    pub lang: Arc<RwLock<Lang>>,
81    /// Template maker.
82    #[cfg(not(debug_assertions))]
83    pub html: Arc<Html>,
84    /// Template maker.
85    #[cfg(debug_assertions)]
86    pub html: Arc<RwLock<Html>>,
87    /// Cache system.
88    pub cache: Arc<Mutex<CacheSys>>,
89    /// Database connections pool.
90    pub db: Arc<DB>,
91    /// Session key.
92    pub session_key: Arc<String>,
93    /// Salt for a crypto functions.
94    pub salt: Arc<String>,
95    /// Mail provider.
96    pub mail: Arc<Mutex<Mail>>,
97    /// Request from web server.
98    pub request: Request,
99    /// Session value.
100    pub session: Option<String>,
101    /// Sender data to output stream
102    pub(crate) tx: Arc<Sender<MessageWrite>>,
103    /// Default controller for request "/" or default class or default action
104    pub(crate) action_index: Arc<Route>,
105    /// Default controller for 404 Not Found
106    pub(crate) action_not_found: Arc<Route>,
107    /// Default controller for error_route
108    pub(crate) action_err: Arc<Route>,
109    /// Stop signal
110    pub(crate) stop: Option<(Arc<Addr>, i64)>,
111    /// The full path to the folder where the server was started.
112    pub(crate) root: Arc<String>,
113}
114
115/// Main struct to run web engine
116///
117///  # Values
118///
119/// * `request: Request` - Request from web server.
120/// * `response: Response` - Response to web server.
121/// * `session: Session` - Session data.
122/// * `salt: Arc<String>` - Secret salt.
123/// * `data: BTreeMap<i64, Data>` - Data transferred between controllers template markers and cache.
124/// * `pub module: String` - Start module name.
125/// * `pub class: String` - Start class name.
126/// * `pub action: String` - Start action (controller) name.
127/// * `param: Option<String>` - Start param.
128/// * `module_id: i64` - Module ID.
129/// * `class_id: i64,` - Class ID.
130/// * `action_id: i64` - Action ID.
131/// * `current_module_id: i64` - Current module ID.
132/// * `current_class_id: i64` - Current class ID.
133/// * `html: Option<Arc<BTreeMap<i64, Vec<Node>>>>` - Current templates.
134/// * `lang: Option<Arc<BTreeMap<i64, String>>>` - Current translates.
135/// * `engine: Arc<ActMap>` - Engine of server.
136/// * `language: Arc<Lang>` - All translates.
137/// * `template: Arc<Html>` - All templates.
138/// * `cache: Arc<Mutex<Cache>>` - Cache.
139/// * `db: Arc<DB>` - Database pool.
140/// * `mail: Arc<Mail>` - Mail function.
141/// * `internal: bool` - Internal call of controller.
142#[derive(Debug)]
143pub struct Action {
144    /// Request from web server
145    pub request: Request,
146    /// Response to web server
147    pub response: Response,
148    /// Session data
149    pub session: Session,
150    /// Secret salt
151    pub salt: Arc<String>,
152    /// Data transferred between controllers template markers and cache
153    data: BTreeMap<i64, Data>,
154    /// Start module name
155    pub module: String,
156    /// Start class name
157    pub class: String,
158    /// Start action (controller) name
159    pub action: String,
160    /// Start param
161    pub param: Option<String>,
162    /// Module ID
163    module_id: i64,
164    /// Class ID
165    class_id: i64,
166    /// Action ID
167    action_id: i64,
168    /// Current module ID
169    current_module_id: i64,
170    /// Current class ID
171    current_class_id: i64,
172    /// Current templates
173    html: Option<Arc<BTreeMap<i64, Nodes>>>,
174    /// Current translates
175    lang: Option<Arc<BTreeMap<i64, String>>>,
176
177    /// Engine of server
178    engine: Arc<ActMap>,
179    /// All translates
180    #[cfg(not(debug_assertions))]
181    language: Arc<Lang>,
182    /// All translates
183    #[cfg(debug_assertions)]
184    language: Arc<RwLock<Lang>>,
185    /// All templates
186    #[cfg(not(debug_assertions))]
187    template: Arc<Html>,
188    /// All templates
189    #[cfg(debug_assertions)]
190    template: Arc<RwLock<Html>>,
191    /// Cache
192    pub cache: Cache,
193    /// Database pool
194    pub db: Arc<DB>,
195    /// Mail function
196    mail: Arc<Mutex<Mail>>,
197
198    /// Internal call of controller
199    pub internal: bool,
200
201    /// Sender data to output stream
202    pub(crate) tx: Arc<Sender<MessageWrite>>,
203    /// Header was sended
204    pub(crate) header_send: bool,
205
206    /// Default controller for 404 Not Found
207    not_found: Arc<Route>,
208    /// Different features
209    pub tool: Tool,
210}
211
212impl Action {
213    /// Run new Action
214    ///
215    /// # Return
216    ///
217    /// `Ok(Action)` - Action (controller) was found success.
218    /// `Err(Redirect, HashMap<String, Vec<WebFile>>)` - Must redirect, and then remove temp files.
219    pub(crate) async fn new(data: ActionData) -> Result<Action, (Redirect, HashMap<String, Vec<WebFile>>)> {
220        let response = Response {
221            redirect: None,
222            content_type: None,
223            headers: Vec::new(),
224            http_code: None,
225            css: Vec::new(),
226            js: Vec::new(),
227            meta: Vec::new(),
228        };
229        #[cfg(not(debug_assertions))]
230        let lang_id = data.lang.default as i64;
231        #[cfg(debug_assertions)]
232        let lang_id = data.lang.read().await.default as i64;
233
234        let mut session = if let Some(session) = data.session {
235            Session::load_session(session.clone(), Arc::clone(&data.db), lang_id, data.session_key).await
236        } else {
237            Session::new(lang_id, &data.salt, &data.request.ip, &data.request.agent, &data.request.host, data.session_key)
238        };
239        // Module, class and action (controller) from URL
240        let route = match Action::extract_route(
241            &data.request,
242            Arc::clone(&data.cache),
243            Arc::clone(&data.db),
244            Arc::clone(&data.action_index),
245            Arc::clone(&data.action_err),
246        )
247        .await
248        {
249            Ok(r) => r,
250            Err(redirect) => return Err((redirect, data.request.input.file)),
251        };
252        let module = route.module;
253        let class = route.class;
254        let action = route.action;
255        let module_id = route.module_id;
256        let class_id = route.class_id;
257        let action_id = route.action_id;
258        if let Some(lang_id) = route.lang_id {
259            session.set_lang_id(lang_id);
260        }
261        let param = route.param;
262        // Load new template list
263        #[cfg(not(debug_assertions))]
264        let html = data.html.list.get(&module_id).and_then(|module| module.get(&class_id).cloned());
265        #[cfg(debug_assertions)]
266        let html = data.html.read().await.list.get(&module_id).and_then(|module| module.get(&class_id).cloned());
267        // Load new translate list
268        #[cfg(not(debug_assertions))]
269        let lang = data
270            .lang
271            .list
272            .get(&session.get_lang_id())
273            .and_then(|langs| langs.get(&module_id))
274            .and_then(|module| module.get(&class_id).cloned());
275        #[cfg(debug_assertions)]
276        let lang = data
277            .lang
278            .read()
279            .await
280            .list
281            .get(&session.get_lang_id())
282            .and_then(|langs| langs.get(&module_id))
283            .and_then(|module| module.get(&class_id).cloned());
284
285        Ok(Action {
286            request: data.request,
287            response,
288            session,
289            salt: data.salt,
290            data: BTreeMap::new(),
291
292            module,
293            class,
294            action,
295            param,
296            module_id,
297            class_id,
298            action_id,
299            current_module_id: module_id,
300            current_class_id: class_id,
301            html,
302            lang,
303
304            engine: data.engine,
305            language: data.lang,
306            template: data.html,
307            cache: Cache::new(data.cache),
308            db: Arc::clone(&data.db),
309            mail: data.mail,
310
311            internal: false,
312
313            tx: data.tx,
314            header_send: false,
315            not_found: data.action_not_found,
316            tool: Tool::new(data.db, data.stop, data.root),
317        })
318    }
319
320    /// Load internal controller
321    pub async fn load(
322        &mut self,
323        key: impl StrOrI64,
324        module: impl StrOrI64,
325        class: impl StrOrI64,
326        action: impl StrOrI64,
327        param: Option<String>,
328    ) {
329        let res = self.start_route(module.to_i64(), class.to_i64(), action.to_i64(), param, true).await;
330        if let Answer::String(value) = res {
331            self.data.insert(key.to_i64(), Data::String(value));
332        }
333    }
334
335    /// Get translate
336    pub fn lang(&self, text: impl StrOrI64) -> String {
337        if let Some(l) = &self.lang {
338            if let Some(str) = l.get(&text.to_i64()) {
339                return str.to_owned();
340            }
341        }
342        text.to_str().to_owned()
343    }
344
345    /// Get current lang
346    pub async fn lang_current(&self) -> Arc<LangItem> {
347        #[cfg(not(debug_assertions))]
348        {
349            Arc::clone(unsafe { self.language.langs.get_unchecked(self.session.get_lang_id() as usize) })
350        }
351        #[cfg(debug_assertions)]
352        {
353            Arc::clone(unsafe { self.language.read().await.langs.get_unchecked(self.session.get_lang_id() as usize) })
354        }
355    }
356
357    /// Get vector of system languages
358    pub async fn lang_list(&self) -> Arc<Vec<Arc<LangItem>>> {
359        #[cfg(not(debug_assertions))]
360        {
361            Arc::clone(&self.language.langs)
362        }
363        #[cfg(debug_assertions)]
364        {
365            Arc::clone(&self.language.read().await.langs)
366        }
367    }
368
369    /// Get vector of all languages
370    pub async fn all_lang_list(&self) -> Vec<LangItem> {
371        Lang::get_all_langs(Arc::clone(&self.db)).await
372    }
373
374    /// Setting data into internal memory
375    pub fn set<T>(&mut self, key: impl StrOrI64, value: T)
376    where
377        T: Into<Data>,
378    {
379        self.data.insert(key.to_i64(), value.into());
380    }
381
382    /// Getting references to data from internal memory
383    pub fn get<T>(&self, key: impl StrOrI64) -> Option<&T>
384    where
385        for<'a> &'a T: From<&'a Data>,
386    {
387        self.data.get(&key.to_i64()).map(|value| value.into())
388    }
389
390    /// Taking (removing) data from internal memory
391    pub fn take<T>(&mut self, key: impl StrOrI64) -> Option<T>
392    where
393        T: From<Data>,
394    {
395        self.data.remove(&key.to_i64()).map(|value| value.into())
396    }
397
398    /// Set flash message to session data
399    pub fn set_flash(&mut self, kind: Flash, value: String) {
400        self.session.set_flash(kind, value);
401    }
402
403    /// Take flash message from session data
404    pub fn take_flash(&mut self) -> Option<Vec<(Flash, String)>> {
405        self.session.take_flash()
406    }
407
408    /// Set value for the template from translate
409    pub fn set_lang(&mut self, key: impl StrOrI64) {
410        let idkey = key.to_i64();
411        if let Some(l) = &self.lang {
412            if let Some(str) = l.get(&idkey) {
413                self.data.insert(idkey, Data::String(str.to_owned()));
414                return;
415            }
416        }
417        let str = key.to_str();
418        if !str.is_empty() {
419            self.data.insert(idkey, Data::String(key.to_str().to_owned()));
420        } else {
421            #[cfg(not(debug_assertions))]
422            self.data.insert(idkey, Data::String("".to_owned()));
423            #[cfg(debug_assertions)]
424            self.data.insert(idkey, Data::String(key.to_i64().to_string().to_owned()));
425        }
426    }
427
428    /// Set an array of values for the template from the translation
429    pub fn set_lang_arr(&mut self, keys: &[impl StrOrI64]) {
430        for key in keys {
431            let idkey = key.to_i64();
432            if let Some(l) = &self.lang {
433                if let Some(str) = l.get(&idkey) {
434                    self.data.insert(idkey, Data::String(str.to_owned()));
435                    continue;
436                }
437            }
438            let str = key.to_str();
439            if !str.is_empty() {
440                self.data.insert(idkey, Data::String(key.to_str().to_owned()));
441            } else {
442                #[cfg(not(debug_assertions))]
443                self.data.insert(idkey, Data::String("".to_owned()));
444                #[cfg(debug_assertions)]
445                self.data.insert(idkey, Data::String(key.to_i64().to_string().to_owned()));
446            }
447        }
448    }
449
450    /// Spawns a new asynchronous task, returning a
451    /// [`tokio::task::JoinHandle`](tokio::task::JoinHandle) for it.
452    ///
453    /// The provided future will start running in the background immediately
454    /// when `spawn` is called, even if you don't await the returned
455    /// `JoinHandle`.
456    pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
457    where
458        F: Future + Send + 'static,
459        F::Output: Send + 'static,
460    {
461        tokio::spawn(future)
462    }
463
464    /// Write data to the output stream
465    pub async fn write(&mut self, answer: Answer) {
466        let vec = match answer {
467            Answer::None => return,
468            Answer::String(str) => str.as_bytes().to_vec(),
469            Answer::Raw(raw) => raw,
470        };
471        Worker::write(self, vec).await;
472        self.header_send = true;
473        yield_now().await;
474    }
475
476    /// Get access to run controller
477    pub async fn get_access(&mut self, module: impl StrOrI64, class: impl StrOrI64, action: impl StrOrI64) -> bool {
478        if !self.db.in_use() {
479            return true;
480        }
481        let module_id = module.to_i64();
482        let class_id = class.to_i64();
483        let action_id = action.to_i64();
484        // Read from cache
485        let key = vec![fnv1a_64!("auth"), self.session.role_id, module_id, class_id, action_id];
486        let (data, key) = self.cache.get(key).await;
487        if let Some(Data::Bool(a)) = data {
488            return a;
489        };
490        // Prepare sql query
491        match self
492            .db
493            .query_prepare(
494                fnv1a_64!("lib_get_auth"),
495                &[&self.session.role_id, &module_id, &module_id, &module_id, &class_id, &class_id, &action_id],
496                false,
497            )
498            .await
499        {
500            Some(rows) => {
501                if rows.len() == 1 {
502                    let access = if let Data::Vec(row) = unsafe { rows.get_unchecked(0) } {
503                        if row.is_empty() {
504                            return false;
505                        }
506                        if let Data::I32(val) = unsafe { row.get_unchecked(0) } {
507                            *val != 0
508                        } else {
509                            return false;
510                        }
511                    } else {
512                        return false;
513                    };
514
515                    self.cache.set(key, Data::Bool(access)).await;
516                    access
517                } else {
518                    self.cache.set(key, Data::Bool(false)).await;
519                    false
520                }
521            }
522            None => false,
523        }
524    }
525
526    /// Render template
527    ///
528    /// # Value
529    ///
530    /// * `template: &str` - Name of template
531    pub fn render(&mut self, template: impl StrOrI64) -> Answer {
532        match &self.html {
533            Some(h) => match h.get(&template.to_i64()) {
534                Some(vec) => {
535                    if !self.response.css.is_empty() {
536                        let mut vec = Vec::with_capacity(self.response.css.len());
537                        for css in self.response.css.drain(..) {
538                            vec.push(Data::String(css));
539                        }
540                        self.data.insert(fnv1a_64!("css"), Data::Vec(vec));
541                    }
542                    if !self.response.js.is_empty() {
543                        let mut vec = Vec::with_capacity(self.response.js.len());
544                        for js in self.response.js.drain(..) {
545                            vec.push(Data::String(js));
546                        }
547                        self.data.insert(fnv1a_64!("js"), Data::Vec(vec));
548                    }
549                    if !self.response.meta.is_empty() {
550                        let mut vec = Vec::with_capacity(self.response.meta.len());
551                        for meta in self.response.meta.drain(..) {
552                            vec.push(Data::String(meta));
553                        }
554                        self.data.insert(fnv1a_64!("meta"), Data::Vec(vec));
555                    }
556                    Html::render(&self.data, vec)
557                }
558                #[cfg(not(debug_assertions))]
559                None => Answer::None,
560                #[cfg(debug_assertions)]
561                None => Answer::String(format!("{{{}}}", template.to_str())),
562            },
563            #[cfg(not(debug_assertions))]
564            None => Answer::None,
565            #[cfg(debug_assertions)]
566            None => Answer::String(format!("{{{}}}", template.to_str())),
567        }
568    }
569
570    /// Get route
571    pub async fn route(&mut self, module: &str, class: &str, action: &str, param: Option<&str>, lang_id: Option<i64>) -> String {
572        if self.db.in_use() {
573            // Read from cache
574            let key = match (param, lang_id) {
575                (Some(p), Some(l)) => vec![
576                    fnv1a_64!("route"),
577                    fnv1a_64(module.as_bytes()),
578                    fnv1a_64(class.as_bytes()),
579                    fnv1a_64(action.as_bytes()),
580                    fnv1a_64(p.as_bytes()),
581                    l,
582                ],
583                (Some(p), None) => vec![
584                    fnv1a_64!("route"),
585                    fnv1a_64(module.as_bytes()),
586                    fnv1a_64(class.as_bytes()),
587                    fnv1a_64(action.as_bytes()),
588                    fnv1a_64(p.as_bytes()),
589                    -1,
590                ],
591                (None, Some(l)) => {
592                    vec![fnv1a_64!("route"), fnv1a_64(module.as_bytes()), fnv1a_64(class.as_bytes()), fnv1a_64(action.as_bytes()), 0, l]
593                }
594                (None, None) => {
595                    vec![fnv1a_64!("route"), fnv1a_64(module.as_bytes()), fnv1a_64(class.as_bytes()), fnv1a_64(action.as_bytes()), 0, -1]
596                }
597            };
598            let (data, key) = self.cache.get(key).await;
599            if let Some(Data::String(s)) = data {
600                return s;
601            };
602            // Prepare sql query
603            match self
604                .db
605                .query_prepare(
606                    fnv1a_64!("lib_get_url"),
607                    &[&fnv1a_64(module.as_bytes()), &fnv1a_64(class.as_bytes()), &fnv1a_64(action.as_bytes()), &param, &lang_id],
608                    false,
609                )
610                .await
611            {
612                Some(rows) => {
613                    if rows.len() == 1 {
614                        let row = if let Data::Vec(vec) = unsafe { rows.get_unchecked(0) } {
615                            vec
616                        } else {
617                            return Action::format_route(module, class, action, param);
618                        };
619                        if row.is_empty() {
620                            return Action::format_route(module, class, action, param);
621                        }
622
623                        let url = if let Data::String(url) = unsafe { row.get_unchecked(0) } {
624                            url.clone()
625                        } else {
626                            return Action::format_route(module, class, action, param);
627                        };
628                        self.cache.set(key, Data::String(url.clone())).await;
629                        url
630                    } else {
631                        let url = Action::format_route(module, class, action, param);
632                        self.cache.set(key, Data::String(url.clone())).await;
633                        url
634                    }
635                }
636                None => Action::format_route(module, class, action, param),
637            }
638        } else {
639            Action::format_route(module, class, action, param)
640        }
641    }
642
643    /// Send email
644    pub async fn mail(&self, message: MailMessage) -> bool {
645        let provider = {
646            let mail = self.mail.lock().await;
647            mail.provider.clone()
648        };
649        Mail::send(provider, Arc::clone(&self.db), message, self.session.user_id, self.request.host.clone()).await
650    }
651
652    /// Percent decode url
653    pub fn percent_decode<'a>(&self, url: &'a str) -> Result<Cow<'a, str>, Utf8Error> {
654        percent_decode_str(url).decode_utf8()
655    }
656
657    /// Get not_found url
658    pub async fn not_found(&mut self) -> String {
659        if !self.db.in_use() {
660            let install = Route::default_install();
661            return format!("/{}/{}/not_found", install.module, install.class);
662        }
663        let key = vec![fnv1a_64!("404"), self.session.get_lang_id()];
664        let (data, key) = self.cache.get(key).await;
665        match data {
666            Some(d) => match d {
667                Data::String(url) => url,
668                _ => match &self.not_found.param {
669                    Some(param) => {
670                        format!("/{}/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action, param)
671                    }
672                    None => {
673                        format!("/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action)
674                    }
675                },
676            },
677            None => {
678                // Load from database
679                match self.db.query_prepare(fnv1a_64!("lib_get_not_found"), &[&self.session.get_lang_id()], false).await {
680                    Some(v) => {
681                        if v.is_empty() {
682                            self.cache.set(key, Data::None).await;
683                            match &self.not_found.param {
684                                Some(param) => {
685                                    format!("/{}/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action, param)
686                                }
687                                None => format!("/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action),
688                            }
689                        } else if let Data::Vec(row) = unsafe { v.get_unchecked(0) } {
690                            if !row.is_empty() {
691                                if let Data::String(url) = unsafe { row.get_unchecked(0) } {
692                                    self.cache.set(key, Data::String(url.clone())).await;
693                                    url.clone()
694                                } else {
695                                    self.cache.set(key, Data::None).await;
696                                    match &self.not_found.param {
697                                        Some(param) => format!(
698                                            "/{}/{}/{}/{}",
699                                            self.not_found.module, self.not_found.class, self.not_found.action, param
700                                        ),
701                                        None => {
702                                            format!("/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action)
703                                        }
704                                    }
705                                }
706                            } else {
707                                self.cache.set(key, Data::None).await;
708                                match &self.not_found.param {
709                                    Some(param) => {
710                                        format!("/{}/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action, param)
711                                    }
712                                    None => {
713                                        format!("/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action)
714                                    }
715                                }
716                            }
717                        } else {
718                            self.cache.set(key, Data::None).await;
719                            match &self.not_found.param {
720                                Some(param) => {
721                                    format!("/{}/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action, param)
722                                }
723                                None => format!("/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action),
724                            }
725                        }
726                    }
727                    None => match &self.not_found.param {
728                        Some(param) => {
729                            format!("/{}/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action, param)
730                        }
731                        None => format!("/{}/{}/{}", self.not_found.module, self.not_found.class, self.not_found.action),
732                    },
733                }
734            }
735        }
736    }
737
738    /// Run execute of controller
739    pub(crate) async fn run(action: &mut Action) -> Answer {
740        action.start_route(action.module_id, action.class_id, action.action_id, action.param.clone(), false).await
741    }
742
743    /// Finish work of controller
744    pub(crate) async fn end(mut action: Action) {
745        // Save session
746        Session::save_session(action.db, &action.session, &action.request).await;
747        // Remove temp file
748        for val in action.request.input.file.values() {
749            for f in val {
750                if let Err(e) = remove_file(&f.tmp).await {
751                    Log::warning(1103, Some(format!("filename={}. Error={}", &f.tmp.display(), e)));
752                };
753            }
754        }
755        if action.tool.install_end {
756            action.tool.stop();
757        }
758    }
759
760    /// Simple remove temp file after redirect
761    pub(crate) async fn clean_file(file: Vec<PathBuf>) {
762        for f in file {
763            if let Err(e) = remove_file(&f).await {
764                Log::warning(1103, Some(format!("filename={}. Error={}", f.display(), e)));
765            };
766        }
767    }
768
769    /// Start internal route
770    async fn start_route(&mut self, module_id: i64, class_id: i64, action_id: i64, param: Option<String>, internal: bool) -> Answer {
771        // Check permission
772        if self.get_access(module_id, class_id, action_id).await {
773            if let Some(answer) = self.invoke(module_id, class_id, action_id, param, internal).await {
774                return answer;
775            };
776        }
777        if internal {
778            return Answer::None;
779        }
780        if self.request.ajax {
781            self.response.http_code = Some(404);
782            return Answer::None;
783        }
784
785        // If not /index/index/not_found - then redirect
786        if !(module_id == self.not_found.module_id && class_id == self.not_found.class_id && class_id == self.not_found.action_id) {
787            self.response.redirect = Some(Redirect {
788                url: self.not_found().await,
789                permanently: false,
790            });
791        }
792        Answer::None
793    }
794
795    /// Invoke found controller
796    async fn invoke(&mut self, module_id: i64, class_id: i64, action_id: i64, param: Option<String>, internal: bool) -> Option<Answer> {
797        if let Some(m) = self.engine.get(&module_id) {
798            if let Some(c) = m.get(&class_id) {
799                if let Some(a) = c.get(&action_id) {
800                    if self.current_module_id == module_id && self.current_class_id == class_id {
801                        // Call from the same module as the current one
802                        let i = self.internal;
803                        let p = match param {
804                            Some(str) => self.param.replace(str),
805                            None => self.param.take(),
806                        };
807                        self.internal = internal;
808                        let res = a(self).await;
809                        self.internal = i;
810                        self.param = p;
811                        return Some(res);
812                    } else {
813                        // Call from the different module as the current one
814
815                        // Load new template list
816                        #[cfg(not(debug_assertions))]
817                        let h = match self.template.list.get(&module_id) {
818                            Some(h) => match h.get(&class_id) {
819                                Some(h) => self.html.replace(Arc::clone(h)),
820                                None => self.html.take(),
821                            },
822                            None => self.html.take(),
823                        };
824                        #[cfg(debug_assertions)]
825                        let h = match self.template.read().await.list.get(&module_id) {
826                            Some(h) => match h.get(&class_id) {
827                                Some(h) => self.html.replace(Arc::clone(h)),
828                                None => self.html.take(),
829                            },
830                            None => self.html.take(),
831                        };
832                        // Load new translate list
833                        #[cfg(not(debug_assertions))]
834                        let l = match self.language.list.get(&self.session.get_lang_id()) {
835                            Some(l) => match l.get(&module_id) {
836                                Some(l) => match l.get(&class_id) {
837                                    Some(l) => self.lang.replace(Arc::clone(l)),
838                                    None => self.lang.take(),
839                                },
840                                None => self.lang.take(),
841                            },
842                            None => self.lang.take(),
843                        };
844                        #[cfg(debug_assertions)]
845                        let l = match self.language.read().await.list.get(&self.session.get_lang_id()) {
846                            Some(l) => match l.get(&module_id) {
847                                Some(l) => match l.get(&class_id) {
848                                    Some(l) => self.lang.replace(Arc::clone(l)),
849                                    None => self.lang.take(),
850                                },
851                                None => self.lang.take(),
852                            },
853                            None => self.lang.take(),
854                        };
855                        let i = self.internal;
856                        let p = match param {
857                            Some(str) => self.param.replace(str),
858                            None => self.param.take(),
859                        };
860                        let m = self.current_module_id;
861                        self.current_module_id = module_id;
862                        let c = self.current_class_id;
863                        self.current_class_id = class_id;
864
865                        self.internal = internal;
866
867                        // Call controlle
868                        let res = a(self).await;
869
870                        self.current_module_id = m;
871                        self.current_class_id = c;
872                        self.html = h;
873                        self.lang = l;
874                        self.internal = i;
875                        self.param = p;
876                        return Some(res);
877                    }
878                }
879            }
880        }
881        None
882    }
883
884    fn error_route(action_err: Arc<Route>) -> Route {
885        Route::clone(&action_err)
886    }
887
888    /// Extract route from url
889    async fn extract_route(
890        request: &Request,
891        cache: Arc<Mutex<CacheSys>>,
892        db: Arc<DB>,
893        action_index: Arc<Route>,
894        action_err: Arc<Route>,
895    ) -> Result<Route, Redirect> {
896        if db.in_use() {
897            // Get redirect
898            let key = vec![fnv1a_64!("redirect"), fnv1a_64(request.url.as_bytes())];
899            match CacheSys::get(Arc::clone(&cache), &key).await {
900                Some(d) => match d {
901                    Data::None => {}
902                    Data::Redirect(r) => return Err(r),
903                    _ => {
904                        Log::warning(3000, Some(format!("{:?}", d)));
905                    }
906                },
907                None => {
908                    // Load from database
909                    match db.query_prepare(fnv1a_64!("lib_get_redirect"), &[&request.url], false).await {
910                        Some(v) => {
911                            if v.is_empty() {
912                                CacheSys::set(Arc::clone(&cache), &key, Data::None).await;
913                            } else {
914                                let row = if let Data::Vec(row) = unsafe { v.get_unchecked(0) } {
915                                    row
916                                } else {
917                                    return Ok(Action::error_route(action_err));
918                                };
919                                if row.len() != 2 {
920                                    return Ok(Action::error_route(action_err));
921                                }
922                                let url = if let Data::String(url) = unsafe { row.get_unchecked(0) } {
923                                    url.to_owned()
924                                } else {
925                                    return Ok(Action::error_route(action_err));
926                                };
927                                let permanently = if let Data::Bool(permanently) = unsafe { row.get_unchecked(1) } {
928                                    *permanently
929                                } else {
930                                    return Ok(Action::error_route(action_err));
931                                };
932                                let r = Redirect { url, permanently };
933                                CacheSys::set(Arc::clone(&cache), &key, Data::Redirect(r.clone())).await;
934                                return Err(r);
935                            }
936                        }
937                        None => return Ok(Action::error_route(action_err)),
938                    }
939                }
940            }
941
942            // Get route
943            let key = vec![fnv1a_64!("route"), fnv1a_64(request.url.as_bytes())];
944            match CacheSys::get(Arc::clone(&cache), &key[..]).await {
945                Some(d) => match d {
946                    Data::None => {}
947                    Data::Route(r) => return Ok(r),
948                    _ => {
949                        Log::warning(3001, Some(format!("{:?}", d)));
950                    }
951                },
952                None => {
953                    // Load from database
954                    match db.query_prepare(fnv1a_64!("lib_get_route"), &[&request.url], false).await {
955                        Some(v) => {
956                            if v.is_empty() {
957                                CacheSys::set(Arc::clone(&cache), &key, Data::None).await;
958                            } else {
959                                let row = if let Data::Vec(row) = unsafe { v.get_unchecked(0) } {
960                                    row
961                                } else {
962                                    return Ok(Action::error_route(action_err));
963                                };
964                                if row.len() != 8 {
965                                    return Ok(Action::error_route(action_err));
966                                }
967                                let module = if let Data::String(module) = unsafe { row.get_unchecked(0) } {
968                                    module.to_owned()
969                                } else {
970                                    return Ok(Action::error_route(action_err));
971                                };
972                                let class = if let Data::String(class) = unsafe { row.get_unchecked(1) } {
973                                    class.to_owned()
974                                } else {
975                                    return Ok(Action::error_route(action_err));
976                                };
977                                let action = if let Data::String(action) = unsafe { row.get_unchecked(2) } {
978                                    action.to_owned()
979                                } else {
980                                    return Ok(Action::error_route(action_err));
981                                };
982                                let param = match unsafe { row.get_unchecked(6) } {
983                                    Data::None => None,
984                                    Data::String(param) => {
985                                        if param.is_empty() {
986                                            None
987                                        } else {
988                                            Some(param.to_owned())
989                                        }
990                                    }
991                                    _ => return Ok(Action::error_route(action_err)),
992                                };
993                                let module_id = if let Data::I64(module_id) = unsafe { row.get_unchecked(3) } {
994                                    *module_id
995                                } else {
996                                    return Ok(Action::error_route(action_err));
997                                };
998                                let class_id = if let Data::I64(class_id) = unsafe { row.get_unchecked(4) } {
999                                    *class_id
1000                                } else {
1001                                    return Ok(Action::error_route(action_err));
1002                                };
1003                                let action_id = if let Data::I64(action_id) = unsafe { row.get_unchecked(5) } {
1004                                    *action_id
1005                                } else {
1006                                    return Ok(Action::error_route(action_err));
1007                                };
1008                                let lang_id = match unsafe { row.get_unchecked(7) } {
1009                                    Data::None => None,
1010                                    Data::I64(lang_id) => Some(*lang_id),
1011                                    _ => return Ok(Action::error_route(action_err)),
1012                                };
1013                                let r = Route {
1014                                    module,
1015                                    class,
1016                                    action,
1017                                    module_id,
1018                                    class_id,
1019                                    action_id,
1020                                    param,
1021                                    lang_id,
1022                                };
1023                                CacheSys::set(Arc::clone(&cache), &key, Data::Route(r.clone())).await;
1024                                return Ok(r);
1025                            }
1026                        }
1027                        None => return Ok(Action::error_route(action_err)),
1028                    }
1029                }
1030            }
1031        }
1032
1033        if request.url != "/" {
1034            let mut load: Vec<&str> = request.url.splitn(5, '/').collect();
1035            load.retain(|&x| !x.is_empty());
1036            let r = match load.len() {
1037                1 => {
1038                    if db.in_use() {
1039                        let module = unsafe { *load.get_unchecked(0) };
1040                        Route {
1041                            module: module.to_owned(),
1042                            class: action_index.class.clone(),
1043                            action: action_index.action.clone(),
1044                            module_id: fnv1a_64(module.as_bytes()),
1045                            class_id: action_index.class_id,
1046                            action_id: action_index.action_id,
1047                            param: None,
1048                            lang_id: None,
1049                        }
1050                    } else {
1051                        Route::default_install()
1052                    }
1053                }
1054                2 => {
1055                    if db.in_use() {
1056                        let module = unsafe { *load.get_unchecked(0) };
1057                        let class = unsafe { *load.get_unchecked(1) };
1058                        Route {
1059                            module: module.to_owned(),
1060                            class: class.to_owned(),
1061                            action: action_index.action.clone(),
1062                            module_id: fnv1a_64(module.as_bytes()),
1063                            class_id: fnv1a_64(class.as_bytes()),
1064                            action_id: action_index.action_id,
1065                            param: None,
1066                            lang_id: None,
1067                        }
1068                    } else {
1069                        Route::default_install()
1070                    }
1071                }
1072                3 => {
1073                    if db.in_use() {
1074                        let module = unsafe { *load.get_unchecked(0) };
1075                        let class = unsafe { *load.get_unchecked(1) };
1076                        let action = unsafe { *load.get_unchecked(2) };
1077                        Route {
1078                            module: module.to_owned(),
1079                            class: class.to_owned(),
1080                            action: action.to_owned(),
1081                            module_id: fnv1a_64(module.as_bytes()),
1082                            class_id: fnv1a_64(class.as_bytes()),
1083                            action_id: fnv1a_64(action.as_bytes()),
1084                            param: None,
1085                            lang_id: None,
1086                        }
1087                    } else {
1088                        let install = Route::default_install();
1089                        let action = unsafe { *load.get_unchecked(2) };
1090                        Route {
1091                            module: install.module.to_owned(),
1092                            class: install.class.to_owned(),
1093                            action: action.to_owned(),
1094                            module_id: install.module_id,
1095                            class_id: install.class_id,
1096                            action_id: fnv1a_64(action.as_bytes()),
1097                            param: None,
1098                            lang_id: None,
1099                        }
1100                    }
1101                }
1102                4 => {
1103                    if db.in_use() {
1104                        let module = unsafe { *load.get_unchecked(0) };
1105                        let class = unsafe { *load.get_unchecked(1) };
1106                        let action = unsafe { *load.get_unchecked(2) };
1107                        let param = unsafe { *load.get_unchecked(3) };
1108                        Route {
1109                            module: module.to_owned(),
1110                            class: class.to_owned(),
1111                            action: action.to_owned(),
1112                            module_id: fnv1a_64(module.as_bytes()),
1113                            class_id: fnv1a_64(class.as_bytes()),
1114                            action_id: fnv1a_64(action.as_bytes()),
1115                            param: Some(param.to_owned()),
1116                            lang_id: None,
1117                        }
1118                    } else {
1119                        let install = Route::default_install();
1120                        let action = unsafe { *load.get_unchecked(2) };
1121                        let param = unsafe { *load.get_unchecked(3) };
1122                        Route {
1123                            module: install.module.to_owned(),
1124                            class: install.class.to_owned(),
1125                            action: action.to_owned(),
1126                            module_id: install.module_id,
1127                            class_id: install.class_id,
1128                            action_id: fnv1a_64(action.as_bytes()),
1129                            param: Some(param.to_owned()),
1130                            lang_id: None,
1131                        }
1132                    }
1133                }
1134                _ => {
1135                    if db.in_use() {
1136                        Route::clone(&action_index)
1137                    } else {
1138                        Route::default_install()
1139                    }
1140                }
1141            };
1142            Ok(r)
1143        } else if db.in_use() {
1144            Ok(Route::clone(&action_index))
1145        } else {
1146            Ok(Route::default_install())
1147        }
1148    }
1149
1150    fn format_route(module: &str, class: &str, action: &str, param: Option<&str>) -> String {
1151        match param {
1152            Some(s) => {
1153                format!("/{}/{}/{}/{}", module, class, action, s)
1154            }
1155            None => format!("/{}/{}/{}", module, class, action),
1156        }
1157    }
1158}