create actix_admin_builder function
This commit is contained in:
parent
7c8cf72668
commit
90a17e64d3
@ -1,11 +1,11 @@
|
|||||||
|
use actix_web::http::header;
|
||||||
use actix_web::{error, web, Error, HttpRequest, HttpResponse};
|
use actix_web::{error, web, Error, HttpRequest, HttpResponse};
|
||||||
|
use async_trait::async_trait;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use sea_orm::{ DatabaseConnection};
|
use sea_orm::DatabaseConnection;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use actix_web::http::header;
|
|
||||||
use tera::{Context, Tera};
|
use tera::{Context, Tera};
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
pub use actix_admin_macros::DeriveActixAdminModel;
|
pub use actix_admin_macros::DeriveActixAdminModel;
|
||||||
|
|
||||||
@ -48,19 +48,27 @@ pub trait AppDataTrait {
|
|||||||
// ActixAdminModel
|
// ActixAdminModel
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait ActixAdminModelTrait {
|
pub trait ActixAdminModelTrait {
|
||||||
async fn list_model(db: &DatabaseConnection, page: usize, posts_per_page: usize) -> Vec<ActixAdminModel>;
|
async fn list_model(
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
page: usize,
|
||||||
|
posts_per_page: usize,
|
||||||
|
) -> Vec<ActixAdminModel>;
|
||||||
fn get_fields() -> Vec<(String, ActixAdminField)>;
|
fn get_fields() -> Vec<(String, ActixAdminField)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct ActixAdminModel {
|
pub struct ActixAdminModel {
|
||||||
pub values: HashMap<String, String>
|
pub values: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActixAdminViewModel
|
// ActixAdminViewModel
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait ActixAdminViewModelTrait {
|
pub trait ActixAdminViewModelTrait {
|
||||||
async fn list(db: &DatabaseConnection, page: usize, entities_per_page: usize) -> Vec<ActixAdminModel>;
|
async fn list(
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
page: usize,
|
||||||
|
entities_per_page: usize,
|
||||||
|
) -> Vec<ActixAdminModel>;
|
||||||
async fn create_entity(db: &DatabaseConnection, model: ActixAdminModel) -> ActixAdminModel;
|
async fn create_entity(db: &DatabaseConnection, model: ActixAdminModel) -> ActixAdminModel;
|
||||||
fn get_entity_name() -> String;
|
fn get_entity_name() -> String;
|
||||||
}
|
}
|
||||||
@ -71,34 +79,64 @@ pub struct ActixAdminViewModel {
|
|||||||
pub fields: Vec<(String, ActixAdminField)>,
|
pub fields: Vec<(String, ActixAdminField)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActixAdminController
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ActixAdmin {
|
pub struct ActixAdmin {
|
||||||
pub entity_names: Vec<String>,
|
pub entity_names: Vec<String>,
|
||||||
pub view_models: HashMap<String, ActixAdminViewModel>
|
pub view_models: HashMap<String, ActixAdminViewModel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActixAdmin {
|
pub struct ActixAdminBuilder {
|
||||||
pub fn new() -> Self {
|
pub scopes: Vec<actix_web::Scope>,
|
||||||
let actix_admin = ActixAdmin {
|
pub actix_admin: ActixAdmin,
|
||||||
entity_names: Vec::new(),
|
}
|
||||||
view_models: HashMap::new()
|
|
||||||
};
|
pub trait ActixAdminBuilderTrait {
|
||||||
actix_admin
|
fn new() -> Self;
|
||||||
|
fn add_entity<T: AppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(&mut self, view_model: &ActixAdminViewModel);
|
||||||
|
fn get_scope<T: AppDataTrait + 'static>(self) -> actix_web::Scope;
|
||||||
|
fn get_actix_admin(&self) -> ActixAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActixAdminBuilderTrait for ActixAdminBuilder {
|
||||||
|
fn new() -> Self {
|
||||||
|
ActixAdminBuilder {
|
||||||
|
actix_admin: ActixAdmin {
|
||||||
|
entity_names: Vec::new(),
|
||||||
|
view_models: HashMap::new(),
|
||||||
|
},
|
||||||
|
scopes: Vec::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_entity<E: ActixAdminViewModelTrait>(mut self, view_model: &ActixAdminViewModel) -> Self {
|
fn add_entity<T: AppDataTrait + 'static, E: ActixAdminViewModelTrait + 'static>(
|
||||||
self.entity_names.push(E::get_entity_name());
|
&mut self,
|
||||||
let view_model_cloned = view_model.clone();
|
view_model: &ActixAdminViewModel,
|
||||||
let key = view_model.entity_name.to_string();
|
) {
|
||||||
self.view_models.insert(key, view_model_cloned);
|
self.scopes.push(
|
||||||
self
|
web::scope(&format!("/{}", E::get_entity_name()))
|
||||||
|
.route("/list", web::get().to(self::list::<T, E>))
|
||||||
|
.route("/create", web::get().to(self::create_get::<T, E>))
|
||||||
|
.route("/create", web::post().to(self::create_post::<T, E>)),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.actix_admin.entity_names.push(E::get_entity_name());
|
||||||
|
//let view_model_cloned = view_model.clone();
|
||||||
|
let key = E::get_entity_name();
|
||||||
|
self.actix_admin.view_models.insert(key, view_model.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_scope<T: AppDataTrait + 'static >(&self) -> actix_web::Scope {
|
fn get_scope<T: AppDataTrait + 'static>(self) -> actix_web::Scope {
|
||||||
let scope = web::scope("/admin").route("/", web::get().to(index::<T>));
|
let mut scope = web::scope("/admin").route("/", web::get().to(index::<T>));
|
||||||
|
for entity_scope in self.scopes {
|
||||||
|
scope = scope.service(entity_scope);
|
||||||
|
}
|
||||||
|
|
||||||
scope
|
scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_actix_admin(&self) -> ActixAdmin {
|
||||||
|
self.actix_admin.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for ActixAdminModel {
|
impl From<String> for ActixAdminModel {
|
||||||
@ -107,13 +145,13 @@ impl From<String> for ActixAdminModel {
|
|||||||
let key_values: Vec<&str> = string.split('&').collect();
|
let key_values: Vec<&str> = string.split('&').collect();
|
||||||
for key_value in key_values {
|
for key_value in key_values {
|
||||||
let mut iter = key_value.splitn(2, '=');
|
let mut iter = key_value.splitn(2, '=');
|
||||||
hashmap.insert(iter.next().unwrap().to_string(), iter.next().unwrap().to_string());
|
hashmap.insert(
|
||||||
|
iter.next().unwrap().to_string(),
|
||||||
|
iter.next().unwrap().to_string(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActixAdminModel {
|
ActixAdminModel { values: hashmap }
|
||||||
// TODO: create dynamically
|
|
||||||
values: hashmap
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,9 +161,9 @@ impl ActixAdminModel {
|
|||||||
let value = self.values.get(key).unwrap().to_string().parse::<T>();
|
let value = self.values.get(key).unwrap().to_string().parse::<T>();
|
||||||
match value {
|
match value {
|
||||||
Ok(val) => Some(val),
|
Ok(val) => Some(val),
|
||||||
Err(_) => None //panic!("key {} could not be parsed", key)
|
Err(_) => None, //panic!("key {} could not be parsed", key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn index<T: AppDataTrait>(data: web::Data<T>) -> Result<HttpResponse, Error> {
|
pub async fn index<T: AppDataTrait>(data: web::Data<T>) -> Result<HttpResponse, Error> {
|
||||||
@ -139,7 +177,10 @@ pub async fn index<T: AppDataTrait>(data: web::Data<T>) -> Result<HttpResponse,
|
|||||||
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(req: HttpRequest, data: web::Data<T>) -> Result<HttpResponse, Error> {
|
pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(
|
||||||
|
req: HttpRequest,
|
||||||
|
data: web::Data<T>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
let entity_name = E::get_entity_name();
|
let entity_name = E::get_entity_name();
|
||||||
let actix_admin = data.get_actix_admin();
|
let actix_admin = data.get_actix_admin();
|
||||||
let view_model: &ActixAdminViewModel = actix_admin.view_models.get(&entity_name).unwrap();
|
let view_model: &ActixAdminViewModel = actix_admin.view_models.get(&entity_name).unwrap();
|
||||||
@ -148,7 +189,9 @@ pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(req: HttpRequest
|
|||||||
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
|
let params = web::Query::<Params>::from_query(req.query_string()).unwrap();
|
||||||
|
|
||||||
let page = params.page.unwrap_or(1);
|
let page = params.page.unwrap_or(1);
|
||||||
let entities_per_page = params.entities_per_page.unwrap_or(DEFAULT_ENTITIES_PER_PAGE);
|
let entities_per_page = params
|
||||||
|
.entities_per_page
|
||||||
|
.unwrap_or(DEFAULT_ENTITIES_PER_PAGE);
|
||||||
|
|
||||||
let db = data.get_db();
|
let db = data.get_db();
|
||||||
let entities: Vec<ActixAdminModel> = E::list(db, page, entities_per_page).await;
|
let entities: Vec<ActixAdminModel> = E::list(db, page, entities_per_page).await;
|
||||||
@ -167,7 +210,12 @@ pub async fn list<T: AppDataTrait, E: ActixAdminViewModelTrait>(req: HttpRequest
|
|||||||
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_get<T: AppDataTrait, E: ActixAdminViewModelTrait>(_req: HttpRequest, data: web::Data<T>, _body: web::Payload, _text: String) -> Result<HttpResponse, Error> {
|
pub async fn create_get<T: AppDataTrait, E: ActixAdminViewModelTrait>(
|
||||||
|
_req: HttpRequest,
|
||||||
|
data: web::Data<T>,
|
||||||
|
_body: web::Payload,
|
||||||
|
_text: String,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
let _db = &data.get_db();
|
let _db = &data.get_db();
|
||||||
let entity_name = E::get_entity_name();
|
let entity_name = E::get_entity_name();
|
||||||
let entity_names = &data.get_actix_admin().entity_names;
|
let entity_names = &data.get_actix_admin().entity_names;
|
||||||
@ -187,16 +235,22 @@ pub async fn create_get<T: AppDataTrait, E: ActixAdminViewModelTrait>(_req: Http
|
|||||||
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
Ok(HttpResponse::Ok().content_type("text/html").body(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_post<T: AppDataTrait, E: ActixAdminViewModelTrait>(_req: HttpRequest, data: web::Data<T>, text: String) -> Result<HttpResponse, Error> {
|
pub async fn create_post<T: AppDataTrait, E: ActixAdminViewModelTrait>(
|
||||||
|
_req: HttpRequest,
|
||||||
|
data: web::Data<T>,
|
||||||
|
text: String,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
let db = &data.get_db();
|
let db = &data.get_db();
|
||||||
let entity_name = E::get_entity_name();
|
let entity_name = E::get_entity_name();
|
||||||
let actix_admin = data.get_actix_admin();
|
let actix_admin = data.get_actix_admin();
|
||||||
|
|
||||||
let view_model = actix_admin.view_models.get(&entity_name).unwrap();
|
let view_model = actix_admin.view_models.get(&entity_name).unwrap();
|
||||||
let mut admin_model = ActixAdminModel::from(text);
|
let mut admin_model = ActixAdminModel::from(text);
|
||||||
admin_model = E::create_entity(db, admin_model).await;
|
admin_model = E::create_entity(db, admin_model).await;
|
||||||
|
|
||||||
Ok(HttpResponse::Found()
|
Ok(HttpResponse::Found()
|
||||||
.append_header((header::LOCATION, format!("/admin/{}/list", view_model.entity_name)))
|
.append_header((
|
||||||
|
header::LOCATION,
|
||||||
|
format!("/admin/{}/list", view_model.entity_name),
|
||||||
|
))
|
||||||
.finish())
|
.finish())
|
||||||
}
|
}
|
||||||
|
BIN
database.db-wal
BIN
database.db-wal
Binary file not shown.
45
src/main.rs
45
src/main.rs
@ -1,7 +1,7 @@
|
|||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
use actix_admin::{
|
use actix_admin::{
|
||||||
ActixAdmin, ActixAdminViewModel,
|
ActixAdmin, ActixAdminBuilder, ActixAdminBuilderTrait, ActixAdminViewModel,
|
||||||
AppDataTrait as ActixAdminAppDataTrait,
|
AppDataTrait as ActixAdminAppDataTrait,
|
||||||
};
|
};
|
||||||
use actix_session::{CookieSession, Session};
|
use actix_session::{CookieSession, Session};
|
||||||
@ -55,24 +55,15 @@ async fn index(session: Session, data: web::Data<AppState>) -> HttpResponse {
|
|||||||
HttpResponse::Ok().body(rendered)
|
HttpResponse::Ok().body(rendered)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Generate this with a Macro accepting Tuples of (Entity, viewmodel)
|
fn create_actix_admin_builder() -> ActixAdminBuilder {
|
||||||
fn setup_actix_admin(
|
let post_view_model = ActixAdminViewModel::from(Post);
|
||||||
actix_admin: &ActixAdmin,
|
let comment_view_model = ActixAdminViewModel::from(Comment);
|
||||||
) -> actix_web::Scope {
|
|
||||||
actix_admin
|
let mut admin_builder = ActixAdminBuilder::new();
|
||||||
.create_scope::<AppState>()
|
admin_builder.add_entity::<AppState, Post>(&post_view_model);
|
||||||
.service(
|
admin_builder.add_entity::<AppState, Comment>(&comment_view_model);
|
||||||
web::scope("/post")
|
|
||||||
.route("/list", web::get().to(actix_admin::list::<AppState, Post>))
|
admin_builder
|
||||||
.route("/create", web::get().to(actix_admin::create_get::<AppState, Post>))
|
|
||||||
.route("/create", web::post().to(actix_admin::create_post::<AppState, Post>))
|
|
||||||
)
|
|
||||||
.service(
|
|
||||||
web::scope("/comment")
|
|
||||||
.route("/list", web::get().to(actix_admin::list::<AppState, Comment>))
|
|
||||||
.route("/create", web::get().to(actix_admin::create_get::<AppState, Comment>))
|
|
||||||
.route("/create", web::post().to(actix_admin::create_post::<AppState, Comment>))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
@ -109,17 +100,13 @@ async fn main() {
|
|||||||
let conn = sea_orm::Database::connect(opt).await.unwrap();
|
let conn = sea_orm::Database::connect(opt).await.unwrap();
|
||||||
let _ = entity::create_post_table(&conn).await;
|
let _ = entity::create_post_table(&conn).await;
|
||||||
|
|
||||||
let post_view_model = ActixAdminViewModel::from(Post);
|
let actix_admin = create_actix_admin_builder().get_actix_admin();
|
||||||
let comment_view_model = ActixAdminViewModel::from(Comment);
|
|
||||||
let actix_admin = ActixAdmin::new()
|
|
||||||
.add_entity::<Post>(&post_view_model)
|
|
||||||
.add_entity::<Comment>(&comment_view_model)
|
|
||||||
;
|
|
||||||
let app_state = AppState {
|
let app_state = AppState {
|
||||||
oauth: client,
|
oauth: client,
|
||||||
tmpl: tera,
|
tmpl: tera,
|
||||||
db: conn,
|
db: conn,
|
||||||
actix_admin: actix_admin.clone(),
|
actix_admin: actix_admin,
|
||||||
};
|
};
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
@ -128,9 +115,9 @@ async fn main() {
|
|||||||
.wrap(CookieSession::signed(&[0; 32]).secure(false))
|
.wrap(CookieSession::signed(&[0; 32]).secure(false))
|
||||||
.route("/", web::get().to(index))
|
.route("/", web::get().to(index))
|
||||||
.service(azure_auth.clone().create_scope::<AppState>())
|
.service(azure_auth.clone().create_scope::<AppState>())
|
||||||
.service(setup_actix_admin(
|
.service(
|
||||||
&actix_admin
|
create_actix_admin_builder().get_scope::<AppState>()
|
||||||
))
|
)
|
||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
})
|
})
|
||||||
.bind("127.0.0.1:5000")
|
.bind("127.0.0.1:5000")
|
||||||
|
Loading…
Reference in New Issue
Block a user