From 521aee96838521dfa17261dab0af5d5499a905b8 Mon Sep 17 00:00:00 2001 From: veypi Date: Fri, 13 Oct 2023 03:57:23 +0800 Subject: [PATCH] crud macro / login token redirect --- oab/proc/src/crud.rs | 96 ++++++++++++++++++--------- oab/proc/src/lib.rs | 5 ++ oab/src/api/access.rs | 35 +++++++++- oab/src/api/app.rs | 11 +-- oab/src/api/appuser.rs | 4 +- oab/src/api/mod.rs | 2 + oab/src/api/resource.rs | 8 +-- oab/src/api/role.rs | 12 ++-- oab/src/api/token.rs | 68 +++++++++++++++++++ oab/src/api/upload.rs | 44 ------------ oab/src/api/user.rs | 4 +- oab/src/models/mod.rs | 4 +- oab/src/models/user_plugin.rs | 2 +- oaweb/src/components/app.vue | 2 +- oaweb/src/components/crud.vue | 2 + oaweb/src/components/editor/index.vue | 4 +- oaweb/src/layouts/AppLayout.vue | 3 +- oaweb/src/pages/AppAuth.vue | 2 +- oaweb/src/pages/AppCfg.vue | 3 + oaweb/src/pages/AppHome.vue | 4 +- oaweb/src/pages/AppUser.vue | 2 +- oaweb/src/pages/login.vue | 20 +++--- 22 files changed, 215 insertions(+), 122 deletions(-) create mode 100644 oab/src/api/token.rs diff --git a/oab/proc/src/crud.rs b/oab/proc/src/crud.rs index 0b3ab69..58c57f7 100644 --- a/oab/proc/src/crud.rs +++ b/oab/proc/src/crud.rs @@ -5,9 +5,9 @@ // Distributed under terms of the MIT license. // -use proc_macro2::{Ident, Span}; +use proc_macro2::Span; use quote::{format_ident, quote, ToTokens}; -use syn::{AttributeArgs, ItemFn, NestedMeta, ReturnType, Token}; +use syn::{AttributeArgs, ItemFn, NestedMeta, ReturnType}; pub struct CrudWrap { func: ItemFn, @@ -17,14 +17,11 @@ pub struct CrudWrap { impl CrudWrap { pub fn new(args: AttributeArgs, func: ItemFn, method: i32) -> syn::Result { - let args = Crud::new(args)?; + let args = Crud::new(args, method)?; Ok(Self { func, args, method }) } -} - -impl ToTokens for CrudWrap { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + fn update(&self, tokens: &mut proc_macro2::TokenStream) { let func_vis = &self.func.vis; let func_block = &self.func.block; @@ -42,7 +39,7 @@ impl ToTokens for CrudWrap { }; let model_name = &self.args.model; - let builder_fields = self.args.attrs.iter().map(|field| { + let builder_fields = self.args.props.iter().map(|field| { quote! { if let Some(#field) = _data.#field { obj.#field = sea_orm::Set(#field.into()) @@ -51,12 +48,10 @@ impl ToTokens for CrudWrap { }); let (args_fields, filter_fields) = match self.args.filters.len() { 1 => { - let pair = &self.args.filters[0]; - let k = &pair[0]; - let _k = format_ident!("_{}", &pair[0]); - let v = &pair[1]; + let k = &self.args.filters[0]; + let _k = format_ident!("_{}", k); ( - vec![quote! {let #_k = #v; }], + vec![quote! {let #_k = _id; }], vec![quote! { filter(crate::models::#model_name::Column::#k.eq(#_k)) }], @@ -67,17 +62,17 @@ impl ToTokens for CrudWrap { .filters .iter() .enumerate() - .map(|(idx, [k, v])| { + .map(|(idx, k)| { let _k = format_ident!("_{}", k); quote! { - let #_k = #v.#idx; + let #_k = &_id[#idx]; } }) .collect(), self.args .filters .iter() - .map(|[k, _]| { + .map(|k| { let _k = format_ident!("_{}", k); quote! { filter(crate::models::#model_name::Column::#k.eq(#_k)) @@ -115,19 +110,43 @@ impl ToTokens for CrudWrap { let _stream = tokens.extend(stream); } + + fn copy(&self, _: &mut proc_macro2::TokenStream) { + let _ = self.args.attrs.len(); + } +} + +impl ToTokens for CrudWrap { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match self.method { + 3 => self.update(tokens), + // 3 => self.update(tokens), + _ => self.copy(tokens), + } + } } struct Crud { model: syn::Ident, attrs: Vec, - filters: Vec<[syn::Ident; 2]>, + filters: Vec, + props: Vec, } impl Crud { - fn new(args: AttributeArgs) -> syn::Result { + fn new(args: AttributeArgs, method: i32) -> syn::Result { let mut model: Option = None; let mut attrs: Vec = Vec::new(); - let mut filters: Vec<[syn::Ident; 2]> = Vec::new(); + let mut filters: Vec = Vec::new(); + let mut props: Vec = Vec::new(); + if method == 0 { + return Ok(Self { + model: format_ident!("a"), + attrs, + filters, + props, + }); + } for arg in args { match arg { // NestedMeta::Lit(syn::Lit::Str(lit)) => { @@ -147,17 +166,32 @@ impl Crud { lit: syn::Lit::Str(lit_str), .. })) => { - match path.get_ident() { - Some(expr) => { - filters.push([expr.to_owned(), format_ident!("{}", lit_str.value())]) - } - None => { - return Err(syn::Error::new_spanned( - path, - "Unknown identifier. Available: 'aid='AppId'", - )); - } - }; + if path.is_ident("filter") { + filters = lit_str + .value() + .replace(" ", "") + .split(",") + .into_iter() + .map(|l| { + return format_ident!("{}", l); + }) + .collect(); + } else if path.is_ident("props") { + props = lit_str + .value() + .replace(" ", "") + .split(",") + .into_iter() + .map(|l| { + return format_ident!("{}", l); + }) + .collect(); + } else { + return Err(syn::Error::new_spanned( + path, + "Unknown identifier. Available: filter, props ", + )); + } } _ => { @@ -165,12 +199,12 @@ impl Crud { } } } - println!("|||||||||||____________________"); match model { Some(model) => Ok(Self { model, attrs, filters, + props, }), None => Err(syn::Error::new( Span::call_site(), diff --git a/oab/proc/src/lib.rs b/oab/proc/src/lib.rs index 409de2e..3b2a9f3 100644 --- a/oab/proc/src/lib.rs +++ b/oab/proc/src/lib.rs @@ -53,6 +53,11 @@ pub fn crud_update(args: TokenStream, input: TokenStream) -> TokenStream { derive_crud(3, args, input) } +#[proc_macro_attribute] +pub fn crud_test(args: TokenStream, input: TokenStream) -> TokenStream { + derive_crud(0, args, input) +} + fn derive_crud(method: i32, args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as AttributeArgs); let func = parse_macro_input!(input as ItemFn); diff --git a/oab/src/api/access.rs b/oab/src/api/access.rs index 85fe373..69c646d 100644 --- a/oab/src/api/access.rs +++ b/oab/src/api/access.rs @@ -87,9 +87,9 @@ pub struct UpdateOpt { #[patch("/app/{aid}/access/{id}")] #[access_delete("app")] -#[crud_update(access, AppId = "_id", Id = "_id", level, rid)] +#[crud_update(access, filter = "AppId, Id", props = "level, rid")] pub async fn update( - id: web::Path<(String, String)>, + id: web::Path<[String; 2]>, data: web::Json, stat: web::Data, ) -> Result { @@ -111,3 +111,34 @@ pub async fn delete( info!("{:#?}", res); Ok("ok") } + +// mod test { +// use crate::{ +// models::{self}, +// AppState, Error, Result, +// }; +// use actix_web::{delete, get, patch, post, web, Responder}; +// use proc::crud_test; +// use proc::{access_create, access_delete, access_read, crud_update}; +// use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter}; +// use serde::{Deserialize, Serialize}; +// use tracing::info; +// #[derive(Debug, Clone, Deserialize, Serialize)] +// pub struct UpdateOpt { +// pub level: Option, +// pub rid: Option, +// } +// #[derive(Debug, Clone, Deserialize, Serialize)] +// pub struct IDOpt { +// pub app_id: Option, +// pub id: Option, +// } +// #[crud_test(access, filter = "AppId, Id", props = "level, rid")] +// pub async fn update( +// id: web::Path<[String; 2]>, +// data: web::Json, +// stat: web::Data, +// ) -> Result { +// Ok("") +// } +// } diff --git a/oab/src/api/app.rs b/oab/src/api/app.rs index b2144c4..a4f5b1e 100644 --- a/oab/src/api/app.rs +++ b/oab/src/api/app.rs @@ -96,15 +96,8 @@ pub struct UpdateOpt { #[access_update("app")] #[crud_update( app, - Id = "_id", - name, - icon, - des, - join_method, - role_id, - redirect, - host, - status + filter = "Id", + props = "name,icon,des,join_method,role_id,redirect,host,status" )] pub async fn update( id: web::Path, diff --git a/oab/src/api/appuser.rs b/oab/src/api/appuser.rs index 23cd22b..32d9658 100644 --- a/oab/src/api/appuser.rs +++ b/oab/src/api/appuser.rs @@ -102,9 +102,9 @@ pub struct UpdateOpt { #[patch("/app/{aid}/user/{uid}")] #[access_delete("app")] -#[crud_update(app_user, AppId = "_id", UserId = "_id", status)] +#[crud_update(app_user, filter = "AppId, UserId", props = "status")] pub async fn update( - id: web::Path<(String, String)>, + id: web::Path<[String; 2]>, data: web::Json, stat: web::Data, ) -> Result { diff --git a/oab/src/api/mod.rs b/oab/src/api/mod.rs index 25eedf7..6d55b58 100644 --- a/oab/src/api/mod.rs +++ b/oab/src/api/mod.rs @@ -11,6 +11,7 @@ mod app; mod appuser; mod resource; mod role; +mod token; mod upload; mod user; use actix_web::web; @@ -29,6 +30,7 @@ pub fn routes(cfg: &mut web::ServiceConfig) { .service(app::update) .service(app::del); // cfg.route("/acc", web::get().to(access::UpdateOpt::update)); + cfg.service(token::get); cfg.service(appuser::get) .service(appuser::add) diff --git a/oab/src/api/resource.rs b/oab/src/api/resource.rs index cccad92..97332d9 100644 --- a/oab/src/api/resource.rs +++ b/oab/src/api/resource.rs @@ -6,9 +6,7 @@ // use actix_web::{delete, get, patch, post, web, Responder}; use proc::{access_create, access_delete, access_read, crud_update}; -use sea_orm::{ - ActiveModelTrait, ColumnTrait, EntityTrait, LoaderTrait, QueryFilter, TransactionTrait, -}; +use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -78,9 +76,9 @@ pub struct UpdateOpt { #[patch("/app/{aid}/resource/{rid}")] #[access_delete("app")] -#[crud_update(resource, AppId = "_id", Name = "_id", des)] +#[crud_update(resource, filter = "AppId, Name", des)] pub async fn update( - id: web::Path<(String, String)>, + id: web::Path<[String; 2]>, data: web::Json, stat: web::Data, ) -> Result { diff --git a/oab/src/api/role.rs b/oab/src/api/role.rs index 981ac29..941501c 100644 --- a/oab/src/api/role.rs +++ b/oab/src/api/role.rs @@ -7,15 +7,11 @@ // use actix_web::{delete, get, patch, post, web, Responder}; use proc::{access_create, access_delete, access_read, access_update, crud_update}; -use sea_orm::{ - ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, LoaderTrait, QueryFilter, - TransactionTrait, -}; +use sea_orm::{ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, QueryFilter}; use serde::{Deserialize, Serialize}; use tracing::info; use crate::{ - libs, models::{self}, AppState, Error, Result, }; @@ -82,10 +78,10 @@ pub struct UpdateOpt { } #[patch("/app/{aid}/role/{rid}")] -#[access_update("app", id = "&id.clone().0")] -#[crud_update(role, AppId = "_id", Id = "_id", des)] +#[access_update("app", id = "&id.clone()[0]")] +#[crud_update(role, filter = "AppId, Id", props = "des")] pub async fn update( - id: web::Path<(String, String)>, + id: web::Path<[String; 2]>, data: web::Json, stat: web::Data, ) -> Result { diff --git a/oab/src/api/token.rs b/oab/src/api/token.rs new file mode 100644 index 0000000..b18ba69 --- /dev/null +++ b/oab/src/api/token.rs @@ -0,0 +1,68 @@ +// +// token.rs +// Copyright (C) 2023 veypi +// 2023-10-13 02:29 +// Distributed under terms of the MIT license. +// + +use actix_web::{get, web, Responder}; +use proc::access_read; +use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; +use std::time::{Duration, Instant}; +use tokio::{self}; + +use crate::{ + models::{self, AUStatus, Token, UserPlugin}, + AppState, Error, Result, +}; + +#[get("/app/{aid}/token/")] +#[access_read("app")] +pub async fn get( + aid: web::Path, + stat: web::Data, + t: web::ReqData, +) -> Result { + let n = aid.into_inner(); + if !n.is_empty() { + let s = models::app_user::Entity::find() + .filter(models::app_user::Column::AppId.eq(&n)) + .filter(models::app_user::Column::UserId.eq(&t.id)) + .one(stat.db()) + .await?; + if s.is_none() { + return Err(Error::NotAuthed); + }; + let s = s.unwrap(); + if s.status == AUStatus::OK as i32 { + let result = sqlx::query_as::<_, models::AccessCore>( + "select access.name, access.rid, access.level from access, user_role, role WHERE user_role.user_id = ? && access.role_id=user_role.role_id && role.id=user_role.role_id && role.app_id = ?", + ) + .bind(&t.id) + .bind(n) + .fetch_all(stat.sqlx()) + .await?; + let u = models::user::Entity::find_by_id(&t.id) + .one(stat.db()) + .await? + .unwrap(); + let str = u.token(result).to_string()?; + // tokio::spawn(async move { + // let mut interval = tokio::time::interval(Duration::from_secs(5)); + // interval.tick().await; + // let start = Instant::now(); + // println!("time:{:?}", start); + // loop { + // interval.tick().await; + // println!("time:{:?}", start.elapsed()); + // } + // }); + + Ok(str) + } else { + Err(Error::NotAuthed) + } + } else { + Err(Error::Missing("id".to_string())) + } +} diff --git a/oab/src/api/upload.rs b/oab/src/api/upload.rs index 1408845..1a4b0dd 100644 --- a/oab/src/api/upload.rs +++ b/oab/src/api/upload.rs @@ -66,47 +66,3 @@ async fn save_files( Ok(web::Json(res)) } - -// #[actix_web::main] -// async fn main() -> std::io::Result<()> { -// HttpServer::new(|| { -// App::new() -// .wrap(middleware::Logger::default()) -// .app_data(TempFileConfig::default().directory("./tmp")) -// .service( -// web::resource("/") -// .route(web::get().to(index)) -// .route(web::post().to(save_files)), -// ) -// }) -// .bind(("127.0.0.1", 8080))? -// .workers(2) -// .run() -// .await -// } - -// /// Example of the old manual way of processing multipart forms. -// #[allow(unused)] -// async fn save_file_manual(mut payload: Multipart) -> Result { -// // iterate over multipart stream -// while let Some(mut field) = payload.try_next().await? { -// // A multipart/form-data stream has to contain `content_disposition` -// let content_disposition = field.content_disposition(); - -// let filename = content_disposition -// .get_filename() -// .map_or_else(|| Uuid::new_v4().to_string(), sanitize_filename::sanitize); -// let filepath = format!("./tmp/{filename}"); - -// // File::create is blocking operation, use threadpool -// let mut f = web::block(|| std::fs::File::create(filepath)).await??; - -// // Field in turn is stream of *Bytes* object -// while let Some(chunk) = field.try_next().await? { -// // filesystem operations are blocking, we have to use threadpool -// f = web::block(move || f.write_all(&chunk).map(|_| f)).await??; -// } -// } - -// Ok(HttpResponse::Ok().into()) -// } diff --git a/oab/src/api/user.rs b/oab/src/api/user.rs index ed8ec1d..3eb61ba 100644 --- a/oab/src/api/user.rs +++ b/oab/src/api/user.rs @@ -14,7 +14,7 @@ use crate::{ }; use actix_web::{delete, get, head, http, patch, post, web, HttpResponse, Responder}; use base64; -use chrono::{DateTime, Local, NaiveDateTime}; +use chrono::Local; use proc::{access_read, access_update, crud_update}; use rand::Rng; use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, TransactionTrait}; @@ -222,7 +222,7 @@ pub struct UpdateOpt { #[patch("/user/{id}")] #[access_update("user")] -#[crud_update(user, Id = "_id", username, icon, nickname, email, phone)] +#[crud_update(user, filter = "Id", props = "username, icon, nickname, email, phone")] pub async fn update( id: web::Path, stat: web::Data, diff --git a/oab/src/models/mod.rs b/oab/src/models/mod.rs index 1df22a8..bb4b386 100644 --- a/oab/src/models/mod.rs +++ b/oab/src/models/mod.rs @@ -9,8 +9,8 @@ mod app_plugin; pub mod entity; mod user_plugin; -use chrono::DateTime; -use sea_orm::EntityTrait; + + use serde::{Deserialize, Serialize}; use tracing::info; diff --git a/oab/src/models/user_plugin.rs b/oab/src/models/user_plugin.rs index 062beff..49f9b20 100644 --- a/oab/src/models/user_plugin.rs +++ b/oab/src/models/user_plugin.rs @@ -61,7 +61,7 @@ impl UserPlugin for super::entity::user::Model { fn token(&self, ac: Vec) -> Token { let default_ico = "/media/".to_string(); let t = Token { - iss: "onedt".to_string(), + iss: "oa".to_string(), aud: "".to_string(), exp: (Utc::now() + Duration::days(4)).timestamp(), iat: Utc::now().timestamp(), diff --git a/oaweb/src/components/app.vue b/oaweb/src/components/app.vue index c34eddd..c6b9983 100644 --- a/oaweb/src/components/app.vue +++ b/oaweb/src/components/app.vue @@ -7,7 +7,7 @@
-
+
{{ core.name }}
{{ }} diff --git a/oaweb/src/components/crud.vue b/oaweb/src/components/crud.vue index 88cc8e1..36f92fc 100644 --- a/oaweb/src/components/crud.vue +++ b/oaweb/src/components/crud.vue @@ -35,6 +35,8 @@ item[k.name] }} + +
diff --git a/oaweb/src/components/editor/index.vue b/oaweb/src/components/editor/index.vue index fbd9e37..e2b5593 100644 --- a/oaweb/src/components/editor/index.vue +++ b/oaweb/src/components/editor/index.vue @@ -5,7 +5,7 @@ * Distributed under terms of the MIT license. -->