From 2dda72bbf377426c4ab74a293f18032d005dfdd4 Mon Sep 17 00:00:00 2001 From: veypi Date: Tue, 10 Oct 2023 01:31:16 +0800 Subject: [PATCH] update crud macro --- oab/migrations/20220720220617_base.sql | 2 +- oab/proc/src/curd.rs | 75 ++++++++++++++++++++-- oab/src/api/access.rs | 17 +++-- oab/src/api/app.rs | 14 +++- oab/src/api/appuser.rs | 24 ++++++- oab/src/api/mod.rs | 4 +- oab/src/api/upload.rs | 34 +++++++--- oab/src/cfg.rs | 6 +- oab/src/main.rs | 5 -- oab/src/models/entity/access.rs | 4 +- oab/src/models/entity/app.rs | 7 +- oab/src/models/entity/app_user.rs | 4 +- oab/src/models/entity/resource.rs | 4 +- oab/src/models/entity/role.rs | 4 +- oab/src/models/entity/user.rs | 4 +- oab/src/models/entity/user_role.rs | 4 +- oab/src/models/mod.rs | 4 +- oaweb/src/boot/api/app.ts | 2 +- oaweb/src/components/FsTree.vue | 2 +- oaweb/src/components/editor/index.vue | 11 ++-- oaweb/src/components/uploader/uploader.vue | 5 +- oaweb/src/libs/oafs.ts | 7 +- oaweb/src/models/index.ts | 5 +- oaweb/src/pages/AppAuth.vue | 15 +++++ oaweb/src/pages/AppHome.vue | 2 +- oaweb/src/pages/AppUser.vue | 56 +++++++++------- oaweb/src/pages/IndexPage.vue | 2 +- oaweb/src/router/routes.ts | 2 +- 28 files changed, 235 insertions(+), 90 deletions(-) create mode 100644 oaweb/src/pages/AppAuth.vue diff --git a/oab/migrations/20220720220617_base.sql b/oab/migrations/20220720220617_base.sql index 6da2708..99bbfde 100644 --- a/oab/migrations/20220720220617_base.sql +++ b/oab/migrations/20220720220617_base.sql @@ -35,7 +35,7 @@ CREATE TABLE IF NOT EXISTS `app` `_key` varchar(32) NOT NULL, `name` varchar(255) NOT NULL, `icon` varchar(255), - `des` MEDIUMTEXT, + `des` TEXT, `user_count` int NOT NULL DEFAULT 0, `hide` tinyint(1) NOT NULL DEFAULT 0, `join_method` int NOT NULL DEFAULT 0, diff --git a/oab/proc/src/curd.rs b/oab/proc/src/curd.rs index 8772b8f..0b3ab69 100644 --- a/oab/proc/src/curd.rs +++ b/oab/proc/src/curd.rs @@ -6,8 +6,8 @@ // use proc_macro2::{Ident, Span}; -use quote::{quote, ToTokens}; -use syn::{AttributeArgs, ItemFn, NestedMeta, ReturnType}; +use quote::{format_ident, quote, ToTokens}; +use syn::{AttributeArgs, ItemFn, NestedMeta, ReturnType, Token}; pub struct CrudWrap { func: ItemFn, @@ -49,24 +49,61 @@ 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]; + ( + vec![quote! {let #_k = #v; }], + vec![quote! { + filter(crate::models::#model_name::Column::#k.eq(#_k)) + }], + ) + } + _ => ( + self.args + .filters + .iter() + .enumerate() + .map(|(idx, [k, v])| { + let _k = format_ident!("_{}", k); + quote! { + let #_k = #v.#idx; + } + }) + .collect(), + self.args + .filters + .iter() + .map(|[k, _]| { + let _k = format_ident!("_{}", k); + quote! { + filter(crate::models::#model_name::Column::#k.eq(#_k)) + } + }) + .collect(), + ), + }; let stream = quote! { #(#fn_attrs)* #func_vis #fn_async fn #fn_name #fn_generics( #fn_args ) -> #fn_output { - let _id = &id.clone(); + let _id = id.clone(); let _data = data.clone(); let _db = &stat.db().clone(); + #(#args_fields)* let f = || async move #func_block; let res = f().await; match res { Err(e) => Err(e), Ok(res) => { - let obj = crate::models::#model_name::Entity::find_by_id(_id).one(_db).await?; + let obj = crate::models::#model_name::Entity::find().#(#filter_fields).*.one(_db).await?; let mut obj: crate::models::#model_name::ActiveModel = match obj { Some(o) => o.into(), - None => return Err(Error::NotFound(_id.to_owned())), + None => return Err(Error::NotFound("".into())), }; #(#builder_fields)* let obj = obj.update(_db).await?; @@ -83,12 +120,14 @@ impl ToTokens for CrudWrap { struct Crud { model: syn::Ident, attrs: Vec, + filters: Vec<[syn::Ident; 2]>, } impl Crud { fn new(args: AttributeArgs) -> syn::Result { let mut model: Option = None; let mut attrs: Vec = Vec::new(); + let mut filters: Vec<[syn::Ident; 2]> = Vec::new(); for arg in args { match arg { // NestedMeta::Lit(syn::Lit::Str(lit)) => { @@ -103,14 +142,36 @@ impl Crud { } None => {} }, + NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { + path, + 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'", + )); + } + }; + } _ => { return Err(syn::Error::new_spanned(arg, "Unknown attribute.")); } } } + println!("|||||||||||____________________"); match model { - Some(model) => Ok(Self { model, attrs }), + Some(model) => Ok(Self { + model, + attrs, + filters, + }), None => Err(syn::Error::new( Span::call_site(), "The #[crud(..)] macro requires one `model` name", diff --git a/oab/src/api/access.rs b/oab/src/api/access.rs index f271318..1cbd951 100644 --- a/oab/src/api/access.rs +++ b/oab/src/api/access.rs @@ -8,13 +8,12 @@ // use actix_web::{web, Responder}; use proc::crud_update; -use sea_orm::{ActiveModelTrait, EntityTrait}; +use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, TransactionTrait}; use serde::{Deserialize, Serialize}; use crate::{models::app, AppState, Error, Result}; -// #[derive(Debug, Deserialize, Serialize)] -#[derive(Clone)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct UpdateOpt { pub name: Option, pub icon: Option, @@ -25,7 +24,17 @@ pub struct UpdateOpt { pub status: Option, } impl UpdateOpt { - #[crud_update(app, name, icon, des, join_method, role_id, redirect, status)] + // #[crud_update( + // app, + // id = "Id", + // name, + // icon, + // des, + // join_method, + // role_id, + // redirect, + // status + // )] pub async fn update( id: web::Path, stat: web::Data, diff --git a/oab/src/api/app.rs b/oab/src/api/app.rs index 4daa221..873346d 100644 --- a/oab/src/api/app.rs +++ b/oab/src/api/app.rs @@ -7,7 +7,7 @@ // 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, EntityTrait, TransactionTrait}; +use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, TransactionTrait}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -93,7 +93,17 @@ pub struct UpdateOpt { #[patch("/app/{id}")] #[access_update("app")] -#[crud_update(app, name, icon, des, join_method, role_id, redirect, status)] +#[crud_update( + app, + Id = "_id", + name, + icon, + des, + join_method, + role_id, + redirect, + status +)] pub async fn update( id: web::Path, stat: web::Data, diff --git a/oab/src/api/appuser.rs b/oab/src/api/appuser.rs index 220952c..23cd22b 100644 --- a/oab/src/api/appuser.rs +++ b/oab/src/api/appuser.rs @@ -5,9 +5,11 @@ // Distributed under terms of the MIT license. // -use actix_web::{get, post, web, Responder}; -use proc::access_read; -use sea_orm::{ColumnTrait, EntityTrait, LoaderTrait, QueryFilter, TransactionTrait}; +use actix_web::{get, patch, post, web, Responder}; +use proc::{access_delete, access_read, crud_update}; +use sea_orm::{ + ActiveModelTrait, ColumnTrait, EntityTrait, LoaderTrait, QueryFilter, TransactionTrait, +}; use serde::{Deserialize, Serialize}; use crate::{ @@ -92,3 +94,19 @@ pub async fn add( db.commit().await?; Ok(web::Json(res)) } + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct UpdateOpt { + pub status: Option, +} + +#[patch("/app/{aid}/user/{uid}")] +#[access_delete("app")] +#[crud_update(app_user, AppId = "_id", UserId = "_id", status)] +pub async fn update( + id: web::Path<(String, String)>, + data: web::Json, + stat: web::Data, +) -> Result { + Ok("") +} diff --git a/oab/src/api/mod.rs b/oab/src/api/mod.rs index 5bd3fdc..8d742a4 100644 --- a/oab/src/api/mod.rs +++ b/oab/src/api/mod.rs @@ -29,5 +29,7 @@ pub fn routes(cfg: &mut web::ServiceConfig) { .service(app::del); // cfg.route("/acc", web::get().to(access::UpdateOpt::update)); - cfg.service(appuser::get).service(appuser::add); + cfg.service(appuser::get) + .service(appuser::add) + .service(appuser::update); } diff --git a/oab/src/api/upload.rs b/oab/src/api/upload.rs index 4af8421..1408845 100644 --- a/oab/src/api/upload.rs +++ b/oab/src/api/upload.rs @@ -6,6 +6,8 @@ // // +use std::{fs, path::Path}; + use actix_multipart::form::{tempfile::TempFile, MultipartForm}; use actix_web::{post, web, Responder}; @@ -19,29 +21,45 @@ struct UploadForm { files: Vec, } -#[post("/upload/")] +#[post("/upload/{dir:.*}")] #[access_read("app")] async fn save_files( MultipartForm(form): MultipartForm, t: web::ReqData, + dir: web::Path, stat: web::Data, ) -> Result { - let l = form.files.len(); let t = t.into_inner(); + let mut dir = dir.into_inner(); + if dir.is_empty() { + dir = "tmp".to_string(); + } let mut res: Vec = Vec::new(); - info!("!|||||||||||_{}_|", l); form.files.into_iter().for_each(|v| { let fname = v.file_name.unwrap_or("unknown".to_string()); - let path = format!("{}tmp/{}.{}", stat.media_path, t.id, fname); - info!("saving to {path}"); - match v.file.persist(path) { + let root = Path::new(&stat.media_path).join(dir.clone()); + if !root.exists() { + match fs::create_dir_all(root.clone()) { + Ok(_) => {} + Err(e) => { + warn!("{}", e); + } + } + } + let temp_file = format!( + "{}/{}.{}", + root.to_str().unwrap_or(&stat.media_path), + t.id, + fname + ); + info!("saving to {temp_file}"); + match v.file.persist(temp_file) { Ok(p) => { info!("{:#?}", p); - res.push(format!("/media/tmp/{}.{}", t.id, fname)) + res.push(format!("/media/{}/{}.{}", dir, t.id, fname)) } Err(e) => { warn!("{}", e); - // return Err(Error::InternalServerError); } }; }); diff --git a/oab/src/cfg.rs b/oab/src/cfg.rs index 83808ec..6c7c4c2 100644 --- a/oab/src/cfg.rs +++ b/oab/src/cfg.rs @@ -129,8 +129,8 @@ impl AppState { log_dir: None, log_temp_size: None, log_pack_compress: None, - media_path: "/Users/veypi/test/media/".to_string(), - fs_root: "/Users/veypi/test/media/".to_string(), + media_path: "/Users/veypi/test/media".to_string(), + fs_root: "/Users/veypi/test/media".to_string(), log_level: None, jwt_secret: None, _sqlx: None, @@ -197,7 +197,7 @@ pub fn init_log() { tracing_subscriber::fmt() .with_line_number(true) .with_timer(FormatTime {}) - .with_max_level(Level::DEBUG) + .with_max_level(Level::TRACE) .with_target(false) .with_file(true) .init(); diff --git a/oab/src/main.rs b/oab/src/main.rs index 969afc5..d72502d 100644 --- a/oab/src/main.rs +++ b/oab/src/main.rs @@ -90,15 +90,10 @@ async fn web(data: AppState) -> Result<()> { .unwrap(), HeaderValue::from_str(&origin).unwrap(), ); - headers.insert( - HeaderName::try_from("123").unwrap(), - HeaderValue::from_str("asd").unwrap(), - ); Ok(expr) } Err(e) => Err(e), }; - println!("Hi from response"); res }) }) diff --git a/oab/src/models/entity/access.rs b/oab/src/models/entity/access.rs index c8e7a60..1eea016 100644 --- a/oab/src/models/entity/access.rs +++ b/oab/src/models/entity/access.rs @@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key)] pub id: i32, - pub created: Option, - pub updated: Option, + pub created: DateTime, + pub updated: DateTime, pub app_id: String, pub name: String, pub role_id: Option, diff --git a/oab/src/models/entity/app.rs b/oab/src/models/entity/app.rs index 3dfc281..ae5b004 100644 --- a/oab/src/models/entity/app.rs +++ b/oab/src/models/entity/app.rs @@ -10,11 +10,14 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, - pub created: Option, - pub updated: Option, + pub created: DateTime, + pub updated: DateTime, + #[sea_orm(column_name = "_key")] + #[serde(skip)] pub key: String, pub name: String, pub icon: Option, + #[sea_orm(column_type = "Text", nullable)] pub des: Option, pub user_count: i32, pub hide: i8, diff --git a/oab/src/models/entity/app_user.rs b/oab/src/models/entity/app_user.rs index dfcc0af..e7cf20c 100644 --- a/oab/src/models/entity/app_user.rs +++ b/oab/src/models/entity/app_user.rs @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; )] #[sea_orm(table_name = "app_user")] pub struct Model { - pub created: Option, - pub updated: Option, + pub created: DateTime, + pub updated: DateTime, #[sea_orm(primary_key, auto_increment = false)] pub app_id: String, #[sea_orm(primary_key, auto_increment = false)] diff --git a/oab/src/models/entity/resource.rs b/oab/src/models/entity/resource.rs index bc8e6dd..3e417ff 100644 --- a/oab/src/models/entity/resource.rs +++ b/oab/src/models/entity/resource.rs @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; )] #[sea_orm(table_name = "resource")] pub struct Model { - pub created: Option, - pub updated: Option, + pub created: DateTime, + pub updated: DateTime, #[sea_orm(primary_key, auto_increment = false)] pub app_id: String, #[sea_orm(primary_key, auto_increment = false)] diff --git a/oab/src/models/entity/role.rs b/oab/src/models/entity/role.rs index d451509..df42417 100644 --- a/oab/src/models/entity/role.rs +++ b/oab/src/models/entity/role.rs @@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, - pub created: Option, - pub updated: Option, + pub created: DateTime, + pub updated: DateTime, pub app_id: String, pub name: String, pub des: Option, diff --git a/oab/src/models/entity/user.rs b/oab/src/models/entity/user.rs index f31248c..7205d23 100644 --- a/oab/src/models/entity/user.rs +++ b/oab/src/models/entity/user.rs @@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize}; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: String, - pub created: Option, - pub updated: Option, + pub created: DateTime, + pub updated: DateTime, #[sea_orm(unique)] pub username: String, pub nickname: Option, diff --git a/oab/src/models/entity/user_role.rs b/oab/src/models/entity/user_role.rs index 1f71482..b2d3648 100644 --- a/oab/src/models/entity/user_role.rs +++ b/oab/src/models/entity/user_role.rs @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; )] #[sea_orm(table_name = "user_role")] pub struct Model { - pub created: Option, - pub updated: Option, + pub created: DateTime, + pub updated: DateTime, #[sea_orm(primary_key, auto_increment = false)] pub user_id: String, #[sea_orm(primary_key, auto_increment = false)] diff --git a/oab/src/models/mod.rs b/oab/src/models/mod.rs index 746c2a5..32101b7 100644 --- a/oab/src/models/mod.rs +++ b/oab/src/models/mod.rs @@ -27,8 +27,8 @@ pub async fn init(data: AppState) { #[derive(Debug, Deserialize, Serialize)] pub struct UnionAppUser { - pub created: Option, - pub updated: Option, + pub created: chrono::NaiveDateTime, + pub updated: chrono::NaiveDateTime, pub app: Option, pub user: Option, pub app_id: String, diff --git a/oaweb/src/boot/api/app.ts b/oaweb/src/boot/api/app.ts index e190445..1f7e77a 100644 --- a/oaweb/src/boot/api/app.ts +++ b/oaweb/src/boot/api/app.ts @@ -40,7 +40,7 @@ export default { add(uid: string) { return ajax.post(this.local + uid) }, - update(uid: number, status: string) { + update(uid: string, status: number) { return ajax.patch(this.local + uid, { status }) }, } diff --git a/oaweb/src/components/FsTree.vue b/oaweb/src/components/FsTree.vue index 5d4c329..886bb7e 100644 --- a/oaweb/src/components/FsTree.vue +++ b/oaweb/src/components/FsTree.vue @@ -12,7 +12,7 @@ '']" style="font-size: 24px;" :name="root.type === 'directory' ? 'v-caret-right' : 'v-file'">
- {{ root.filename }} + {{ root.basename || '/' }}
{{ new Date(root.lastmod).toLocaleString() }}
diff --git a/oaweb/src/components/editor/index.vue b/oaweb/src/components/editor/index.vue index acc353a..6759688 100644 --- a/oaweb/src/components/editor/index.vue +++ b/oaweb/src/components/editor/index.vue @@ -64,11 +64,9 @@ const fileUpload = (f: File, cb: (url: string, params: any) => void) => { * @param params.width 设置宽度,可以是像素、也可以是百分比(图片、视频场景下生效) * @param params.height 设置高度,可以是像素、也可以是百分比(图片、视频场景下生效) */ - console.log('uploading file' + f.name) - let url = '/abc/' - oafs.appdav().upload(url, oafs.rename(f.name), f).then((e: any) => { - cb(e, { - name: f.name, isBorder: false, isShadow: false, isRadius: false, width: '80%', height: '80%', + oafs.upload([f], props.static_dir).then((e: any) => { + cb(e[0], { + name: f.name, isBorder: false, isShadow: false, isRadius: false, width: '', height: '', }) }) } @@ -77,7 +75,8 @@ const init = () => { value: props.content, id: props.eid, // isPreviewOnly: props.preview, - callback: {}, + callback: { + }, fileUpload: fileUpload, } as CherryOptions; config.callback.afterInit = () => { diff --git a/oaweb/src/components/uploader/uploader.vue b/oaweb/src/components/uploader/uploader.vue index 8899f11..d907c51 100644 --- a/oaweb/src/components/uploader/uploader.vue +++ b/oaweb/src/components/uploader/uploader.vue @@ -16,7 +16,8 @@ let emits = defineEmits<{ }>() let props = withDefaults(defineProps<{ multiple?: boolean, - renames?: string + renames?: string, + dir?: string, }>(), { multiple: false, renames: '' @@ -29,7 +30,7 @@ function click() { const upload = (evt: Event) => { evt.preventDefault() let f = (evt.target as HTMLInputElement).files as FileList - oafs.upload(f, props.renames?.split(/[, ]+/)).then((e: any) => { + oafs.upload(f, props.dir, props.renames?.split(/[, ]+/)).then((e: any) => { console.log(e) emits('success', props.multiple ? e : e[0]) }) diff --git a/oaweb/src/libs/oafs.ts b/oaweb/src/libs/oafs.ts index 94a4ae9..e473ebc 100644 --- a/oaweb/src/libs/oafs.ts +++ b/oaweb/src/libs/oafs.ts @@ -51,14 +51,15 @@ const get = (url: string): Promise => { return fetch(url, { headers: { auth_token: util.getToken() } }).then((response) => response.text()) } -const upload = (f: FileList | File[], renames?: string[]) => { +// rename 可以保持url不变 +const upload = (f: FileList | File[], dir?: string, renames?: string[]) => { return new Promise((resolve, reject) => { var data = new FormData(); for (let i = 0; i < f.length; i++) { - let nf = renames ? new File([f[i]], rename(f[i].name, renames[i]), { type: f[i].type }) : f[i] + let nf = new File([f[i]], rename(f[i].name, renames && renames[i] ? renames[i] : undefined), { type: f[i].type }) data.append('files', nf, nf.name) } - axios.post("/api/upload/", data, { + axios.post("/api/upload/" + (dir || ''), data, { headers: { "Content-Type": 'multipart/form-data', 'auth_token': cfg.token, diff --git a/oaweb/src/models/index.ts b/oaweb/src/models/index.ts index a3c4ebe..96aeb88 100644 --- a/oaweb/src/models/index.ts +++ b/oaweb/src/models/index.ts @@ -85,8 +85,8 @@ export enum AUStatus { } export interface modelsAppUser { - app?: modelsApp, - user?: modelsUser, + app: modelsApp, + user: modelsUser, app_id: string user_id: string status: AUStatus @@ -105,6 +105,7 @@ export interface modelsUser { status: number used: number space: number + au: AUStatus // Index 前端缓存 // Index?: number diff --git a/oaweb/src/pages/AppAuth.vue b/oaweb/src/pages/AppAuth.vue new file mode 100644 index 0000000..a8c48da --- /dev/null +++ b/oaweb/src/pages/AppAuth.vue @@ -0,0 +1,15 @@ + + + + + + + diff --git a/oaweb/src/pages/AppHome.vue b/oaweb/src/pages/AppHome.vue index 13e98ec..b157b2e 100644 --- a/oaweb/src/pages/AppHome.vue +++ b/oaweb/src/pages/AppHome.vue @@ -44,7 +44,7 @@ const sync = () => { const save = (des: string) => { let a = new File([des], app.value.name + '.md'); - oafs.upload([a]).then(url => { + oafs.upload([a], app.value.id).then(url => { api.app.update(app.value.id, { des: url[0] }).then(e => { edit_mode.value = false app.value.des = url[0] diff --git a/oaweb/src/pages/AppUser.vue b/oaweb/src/pages/AppUser.vue index a20eaab..a78d9c2 100644 --- a/oaweb/src/pages/AppUser.vue +++ b/oaweb/src/pages/AppUser.vue @@ -10,17 +10,17 @@ diff --git a/oaweb/src/pages/IndexPage.vue b/oaweb/src/pages/IndexPage.vue index c4b91fc..674ac98 100644 --- a/oaweb/src/pages/IndexPage.vue +++ b/oaweb/src/pages/IndexPage.vue @@ -32,7 +32,7 @@
- + diff --git a/oaweb/src/router/routes.ts b/oaweb/src/router/routes.ts index f787b04..60e454c 100644 --- a/oaweb/src/router/routes.ts +++ b/oaweb/src/router/routes.ts @@ -45,7 +45,7 @@ const routes: RouteRecordRaw[] = [ children: [ loadcomponents('home', 'app.home', 'AppHome'), loadcomponents('user', 'app.user', 'AppUser'), - loadcomponents('user', 'app.user', 'AppUser'), + loadcomponents('auth', 'app.auth', 'AppAuth'), loadcomponents('settings', 'app.settings', 'IndexPage'), ] }