From 198fec5c1957150f9262d8b3802482f6a9a94c77 Mon Sep 17 00:00:00 2001 From: git Date: Thu, 21 Jul 2022 00:40:09 +0800 Subject: [PATCH] rbatis db --- oab/Cargo.lock | 173 +++++++++++++++++++++++++++++++++++-- oab/Cargo.toml | 10 ++- oab/Makefile | 11 +++ oab/sql/table.sql | 71 ++++++++------- oab/src/api/access.rs | 6 ++ oab/src/api/app.rs | 6 ++ oab/src/api/mod.rs | 19 ++-- oab/src/api/resource.rs | 6 ++ oab/src/api/role.rs | 6 ++ oab/src/api/user.rs | 107 +++++++++++++++++++++++ oab/src/cfg.rs | 38 +++++--- oab/src/lib.rs | 3 +- oab/src/libs/mod.rs | 70 +++++++++++++++ oab/src/main.rs | 22 ++++- oab/src/models/app.rs | 20 +++-- oab/src/models/mod.rs | 2 +- oab/src/models/user.rs | 130 +++++++++++++++++++++++++++- oab/src/result.rs | 62 +++++++++++-- oaf/src/api/ajax.ts | 28 +++--- oaf/src/api/interface.ts | 39 +-------- oaf/src/api/user.ts | 10 +-- oaf/src/views/register.vue | 139 +++++++++++++++-------------- 22 files changed, 773 insertions(+), 205 deletions(-) create mode 100644 oab/Makefile create mode 100644 oab/src/api/access.rs create mode 100644 oab/src/api/app.rs create mode 100644 oab/src/api/resource.rs create mode 100644 oab/src/api/role.rs create mode 100644 oab/src/api/user.rs create mode 100644 oab/src/libs/mod.rs diff --git a/oab/Cargo.lock b/oab/Cargo.lock index 147bca7..81e61e7 100644 --- a/oab/Cargo.lock +++ b/oab/Cargo.lock @@ -187,6 +187,41 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array 0.14.5", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.7.6" @@ -298,7 +333,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1e50562e37200edf7c6c43e54a08e64a5553bfb59d9c297d5572512aa517256" dependencies = [ - "num-bigint", + "num-bigint 0.3.3", "num-integer", "num-traits", "serde", @@ -322,7 +357,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding", + "block-padding 0.1.5", "byte-tools", "byteorder", "generic-array 0.12.4", @@ -346,6 +381,15 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "block-padding" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a90ec2df9600c28a01c56c4784c9207a96d2451833aeceb8cc97e4c9548bb78" +dependencies = [ + "generic-array 0.14.5", +] + [[package]] name = "brotli" version = "3.3.4" @@ -429,6 +473,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.5", +] + [[package]] name = "clap" version = "3.1.18" @@ -570,6 +623,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + [[package]] name = "dashmap" version = "4.0.2" @@ -821,6 +883,16 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug 0.3.0", + "polyval", +] + [[package]] name = "h2" version = "0.3.13" @@ -1007,6 +1079,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa4b4af834c6cfd35d8763d359661b90f2e45d8f750a0849156c7f4671af09c" +dependencies = [ + "base64", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "language-tags" version = "0.3.2" @@ -1149,6 +1235,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.8.1" @@ -1221,16 +1318,24 @@ name = "oab" version = "0.1.0" dependencies = [ "actix-web", + "aes-gcm", + "base64", + "block-padding 0.3.2", + "chrono", "clap", + "futures-util", + "generic-array 0.14.5", "include_dir", + "jsonwebtoken", "lazy_static", + "rand", "rbatis", "rbson", "serde", + "serde-big-array", "serde_json", "serde_yaml", "thiserror", - "time 0.3.9", "tokio", "tracing", "tracing-subscriber", @@ -1248,6 +1353,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "os_str_bytes" version = "6.1.0" @@ -1308,6 +1419,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +[[package]] +name = "pem" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947" +dependencies = [ + "base64", +] + [[package]] name = "pem-rfc7468" version = "0.3.1" @@ -1400,6 +1520,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1742,6 +1874,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3323f09a748af288c3dc2474ea6803ee81f118321775bffa3ac8f7e65c5e90e7" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.6" @@ -1807,7 +1948,7 @@ dependencies = [ "block-buffer 0.7.3", "digest 0.8.1", "fake-simd", - "opaque-debug", + "opaque-debug 0.2.3", ] [[package]] @@ -1876,6 +2017,18 @@ dependencies = [ "libc", ] +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint 0.4.3", + "num-traits", + "thiserror", + "time 0.3.9", +] + [[package]] name = "slab" version = "0.4.6" @@ -1956,7 +2109,7 @@ dependencies = [ "libc", "log", "memchr", - "num-bigint", + "num-bigint 0.3.3", "once_cell", "paste", "percent-encoding", @@ -2373,6 +2526,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.5", + "subtle", +] + [[package]] name = "untrusted" version = "0.7.1" diff --git a/oab/Cargo.toml b/oab/Cargo.toml index 647483b..8cf6cfc 100644 --- a/oab/Cargo.toml +++ b/oab/Cargo.toml @@ -12,8 +12,9 @@ serde = { version = "1", features = ["derive"] } serde_json = "*" serde_yaml = "*" clap = { version = "3", features = ["derive"] } -time = { version = "0.3", features = ["formatting", "macros"] } +chrono = "0.4" tokio = { version = "1", features = ["full"] } +futures-util = "*" tracing = "*" tracing-subscriber = "*" thiserror = "1.0" @@ -21,4 +22,11 @@ thiserror = "1.0" rbson = "2" rbatis = { version = "*", default-features = false, features = ["runtime-tokio-rustls","mysql","debug_mode"] } actix-web = "4" +jsonwebtoken = "8" +aes-gcm="0.9" +rand = "0.8.5" +block-padding = "0.3.2" +generic-array = "0.14.5" +serde-big-array = "0.4.1" +base64 = "0.13.0" diff --git a/oab/Makefile b/oab/Makefile new file mode 100644 index 0000000..3a26801 --- /dev/null +++ b/oab/Makefile @@ -0,0 +1,11 @@ +# +# Makefile +# Copyright (C) 2022 veypi +# 2022-07-16 23:43 +# Distributed under terms of the Apache license. +# + +run: + @cargo run +init: + @cargo run -- init diff --git a/oab/sql/table.sql b/oab/sql/table.sql index 9a6ea2f..c95c9ac 100644 --- a/oab/sql/table.sql +++ b/oab/sql/table.sql @@ -4,22 +4,27 @@ * * Distributed under terms of the Apache license. */ +DROP DATABASE test; +CREATE DATABASE test CHARSET=utf8; +USE test; CREATE TABLE IF NOT EXISTS `user` ( `id` varchar(32) NOT NULL DEFAULT '' COMMENT 'User UUID', - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `delete_flag` int(1) NOT NULL, + `created` datetime DEFAULT CURRENT_TIMESTAMP, + `updated` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `delete_flag` tinyint(1) NOT NULL, `username` varchar(255) NOT NULL UNIQUE, `nickname` varchar(255), `email` varchar(255) UNIQUE, `phone` varchar(255) UNIQUE, `icon` varchar(255), + `real_code` varchar(32), + `check_code` binary(48), `status` int NOT NULL COMMENT '状态(0:ok,1:disabled)', - `used` int, + `used` int NOT NULL DEFAULT 0, `space` int DEFAULT 300, PRIMARY KEY (`id`) USING BTREE @@ -28,16 +33,17 @@ CREATE TABLE IF NOT EXISTS `user` CREATE TABLE IF NOT EXISTS `app` ( `id` varchar(32) NOT NULL, - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `delete_flag` int(1) NOT NULL, + `created` datetime DEFAULT CURRENT_TIMESTAMP, + `updated` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `delete_flag` tinyint(1) NOT NULL, + `key` varchar(32) NOT NULL, `name` varchar(255) NOT NULL, `icon` varchar(255), `des` varchar(255), - `user_count` int NOT NULL, - `hide` int(1) NOT NULL, - `regist` int(1) NOT NULL, + `user_count` int NOT NULL DEFAULT 0, + `hide` tinyint(1) NOT NULL DEFAULT 0, + `join_method` varchar(32) NOT NULL DEFAULT 'auto', `role_id` varchar(32), `redirect` varchar(255), @@ -48,8 +54,8 @@ CREATE TABLE IF NOT EXISTS `app` CREATE TABLE IF NOT EXISTS `app_user` ( - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `created` datetime DEFAULT CURRENT_TIMESTAMP, + `updated` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `app_id` varchar(32) NOT NULL, `user_id` varchar(32) NOT NULL, @@ -65,9 +71,9 @@ CREATE TABLE IF NOT EXISTS `app_user` CREATE TABLE IF NOT EXISTS `role` ( `id` varchar(32) NOT NULL, - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `delete_flag` int(1) NOT NULL, + `created` datetime DEFAULT CURRENT_TIMESTAMP, + `updated` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `delete_flag` tinyint(1) NOT NULL, `app_id` varchar(32) NOT NULL, `name` varchar(255) NOT NULL, @@ -80,8 +86,8 @@ CREATE TABLE IF NOT EXISTS `role` CREATE TABLE IF NOT EXISTS `user_role` ( - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `created` datetime DEFAULT CURRENT_TIMESTAMP, + `updated` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `user_id` varchar(32) NOT NULL, `role_id` varchar(32) NOT NULL, @@ -94,9 +100,9 @@ CREATE TABLE IF NOT EXISTS `user_role` CREATE TABLE IF NOT EXISTS `resource` ( - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `delete_flag` int(1) NOT NULL, + `created` datetime DEFAULT CURRENT_TIMESTAMP, + `updated` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `delete_flag` tinyint(1) NOT NULL, `app_id` varchar(32) NOT NULL, `name` varchar(32) NOT NULL, @@ -110,9 +116,9 @@ CREATE TABLE IF NOT EXISTS `resource` CREATE TABLE IF NOT EXISTS `access` ( - `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `delete_flag` int(1) NOT NULL, + `created` datetime DEFAULT CURRENT_TIMESTAMP, + `updated` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `delete_flag` tinyint(1) NOT NULL, `app_id` varchar(32) NOT NULL, `name` varchar(32) NOT NULL, @@ -128,20 +134,19 @@ CREATE TABLE IF NOT EXISTS `access` FOREIGN KEY (`app_id`,`name`) REFERENCES `resource`(`app_id`,`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -ALTER TABLE `app` -ADD FOREIGN KEY (`role_id`) REFERENCES `role`(`id`); -INSERT INTO `user` (`id`, `username`) -VALUES ('user0', 'name'); -INSERT INTO `app` (`id`, `name`) -VALUES ('app0', 'a0'); +INSERT INTO `app` (`id`, `name`, `key`, `role_id`) +VALUES ('FR9P5t8debxc11aFF', 'oa', 'AMpjwQHwVjGsb1WC4WG6', '1lytMwQL4uiNd0vsc'); INSERT INTO `resource` (`app_id`, `name`) -VALUES ('app0', 'sound'); +VALUES ('FR9P5t8debxc11aFF', 'app'); -INSERT INTO `role` (`id`, `app_id`) -VALUES ('role0', 'app0'); +INSERT INTO `role` (`id`, `app_id`, `name`) +VALUES ('1lytMwQL4uiNd0vsc', 'FR9P5t8debxc11aFF', 'admin'); INSERT INTO `access` (`app_id`, `name`, `role_id`, `user_id`) -VALUES ('app0', 'sound', NULL, 'user0'); +VALUES ('FR9P5t8debxc11aFF', 'app', '1lytMwQL4uiNd0vsc', null); + +ALTER TABLE `app` +ADD FOREIGN KEY (`role_id`) REFERENCES `role`(`id`); diff --git a/oab/src/api/access.rs b/oab/src/api/access.rs new file mode 100644 index 0000000..ffedd8f --- /dev/null +++ b/oab/src/api/access.rs @@ -0,0 +1,6 @@ +// +// access.rs +// Copyright (C) 2022 veypi +// 2022-07-09 03:10 +// Distributed under terms of the Apache license. +// diff --git a/oab/src/api/app.rs b/oab/src/api/app.rs new file mode 100644 index 0000000..565e598 --- /dev/null +++ b/oab/src/api/app.rs @@ -0,0 +1,6 @@ +// +// app.rs +// Copyright (C) 2022 veypi +// 2022-07-09 03:10 +// Distributed under terms of the Apache license. +// diff --git a/oab/src/api/mod.rs b/oab/src/api/mod.rs index def96df..99d0310 100644 --- a/oab/src/api/mod.rs +++ b/oab/src/api/mod.rs @@ -5,8 +5,14 @@ // Distributed under terms of the Apache license. // // + +mod access; +mod app; +mod resource; +mod role; +mod user; use crate::{Error, Result}; -use actix_web::{get, web, Responder}; +use actix_web::{get, web}; #[get("/hello/{name}")] async fn greet(name: web::Path) -> Result { @@ -18,12 +24,11 @@ async fn greet(name: web::Path) -> Result { } } -#[get("/topic/derive")] -async fn hello() -> impl Responder { - "Hello World!" -} - pub fn routes(cfg: &mut web::ServiceConfig) { + cfg.service(user::get) + .service(user::list) + .service(user::register) + .service(user::login) + .service(user::delete); cfg.service(greet); - cfg.service(hello); } diff --git a/oab/src/api/resource.rs b/oab/src/api/resource.rs new file mode 100644 index 0000000..7dcce20 --- /dev/null +++ b/oab/src/api/resource.rs @@ -0,0 +1,6 @@ +// +// resource.rs +// Copyright (C) 2022 veypi +// 2022-07-09 03:09 +// Distributed under terms of the Apache license. +// diff --git a/oab/src/api/role.rs b/oab/src/api/role.rs new file mode 100644 index 0000000..edb1fad --- /dev/null +++ b/oab/src/api/role.rs @@ -0,0 +1,6 @@ +// +// role.rs +// Copyright (C) 2022 veypi +// 2022-07-09 03:10 +// Distributed under terms of the Apache license. +// diff --git a/oab/src/api/user.rs b/oab/src/api/user.rs new file mode 100644 index 0000000..e84f88d --- /dev/null +++ b/oab/src/api/user.rs @@ -0,0 +1,107 @@ +// +// user.rs +// Copyright (C) 2022 veypi +// 2022-07-09 03:10 +// Distributed under terms of the Apache license. +// + +use crate::{dbtx, models, Error, Result, CONFIG, DB}; +use actix_web::{delete, get, head, post, web, Responder}; +use base64; +use rbatis::crud::CRUD; +use serde::{Deserialize, Serialize}; +use tracing::info; + +#[get("/user/{id}")] +pub async fn get(id: web::Path) -> Result { + let n = id.into_inner(); + if !n.is_empty() { + let u: Option = DB.fetch_by_column("id", &n).await?; + match u { + Some(u) => { + info!("{:#?}", u.token()); + return Ok(u); + } + None => Err(Error::NotFound(format!("user {}", n))), + } + } else { + Err(Error::Missing("id".to_string())) + } +} + +#[get("/user/")] +pub async fn list() -> impl Responder { + let result: Vec = DB.fetch_list().await.unwrap(); + web::Json(result) +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct LoginOpt { + typ: Option, + password: String, +} + +#[head("/user/{id}")] +pub async fn login(q: web::Query, id: web::Path) -> impl Responder { + let id = id.into_inner(); + let q = q.into_inner(); + info!("{} try to login{:#?}", id, q); + let mut w = DB.new_wrapper(); + match q.typ { + _ => w = w.eq("username", id), + } + let u: Option = DB.fetch_by_wrapper(w).await.unwrap(); + info!("{:#?}", u); + + "" +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct RegisterOpt { + username: String, + password: String, +} + +#[post("/user/")] +pub async fn register(q: web::Json) -> Result { + let q = q.into_inner(); + // let mut tx = dbtx().await; + println!("{:#?}", q); + let u: Option = DB.fetch_by_column("username", &q.username).await.unwrap(); + let u: models::User = match u { + Some(_) => return Err(Error::ArgDuplicated(format!("username: {}", q.username))), + None => { + let mut u = models::User::default(); + u.username = q.username.clone(); + let p = match base64::decode(q.password.as_bytes()) { + Err(_) => return Err(Error::ArgInvalid("password".to_string())), + Ok(p) => p, + }; + let p = match std::str::from_utf8(&p) { + Ok(p) => p, + Err(_) => return Err(Error::ArgInvalid("password".to_string())), + }; + info!("{}", p); + u.update_pass(&p)?; + u + } + }; + let oa: Option = DB.fetch_by_column("id", CONFIG.uuid.clone()).await?; + let oa = oa.unwrap(); + let mut au = models::AppUser::new(); + au.app_id = oa.id; + au.user_id = u.id.clone(); + match oa.join_method { + models::app::AppJoin::Disabled => return Err(Error::AppDisabledRegister), + models::app::AppJoin::Auto => au.status = models::app::AUStatus::OK, + models::app::AppJoin::Applying => au.status = models::app::AUStatus::Applying, + } + DB.save(&u, &[]).await?; + DB.save(&au, &[]).await?; + Ok("ok".to_string()) +} + +#[delete("/user/")] +pub async fn delete() -> impl Responder { + "" +} diff --git a/oab/src/cfg.rs b/oab/src/cfg.rs index f04d789..7f0dda3 100644 --- a/oab/src/cfg.rs +++ b/oab/src/cfg.rs @@ -16,10 +16,11 @@ use std::{ use clap::{Args, Parser, Subcommand}; use lazy_static::lazy_static; -use rbatis::rbatis::Rbatis; +use rbatis::{logic_delete::RbatisLogicDeletePlugin, rbatis::Rbatis}; +use tracing::log::warn; lazy_static! { // Rbatis是线程、协程安全的,运行时的方法是Send+Sync,无需担心线程竞争 - pub static ref DB:Rbatis=Rbatis::new(); + pub static ref DB:Rbatis= new_db(); } lazy_static! { @@ -61,6 +62,8 @@ impl AppCli { #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] pub struct ApplicationConfig { + pub uuid: String, + pub key: String, pub debug: bool, pub server_url: String, pub db_url: String, @@ -98,6 +101,8 @@ impl ApplicationConfig { } pub fn defaut() -> Self { Self { + uuid: "FR9P5t8debxc11aFF".to_string(), + key: "AMpjwQHwVjGsb1WC4WG6".to_string(), debug: true, server_url: "127.0.0.1:4001".to_string(), db_url: "127.0.0.1:3306".to_string(), @@ -124,18 +129,29 @@ impl ApplicationConfig { } } +fn new_db() -> rbatis::rbatis::Rbatis { + let mut rb = rbatis::rbatis::Rbatis::new(); + rb.logic_plugin = Some(Box::new(RbatisLogicDeletePlugin::new("delete_flag"))); + rb +} + +pub async fn dbtx() -> rbatis::executor::RBatisTxExecutorGuard<'static> { + DB.acquire_begin() + .await + .unwrap() + .defer_async(|mut tx1| async move { + if !tx1.is_done() { + _ = tx1.rollback().await; + warn!("db rollback!") + } + }) +} + struct FormatTime; impl tracing_subscriber::fmt::time::FormatTime for FormatTime { fn format_time(&self, w: &mut tracing_subscriber::fmt::format::Writer<'_>) -> std::fmt::Result { - let d = - time::OffsetDateTime::now_utc().to_offset(time::UtcOffset::from_hms(8, 0, 0).unwrap()); - w.write_str(&format!( - "{} {}:{}:{}", - d.date(), - d.hour(), - d.minute(), - d.second() - )) + let d = chrono::Local::now(); + w.write_str(&d.format("%Y-%m-%d %H:%M:%S").to_string()) } } diff --git a/oab/src/lib.rs b/oab/src/lib.rs index 8a33f34..76fb46a 100644 --- a/oab/src/lib.rs +++ b/oab/src/lib.rs @@ -7,7 +7,8 @@ pub mod api; mod cfg; +pub mod libs; pub mod models; mod result; -pub use cfg::{init_log, ApplicationConfig, Clis, CLI, CONFIG, DB}; +pub use cfg::{dbtx, init_log, ApplicationConfig, Clis, CLI, CONFIG, DB}; pub use result::{Error, Result}; diff --git a/oab/src/libs/mod.rs b/oab/src/libs/mod.rs new file mode 100644 index 0000000..2f1686c --- /dev/null +++ b/oab/src/libs/mod.rs @@ -0,0 +1,70 @@ +// +// mod.rs +// Copyright (C) 2022 veypi +// 2022-07-12 14:22 +// Distributed under terms of the Apache license. +// + +use std::future::{ready, Ready}; + +use actix_web::{ + dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, + Error, +}; +use futures_util::future::LocalBoxFuture; +use tracing::info; + +// There are two steps in middleware processing. +// 1. Middleware initialization, middleware factory gets called with +// next service in chain as parameter. +// 2. Middleware's call method gets called with normal request. +pub struct SayHi; + +// Middleware factory is `Transform` trait +// `S` - type of the next service +// `B` - type of response's body +impl Transform for SayHi +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = SayHiMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ready(Ok(SayHiMiddleware { service })) + } +} + +pub struct SayHiMiddleware { + service: S, +} + +impl Service for SayHiMiddleware +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type Future = LocalBoxFuture<'static, Result>; + + forward_ready!(service); + + fn call(&self, req: ServiceRequest) -> Self::Future { + println!("Hi from start. You requested: {}", req.path()); + + let fut = self.service.call(req); + + Box::pin(async move { + let res = fut.await?; + println!("Hi from response"); + Ok(res) + }) + } +} diff --git a/oab/src/main.rs b/oab/src/main.rs index 9c5811b..aae8d4e 100644 --- a/oab/src/main.rs +++ b/oab/src/main.rs @@ -4,10 +4,10 @@ // 2022-07-07 23:51 // Distributed under terms of the Apache license. // - use actix_web::{middleware, web, App, HttpServer}; + use oab::{api, init_log, models, Clis, Result, CLI, CONFIG}; -use tracing::info; +use tracing::{info, warn}; #[tokio::main] async fn main() -> Result<()> { @@ -31,10 +31,26 @@ async fn web() -> Result<()> { std::env::set_var("RUST_BACKTRACE", "1"); let serv = HttpServer::new(move || { let logger = middleware::Logger::default(); + let json_config = web::JsonConfig::default() + .limit(4096) + .error_handler(|err, _req|{ + // create custom error response + // oab::Error::InternalServerError + warn!("{:#?}", err); + actix_web::error::InternalError::from_response( + err, + actix_web::HttpResponse::Conflict().finish(), + ) + .into() + }); let app = App::new(); app.wrap(logger) .wrap(middleware::Compress::default()) - .service(web::scope("api").configure(api::routes)) + .service( + web::scope("api") + .app_data(json_config) + .configure(api::routes), + ) }); info!("listen to {}", CONFIG.server_url); serv.bind(CONFIG.server_url.clone())?.run().await?; diff --git a/oab/src/models/app.rs b/oab/src/models/app.rs index 58bddff..82cf629 100644 --- a/oab/src/models/app.rs +++ b/oab/src/models/app.rs @@ -6,6 +6,15 @@ // use rbatis::{crud_table, DateTimeNative}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] +#[serde(rename_all = "lowercase")] +pub enum AppJoin { + Auto, + Disabled, + Applying, +} #[crud_table] #[derive(Debug, Clone)] @@ -21,11 +30,11 @@ pub struct App { pub user_count: usize, pub hide: bool, - pub register: bool, + pub join_method: AppJoin, pub role_id: Option, pub redirect: Option, - pub status: usize, + pub status: i64, } impl App { @@ -41,7 +50,7 @@ impl App { icon: None, user_count: 0, hide: false, - register: false, + join_method: AppJoin::Auto, role_id: None, redirect: None, status: 0, @@ -49,7 +58,8 @@ impl App { } } -#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] +#[derive(Debug, Deserialize, Serialize, Clone)] +#[serde(rename_all = "lowercase")] pub enum AUStatus { OK, Disabled, @@ -58,7 +68,7 @@ pub enum AUStatus { } #[crud_table] -#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct AppUser { pub created: Option, pub updated: Option, diff --git a/oab/src/models/mod.rs b/oab/src/models/mod.rs index 1e5079a..5d12a7e 100644 --- a/oab/src/models/mod.rs +++ b/oab/src/models/mod.rs @@ -5,7 +5,7 @@ // Distributed under terms of the MIT license. // -mod app; +pub mod app; mod role; mod user; use std::{fs::File, io::Read}; diff --git a/oab/src/models/user.rs b/oab/src/models/user.rs index 5b8a371..e588570 100644 --- a/oab/src/models/user.rs +++ b/oab/src/models/user.rs @@ -6,7 +6,43 @@ // // +use actix_web::ResponseError; +use chrono::{prelude::*, Duration}; + +use generic_array::typenum::U32; +use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; use rbatis::{crud_table, DateTimeNative}; +use serde::{Deserialize, Serialize}; +use tracing::info; + +use crate::{Error, Result}; +use aes_gcm::aead::{Aead, NewAead}; +use aes_gcm::{Aes256Gcm, Key, Nonce}; +use block_padding::{Padding, Pkcs7}; + +use generic_array::{ArrayLength, GenericArray}; +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; + +fn rand_str(l: usize) -> String { + thread_rng() + .sample_iter(&Alphanumeric) + .take(l) + .map(char::from) + .collect() +} + +// fn padding>(s: &str) -> GenericArray { +// let msg = s.as_bytes(); +// let pos = msg.len(); +// let mut block: GenericArray = [0xff; 32].into(); +// block[..pos].copy_from_slice(msg); +// Pkcs7::pad(&mut block, pos); +// assert_eq!(&block[..], b"test\x04\x04\x04\x04"); +// let res = Pkcs7::unpad(&block).unwrap(); +// assert_eq!(res, msg); +// block +// } #[crud_table] #[derive(Debug, Clone)] @@ -16,32 +52,120 @@ pub struct User { pub updated: Option, pub delete_flag: bool, - pub username: Option, + pub username: String, pub nickname: Option, pub email: Option, pub phone: Option, pub icon: Option, + pub real_code: Option, + pub check_code: Option, pub status: usize, pub used: usize, pub space: usize, } +impl User { + pub fn token(&self) -> Token { + let t = Token { + iss: "oa".to_string(), + aud: "".to_string(), + exp: (Utc::now() + Duration::days(4)).timestamp(), + iat: Utc::now().timestamp(), + id: self.id.clone(), + }; + t + } + pub fn update_pass(&mut self, p: &str) -> Result<()> { + if p.len() < 6 || p.len() > 32 { + return Err(Error::ArgInvalid("password".to_string())); + } + let p = p.as_bytes(); + let mut key_block: GenericArray = [0xff; 32].into(); + key_block[..p.len()].copy_from_slice(p); + Pkcs7::pad(&mut key_block, p.len()); + // key 32 Byte + let key = Key::from_slice(&key_block); + let cipher = Aes256Gcm::new(&key); + + // 12 Byte + // 96-bits; unique per message + let nonce = Nonce::from_slice(&self.id.as_bytes()[..12]); + + let real = rand_str(32); + + let ciphertext = cipher.encrypt(nonce, real.as_bytes().as_ref())?; + self.check_code = Some(ciphertext); + self.real_code = Some(real); + // let plaintext = cipher.decrypt(nonce, ciphertext.as_ref())?; + // let plaintext = std::str::from_utf8(&plaintext).unwrap(); + // info!("123123{:?}\n{:?}\n", real, plaintext); + + Ok(()) + } +} + impl Default for User { fn default() -> Self { Self { id: rbatis::plugin::object_id::ObjectId::new().to_string(), - created: None, + created: Some(DateTimeNative::now()), updated: None, delete_flag: false, - username: None, + username: "".to_string(), nickname: None, email: None, phone: None, icon: None, + check_code: None, + real_code: None, status: 0, used: 0, space: 300, } } } + +impl actix_web::Responder for User { + type Body = actix_web::body::BoxBody; + + fn respond_to(self, _req: &actix_web::HttpRequest) -> actix_web::HttpResponse { + match serde_json::to_string(&self) { + Ok(body) => match actix_web::HttpResponse::Ok() + .content_type(actix_web::http::header::ContentType::json()) + .message_body(body) + { + Ok(res) => res.map_into_boxed_body(), + Err(err) => Error::from(err).error_response(), + }, + Err(_err) => Error::SerdeError.error_response(), + } + } +} +#[derive(Debug, Serialize, Deserialize)] +pub struct Token { + pub iss: String, // Optional. token 发行者 + pub aud: String, // Optional. token 使用者 + pub exp: i64, // Required 失效时间 + pub iat: i64, // Optional. 发布时间 + pub id: String, // 用户id +} + +impl Token { + pub fn from(t: &str) -> Result { + let token = decode::( + t, + &DecodingKey::from_secret("secret".as_ref()), + &Validation::default(), + )?; + Ok(token.claims) + } + pub fn to_string(&self) -> Result { + let token = encode( + &Header::default(), + self, + &EncodingKey::from_secret("secret".as_ref()), + )?; + Ok(token) + } +} diff --git a/oab/src/result.rs b/oab/src/result.rs index d9101a0..75b0880 100644 --- a/oab/src/result.rs +++ b/oab/src/result.rs @@ -5,15 +5,20 @@ // Distributed under terms of the Apache license. // +use actix_web::http::header; +use actix_web::middleware::ErrorHandlerResponse; +use actix_web::ResponseError; use actix_web::{ - error, + dev, error, http::{header::ContentType, StatusCode}, HttpResponse, }; + use serde::{Deserialize, Serialize}; use thiserror::Error as ThisError; pub type Result = std::result::Result; + // pub type AsyncResult = std::result::Result>; #[derive(Clone, ThisError, Debug, Deserialize, Serialize)] @@ -31,8 +36,18 @@ pub enum Error { #[error("Deserialize / Serialize failed")] SerdeError, - #[error("Resource not found")] - NotFound, + #[error("register disabled")] + AppDisabledRegister, + + #[error("missing {0}")] + Missing(String), + #[error("invalid arg {0}")] + ArgInvalid(String), + #[error("duplicated arg {0}")] + ArgDuplicated(String), + + #[error("not found {0}")] + NotFound(String), #[error("timeout")] Timeout, @@ -94,6 +109,39 @@ impl From for Error { Error::UnsupportedFileType(format!("{:?}", e)) } } +impl From for Error { + fn from(e: actix_web::Error) -> Self { + Error::BusinessException(format!("{:?}", e)) + } +} +impl From for Error { + fn from(e: jsonwebtoken::errors::Error) -> Self { + Error::BusinessException(format!("{:?}", e)) + } +} +impl From for Error { + fn from(e: rbatis::error::Error) -> Self { + Error::BusinessException(format!("{:?}", e)) + } +} +impl From for Error { + fn from(e: aes_gcm::Error) -> Self { + Error::BusinessException(format!("{:?}", e)) + } +} + +impl From> for Error { + fn from(e: Box) -> Self { + Error::BusinessException(format!("{}", e)) + } +} + +impl actix_web::Responder for Error { + type Body = actix_web::body::BoxBody; + fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse { + self.error_response() + } +} impl error::ResponseError for Error { fn error_response(&self) -> HttpResponse { @@ -107,13 +155,9 @@ impl error::ResponseError for Error { Error::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR, Error::BadRequest => StatusCode::BAD_REQUEST, Error::Timeout => StatusCode::GATEWAY_TIMEOUT, + Error::NotFound(_) => StatusCode::NOT_FOUND, + Error::NotAuthed => StatusCode::UNAUTHORIZED, _ => StatusCode::BAD_REQUEST, } } } - -#[derive(Debug, Deserialize, Serialize)] -pub struct ErrResponse { - pub code: Error, - pub detail: String, -} diff --git a/oaf/src/api/ajax.ts b/oaf/src/api/ajax.ts index 918344d..6d8cb83 100644 --- a/oaf/src/api/ajax.ts +++ b/oaf/src/api/ajax.ts @@ -1,5 +1,6 @@ import axios from 'axios' -import {store} from '@/store' +import { store } from '@/store' +import msg from '@veypi/msg' function getQueryVariable(variable: string) { @@ -36,29 +37,30 @@ function baseRequests(url: string, method: any = 'GET', query: any, data: any, s window.location.href = res.headers.redirect_url return } + console.log(res) if (method === 'HEAD') { success(res.headers) } else { - success(res.data) + success(res) } }) .catch((e: any) => { - if (e.response && e.response.status === 401) { + if (typeof fail === 'function') { + fail(e.response) + return + } + let code = e.response.status + if (code === 400) { + msg.Warn(e.response.data) + return + } else if (code === 401) { console.log(e) store.commit('user/logout') return - } - console.log(e) - if (e.response && e.response.status === 500) { + } else if (code === 500) { return } - if (typeof fail === 'function') { - fail(e.response) - } else if (e.response && e.response.status === 400) { - console.log(400) - } else { - console.log(e.request) - } + console.log(e) }) } diff --git a/oaf/src/api/interface.ts b/oaf/src/api/interface.ts index babc42a..da3bc9e 100644 --- a/oaf/src/api/interface.ts +++ b/oaf/src/api/interface.ts @@ -1,4 +1,3 @@ -import {store} from "@/store" export type SuccessFunction = (e: any) => void; export type FailedFunction = (e: any) => void; @@ -22,42 +21,6 @@ export class Interface { } Start(success?: SuccessFunction, fail?: FailedFunction) { - const newFail = function (data: any) { - if (data) { - if (data.code === 40001) { - // no login - store.commit('user/logout') - return - // @ts-ignore - } else if (data.code === 42011 && window.$msg) { - // @ts-ignore - window.$msg.warning('无权限') - } - } - // eslint-disable-next-line @typescript-eslint/ban-ts-ignore - // @ts-ignore - if (data && data.code && Code[data.code]) { - } - if (fail) { - fail(data.err) - } else { - // @ts-ignore - window.$msg.warning(data.err) - } - } - - const newSuccess = function (data: any) { - if (Number(data.status) === 1) { - if (success) { - success(data.content) - } else { - // @ts-ignore - window.$msg.warning('ok') - } - } else { - newFail(data) - } - } - this.method(this.api, this.data, newSuccess, newFail, this.header) + this.method(this.api, this.data, success, fail, this.header) } } diff --git a/oaf/src/api/user.ts b/oaf/src/api/user.ts index 7511b4e..156eb73 100644 --- a/oaf/src/api/user.ts +++ b/oaf/src/api/user.ts @@ -1,7 +1,7 @@ -import {Base64} from 'js-base64' -import {Interface} from './interface' +import { Base64 } from 'js-base64' +import { Interface } from './interface' import ajax from './ajax' -import {BaseUrl} from './setting' +import { BaseUrl } from './setting' export default { local: BaseUrl + 'user/', @@ -14,12 +14,12 @@ export default { }, login(username: string, password: string) { return new Interface(ajax.head, this.local + username, { - UidType: 'username', + typ: 'username', password: Base64.encode(password), }) }, search(q: string) { - return new Interface(ajax.get, this.local, {username: q}) + return new Interface(ajax.get, this.local, { username: q }) }, get(id: number) { return new Interface(ajax.get, this.local + id) diff --git a/oaf/src/views/register.vue b/oaf/src/views/register.vue index e1cd87f..a9134fa 100644 --- a/oaf/src/views/register.vue +++ b/oaf/src/views/register.vue @@ -1,93 +1,92 @@