actix_admin/
lib.rs

1//! # Actix Admin
2//!
3//! The actix-admin crate aims at creating a web admin interface similar to other admin interfaces (such as [flask-admin](https://github.com/flask-admin/flask-admin) in python).
4//!
5//! See the [documentation](https://mgugger.github.io/actix-admin/) at [https://mgugger.github.io/actix-admin/](https://mgugger.github.io/actix-admin/).
6
7use actix_session::Session;
8use actix_web::{
9    error,
10    http::{header::ContentType, StatusCode},
11    HttpResponse,
12};
13use async_trait::async_trait;
14use derive_more::{Display, Error};
15use sea_orm::DatabaseConnection;
16use serde_derive::Serialize;
17use std::{collections::HashMap, fmt};
18use tera::Tera;
19
20pub mod builder;
21pub mod model;
22pub mod routes;
23pub mod tera_templates;
24pub mod view_model;
25
26pub mod prelude {
27    pub use crate::builder::{ActixAdminBuilder, ActixAdminBuilderTrait};
28    pub use crate::model::{
29        ActixAdminModel, ActixAdminModelFilter, ActixAdminModelFilterTrait,
30        ActixAdminModelFilterType, ActixAdminModelTrait, ActixAdminModelValidationTrait,
31    };
32    pub use crate::routes::{create_or_edit_post, get_admin_ctx, SortOrder};
33    pub use crate::view_model::{
34        ActixAdminViewModel, ActixAdminViewModelField, ActixAdminViewModelFieldType,
35        ActixAdminViewModelFilter, ActixAdminViewModelParams, ActixAdminViewModelSerializable,
36        ActixAdminViewModelTrait,
37    };
38    pub use crate::{hashmap, ActixAdminSelectListTrait};
39    pub use crate::{ActixAdmin, ActixAdminConfiguration, ActixAdminError, ActixAdminErrorType};
40    pub use actix_admin_macros::{
41        DeriveActixAdmin, DeriveActixAdminEnumSelectList, DeriveActixAdminModel,
42        DeriveActixAdminModelSelectList, DeriveActixAdminViewModel,
43    };
44    pub use actix_session::Session;
45    pub use async_trait::async_trait;
46    pub use lazy_static::lazy_static;
47}
48
49use crate::prelude::*;
50
51#[macro_export]
52macro_rules! hashmap {
53    ($( $key: expr => $val: expr ),*) => {{
54         let mut map = ::std::collections::HashMap::new();
55         $( map.insert($key.to_string(), $val); )*
56         map
57    }}
58}
59
60// SelectListTrait
61#[async_trait]
62pub trait ActixAdminSelectListTrait {
63    async fn get_key_value(
64        db: &DatabaseConnection,
65        tenant_ref: Option<i32>
66    ) -> core::result::Result<Vec<(String, String)>, ActixAdminError>;
67}
68
69#[derive(Clone)]
70pub struct ActixAdminConfiguration {
71    pub enable_auth: bool,
72    pub user_is_logged_in: Option<for<'a> fn(&'a Session) -> bool>,
73    pub user_tenant_ref: Option<for<'a> fn(&'a Session) -> Option<i32>>,
74    pub login_link: Option<String>,
75    pub logout_link: Option<String>,
76    pub file_upload_directory: &'static str,
77    pub navbar_title: &'static str,
78    pub base_path: &'static str,
79    pub custom_css_paths: Option<Vec<String>>,
80    pub custom_js_paths: Option<Vec<String>>
81}
82
83#[derive(Clone)]
84pub struct ActixAdmin {
85    pub entity_names: HashMap<String, Vec<ActixAdminMenuElement>>,
86    pub view_models: HashMap<String, ActixAdminViewModel>,
87    pub card_grids: HashMap<String, Vec<Vec<String>>>,
88    pub configuration: ActixAdminConfiguration,
89    pub tera: Tera,
90    pub support_path: Option<String>
91}
92
93#[derive(PartialEq, Eq, Clone, Serialize)]
94pub struct ActixAdminMenuElement {
95    pub name: String,
96    pub link: String,
97    pub is_custom_handler: bool,
98}
99
100#[derive(Debug, Error)]
101pub struct ActixAdminError {
102    pub ty: ActixAdminErrorType,
103    pub msg: String,
104}
105
106impl Display for ActixAdminError {
107    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
108        match &*self {
109            _ => write!(formatter, "{}: {}", &self.ty, &self.msg),
110        }
111    }
112}
113
114// Errors
115#[derive(Debug, Display, Error)]
116pub enum ActixAdminErrorType {
117    #[display("Internal error")]
118    InternalError,
119
120    #[display("Form has validation errors")]
121    ValidationErrors,
122
123    #[display("Could not list entities")]
124    ListError,
125
126    #[display("Could not create entity")]
127    CreateError,
128
129    #[display("Could not delete entity")]
130    DeleteError,
131
132    #[display("Could not edit entity")]
133    EditError,
134
135    #[display("Database error")]
136    DatabaseError,
137
138    #[display("Entity does not exist")]
139    EntityDoesNotExistError,
140}
141
142impl error::ResponseError for ActixAdminError {
143    fn error_response(&self) -> HttpResponse {
144        HttpResponse::build(self.status_code())
145            .insert_header(ContentType::html())
146            .body(self.to_string())
147    }
148
149    fn status_code(&self) -> StatusCode {
150        match *self {
151            _ => StatusCode::INTERNAL_SERVER_ERROR,
152        }
153    }
154}
155
156impl std::convert::From<sea_orm::DbErr> for ActixAdminError {
157    fn from(err: sea_orm::DbErr) -> ActixAdminError {
158        ActixAdminError {
159            ty: ActixAdminErrorType::DatabaseError,
160            msg: err.to_string(),
161        }
162    }
163}
164
165// Notifications
166#[derive(Debug, Display, Serialize)]
167pub enum ActixAdminNotificationType {
168    #[display("is-danger")]
169    Danger,
170}
171
172#[derive(Debug, Serialize)]
173pub struct ActixAdminNotification {
174    css_class: String,
175    message: String,
176}
177
178impl std::convert::From<ActixAdminError> for ActixAdminNotification {
179    fn from(e: ActixAdminError) -> ActixAdminNotification {
180        ActixAdminNotification {
181            css_class: ActixAdminNotificationType::Danger.to_string(),
182            message: e.to_string(),
183        }
184    }
185}