From 7990fbcf10409cacc266e37e5833d5b008b61542 Mon Sep 17 00:00:00 2001 From: veypi Date: Sat, 25 Mar 2023 18:10:51 +0800 Subject: [PATCH] update --- oab/Cargo.lock | 65 +++ oab/Cargo.toml | 3 + oab/migrations/20220720220617_base.sql | 10 +- oab/proc/Cargo.lock | 47 ++ oab/proc/Cargo.toml | 14 + oab/proc/src/access.rs | 158 +++++++ oab/proc/src/lib.rs | 131 ++++++ oab/src/api/app.rs | 30 +- oab/src/api/user.rs | 93 +++- oab/src/cfg.rs | 2 + oab/src/libs/auth.rs | 80 ++++ oab/src/libs/mod.rs | 2 + oab/src/main.rs | 23 +- oab/src/models/mod.rs | 4 +- oab/src/models/user.rs | 133 ++++-- oab/src/result.rs | 32 +- oaf/src/router/index.ts | 10 +- oafAdmin/package.json | 17 + oafAdmin/src/App.vue | 18 +- oafAdmin/src/api/ajax.ts | 82 ++++ oafAdmin/src/api/app.ts | 49 +++ oafAdmin/src/api/auth.ts | 38 ++ oafAdmin/src/api/index.ts | 31 ++ oafAdmin/src/api/interface.ts | 26 ++ oafAdmin/src/api/resource.ts | 28 ++ oafAdmin/src/api/role.ts | 40 ++ oafAdmin/src/api/setting.ts | 10 + oafAdmin/src/api/token.ts | 18 + oafAdmin/src/api/user.ts | 33 ++ oafAdmin/src/assets/dark.css | 18 + oafAdmin/src/assets/icon.js | 2 +- oafAdmin/src/assets/light.css | 31 ++ oafAdmin/src/assets/mybar.css | 31 ++ oafAdmin/src/auth/index.ts | 74 ++++ oafAdmin/src/components/WxLogin.vue | 33 ++ oafAdmin/src/components/app.vue | 74 ++++ .../src/components/connectors/roleauths.vue | 142 ++++++ .../src/components/connectors/roleusers.vue | 103 +++++ oafAdmin/src/components/editor/resource.vue | 64 +++ oafAdmin/src/components/editor/role.vue | 77 ++++ oafAdmin/src/components/frame.vue | 18 +- oafAdmin/src/components/menu.vue | 134 ++++++ oafAdmin/src/components/myinput/index.ts | 9 + oafAdmin/src/components/myinput/index.vue | 411 ++++++++++++++++++ oafAdmin/src/components/siderframe.vue | 61 +++ oafAdmin/src/components/uploader/index.ts | 9 + oafAdmin/src/components/uploader/uploader.vue | 58 +++ oafAdmin/src/components/userSelect.vue | 54 +++ oafAdmin/src/components/vavator/index.ts | 10 + oafAdmin/src/components/vavator/index.vue | 39 ++ oafAdmin/src/components/vmodal/index.ts | 9 + oafAdmin/src/components/vmodal/index.vue | 50 +++ oafAdmin/src/i18n/en.ts | 33 ++ oafAdmin/src/i18n/index.ts | 26 ++ oafAdmin/src/i18n/zh.ts | 33 ++ oafAdmin/src/index.css | 37 +- oafAdmin/src/main.ts | 3 + oafAdmin/src/models/index.ts | 151 +++++++ oafAdmin/src/msg/index.css | 135 ++++++ oafAdmin/src/msg/index.ts | 110 +++++ oafAdmin/src/router/index.ts | 118 ++++- oafAdmin/src/store/app.ts | 14 + oafAdmin/src/store/index.ts | 11 + oafAdmin/src/store/user.ts | 32 +- oafAdmin/src/views/404.vue | 25 +- oafAdmin/src/views/about.vue | 12 + oafAdmin/src/views/app.vue | 137 ++++++ oafAdmin/src/views/app/main.vue | 25 ++ oafAdmin/src/views/app/roles.vue | 190 ++++++++ oafAdmin/src/views/app/setting.vue | 115 +++++ oafAdmin/src/views/app/users.vue | 202 +++++++++ oafAdmin/src/views/demo.vue | 8 +- oafAdmin/src/views/file.vue | 10 + oafAdmin/src/views/home.vue | 141 +++++- oafAdmin/src/views/login.vue | 155 +++++++ oafAdmin/src/views/register.vue | 94 ++++ oafAdmin/src/views/user_setting.vue | 135 ++++++ oafAdmin/src/views/wx.vue | 51 +++ oafAdmin/vite.config.ts | 8 +- oafAdmin/yarn.lock | 354 ++++++++++++++- 80 files changed, 5006 insertions(+), 97 deletions(-) create mode 100644 oab/proc/Cargo.lock create mode 100644 oab/proc/Cargo.toml create mode 100644 oab/proc/src/access.rs create mode 100644 oab/proc/src/lib.rs create mode 100644 oab/src/libs/auth.rs create mode 100644 oafAdmin/src/api/ajax.ts create mode 100644 oafAdmin/src/api/app.ts create mode 100644 oafAdmin/src/api/auth.ts create mode 100644 oafAdmin/src/api/index.ts create mode 100644 oafAdmin/src/api/interface.ts create mode 100644 oafAdmin/src/api/resource.ts create mode 100644 oafAdmin/src/api/role.ts create mode 100644 oafAdmin/src/api/setting.ts create mode 100644 oafAdmin/src/api/token.ts create mode 100644 oafAdmin/src/api/user.ts create mode 100644 oafAdmin/src/assets/dark.css create mode 100644 oafAdmin/src/assets/light.css create mode 100644 oafAdmin/src/assets/mybar.css create mode 100644 oafAdmin/src/auth/index.ts create mode 100644 oafAdmin/src/components/WxLogin.vue create mode 100644 oafAdmin/src/components/app.vue create mode 100644 oafAdmin/src/components/connectors/roleauths.vue create mode 100644 oafAdmin/src/components/connectors/roleusers.vue create mode 100644 oafAdmin/src/components/editor/resource.vue create mode 100644 oafAdmin/src/components/editor/role.vue create mode 100644 oafAdmin/src/components/menu.vue create mode 100644 oafAdmin/src/components/myinput/index.ts create mode 100644 oafAdmin/src/components/myinput/index.vue create mode 100644 oafAdmin/src/components/siderframe.vue create mode 100644 oafAdmin/src/components/uploader/index.ts create mode 100644 oafAdmin/src/components/uploader/uploader.vue create mode 100644 oafAdmin/src/components/userSelect.vue create mode 100644 oafAdmin/src/components/vavator/index.ts create mode 100644 oafAdmin/src/components/vavator/index.vue create mode 100644 oafAdmin/src/components/vmodal/index.ts create mode 100644 oafAdmin/src/components/vmodal/index.vue create mode 100644 oafAdmin/src/i18n/en.ts create mode 100644 oafAdmin/src/i18n/index.ts create mode 100644 oafAdmin/src/i18n/zh.ts create mode 100644 oafAdmin/src/models/index.ts create mode 100644 oafAdmin/src/msg/index.css create mode 100644 oafAdmin/src/msg/index.ts create mode 100644 oafAdmin/src/store/index.ts create mode 100644 oafAdmin/src/views/about.vue create mode 100644 oafAdmin/src/views/app.vue create mode 100644 oafAdmin/src/views/app/main.vue create mode 100644 oafAdmin/src/views/app/roles.vue create mode 100644 oafAdmin/src/views/app/setting.vue create mode 100644 oafAdmin/src/views/app/users.vue create mode 100644 oafAdmin/src/views/file.vue create mode 100644 oafAdmin/src/views/login.vue create mode 100644 oafAdmin/src/views/register.vue create mode 100644 oafAdmin/src/views/user_setting.vue create mode 100644 oafAdmin/src/views/wx.vue diff --git a/oab/Cargo.lock b/oab/Cargo.lock index b770ff0..c23459c 100644 --- a/oab/Cargo.lock +++ b/oab/Cargo.lock @@ -19,6 +19,29 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "actix-files" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d832782fac6ca7369a70c9ee9a20554623c5e51c76e190ad151780ebea1cf689" +dependencies = [ + "actix-http", + "actix-service", + "actix-utils", + "actix-web", + "askama_escape", + "bitflags", + "bytes", + "derive_more", + "futures-core", + "http-range", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", +] + [[package]] name = "actix-http" version = "3.2.1" @@ -266,6 +289,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + [[package]] name = "atoi" version = "0.4.0" @@ -819,6 +848,12 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + [[package]] name = "httparse" version = "1.7.1" @@ -1024,6 +1059,16 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1154,6 +1199,7 @@ dependencies = [ name = "oab" version = "0.1.0" dependencies = [ + "actix-files", "actix-web", "aes-gcm", "base64", @@ -1165,6 +1211,7 @@ dependencies = [ "include_dir", "jsonwebtoken", "lazy_static", + "proc", "rand", "serde", "serde-big-array", @@ -1327,6 +1374,15 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "proc" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2038,6 +2094,15 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.8" diff --git a/oab/Cargo.toml b/oab/Cargo.toml index 5443a3a..5a68ada 100644 --- a/oab/Cargo.toml +++ b/oab/Cargo.toml @@ -22,6 +22,7 @@ thiserror = "1.0" sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "mysql", "macros", "migrate", "chrono"] } actix-web = "4" +actix-files = "0.6.2" jsonwebtoken = "8" aes-gcm="0.9" @@ -33,3 +34,5 @@ base64 = "0.13.0" uuid = { version = "1.1", features = ["v3","v4", "fast-rng", "macro-diagnostics"]} serde_repr = "0.1.8" + +proc = {path="proc"} diff --git a/oab/migrations/20220720220617_base.sql b/oab/migrations/20220720220617_base.sql index 08f3409..51c9254 100644 --- a/oab/migrations/20220720220617_base.sql +++ b/oab/migrations/20220720220617_base.sql @@ -137,13 +137,17 @@ INSERT INTO `app` (`id`, `name`, `key`, `role_id`) VALUES ('FR9P5t8debxc11aFF', 'oa', 'AMpjwQHwVjGsb1WC4WG6', '1lytMwQL4uiNd0vsc'); INSERT INTO `resource` (`app_id`, `name`) -VALUES ('FR9P5t8debxc11aFF', 'app'); +VALUES ('FR9P5t8debxc11aFF', 'app'), +('FR9P5t8debxc11aFF', 'user'); INSERT INTO `role` (`id`, `app_id`, `name`) VALUES ('1lytMwQL4uiNd0vsc', 'FR9P5t8debxc11aFF', 'admin'); -INSERT INTO `access` (`app_id`, `name`, `role_id`, `user_id`) -VALUES ('FR9P5t8debxc11aFF', 'app', '1lytMwQL4uiNd0vsc', null); +INSERT INTO `access` (`app_id`, `name`, `role_id`, `user_id`,`level`) +VALUES ('FR9P5t8debxc11aFF', 'app', '1lytMwQL4uiNd0vsc', NULL,6), +('FR9P5t8debxc11aFF', 'user', '1lytMwQL4uiNd0vsc', NULL,6); + + ALTER TABLE `app` ADD FOREIGN KEY (`role_id`) REFERENCES `role`(`id`); diff --git a/oab/proc/Cargo.lock b/oab/proc/Cargo.lock new file mode 100644 index 0000000..f96bbb7 --- /dev/null +++ b/oab/proc/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" diff --git a/oab/proc/Cargo.toml b/oab/proc/Cargo.toml new file mode 100644 index 0000000..e9300de --- /dev/null +++ b/oab/proc/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "proc" +version = "0.1.0" +edition = "2021" + + +[lib] +proc-macro = true + +[dependencies] +quote = "1" +proc-macro2 = "1.0" +syn = { version = "1.0", features = ["full", "derive", "extra-traits"] } + diff --git a/oab/proc/src/access.rs b/oab/proc/src/access.rs new file mode 100644 index 0000000..a20fcf7 --- /dev/null +++ b/oab/proc/src/access.rs @@ -0,0 +1,158 @@ +// +// access.rs +// Copyright (C) 2022 veypi +// 2022-09-16 00:13 +// Distributed under terms of the Apache license. +// + +use proc_macro2::{Ident, Span}; +use quote::{quote, ToTokens}; +use syn::{AttributeArgs, ItemFn, NestedMeta, ReturnType}; + +pub struct AccessWrap { + cb_fn: Option, + func: ItemFn, + access: Access, +} + +impl AccessWrap { + pub fn new(args: AttributeArgs, func: ItemFn, cb_fn: Option<&str>) -> syn::Result { + let cb_fn: Option = match cb_fn { + Some(cb) => Some(syn::parse_str(cb)?), + None => None, + }; + let args = Access::new(args)?; + + Ok(Self { + cb_fn, + func, + access: args, + }) + } +} + +impl ToTokens for AccessWrap { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let func_vis = &self.func.vis; + let func_block = &self.func.block; + + let fn_sig = &self.func.sig; + let fn_attrs = &self.func.attrs; + let fn_name = &fn_sig.ident; + let fn_generics = &fn_sig.generics; + let fn_args = &fn_sig.inputs; + let fn_async = &fn_sig.asyncness.unwrap(); + let fn_output = match &fn_sig.output { + ReturnType::Type(ref _arrow, ref ty) => ty.to_token_stream(), + ReturnType::Default => { + quote! {()} + } + }; + + let resp = quote!(Err(Error::NotAuthed)); + + let permissions = &self.access.domain; + // let args = quote! { + // #(#permissions,)* + // }; + let stream = match &self.cb_fn { + Some(cb_fn) => { + let condition = match &self.access.did { + Some(did) => { + quote! { + let _auth_did = #did; + if _auth_token.#cb_fn(#permissions, _auth_did) + } + } + None => { + quote! { + if _auth_token.#cb_fn(#permissions, "") + } + } + }; + quote! { + #(#fn_attrs)* + #func_vis #fn_async fn #fn_name #fn_generics( + _auth_token: Option>, + #fn_args + ) -> #fn_output { + let _auth_token = match _auth_token { + Some(_auth_token) => _auth_token.into_inner(), + None => { + return #resp + } + }; + #condition { + let f = || async move #func_block; + f().await + } else { + #resp + } + } + } + } + None => { + quote! { + #(#fn_attrs)* + #func_vis #fn_async fn #fn_name #fn_generics( + _auth_token: Option>, + #fn_args + ) -> #fn_output { + if _auth_token.is_some() { + let f = || async move #func_block; + f().await + } else { + #resp + } + } + } + } + }; + + let _stream = tokens.extend(stream); + } +} + +struct Access { + domain: syn::LitStr, + did: Option, +} + +impl Access { + fn new(args: AttributeArgs) -> syn::Result { + let mut domain: Option = None; + let mut did = None; + for arg in args { + match arg { + NestedMeta::Lit(syn::Lit::Str(lit)) => { + domain = Some(lit); + } + NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { + path, + lit: syn::Lit::Str(lit_str), + .. + })) => { + if path.is_ident("id") { + let expr = lit_str.parse().unwrap(); + did = Some(expr); + } else { + return Err(syn::Error::new_spanned( + path, + "Unknown identifier. Available: 'id'", + )); + } + } + _ => { + return Err(syn::Error::new_spanned(arg, "Unknown attribute.")); + } + } + } + match domain { + Some(domain) => Ok(Self { domain, did }), + None => Err(syn::Error::new( + Span::call_site(), + "The #[access(..)] macro requires one `auth` argument", + )), + } + } +} diff --git a/oab/proc/src/lib.rs b/oab/proc/src/lib.rs new file mode 100644 index 0000000..a3d8da5 --- /dev/null +++ b/oab/proc/src/lib.rs @@ -0,0 +1,131 @@ +// +// lib.rs +// Copyright (C) 2022 veypi +// 2022-09-16 00:07 +// Distributed under terms of the Apache license. +// +// +use proc_macro::TokenStream; +use quote::{quote, ToTokens}; +use syn::{parse_macro_input, AttributeArgs, ItemFn}; +mod access; +use access::AccessWrap; + +#[proc_macro_attribute] +pub fn have_access(args: TokenStream, input: TokenStream) -> TokenStream { + check_permissions(None, args, input) +} + +#[proc_macro_attribute] +pub fn access_read(args: TokenStream, input: TokenStream) -> TokenStream { + check_permissions(Some("can_read"), args, input) +} + +#[proc_macro_attribute] +pub fn access_create(args: TokenStream, input: TokenStream) -> TokenStream { + check_permissions(Some("can_create"), args, input) +} + +#[proc_macro_attribute] +pub fn access_update(args: TokenStream, input: TokenStream) -> TokenStream { + check_permissions(Some("can_update"), args, input) +} + +#[proc_macro_attribute] +pub fn access_delete(args: TokenStream, input: TokenStream) -> TokenStream { + check_permissions(Some("can_delete"), args, input) +} + +fn check_permissions(cb_fn: Option<&str>, args: TokenStream, input: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as AttributeArgs); + let func = parse_macro_input!(input as ItemFn); + + match AccessWrap::new(args, func, cb_fn) { + Ok(ac) => ac.into_token_stream().into(), + Err(err) => err.to_compile_error().into(), + } +} + +#[proc_macro_derive(MyDisplay)] +#[doc(hidden)] +pub fn display(input: TokenStream) -> TokenStream { + // Parse the string representation + let ast: syn::DeriveInput = syn::parse(input).unwrap(); + + match ast.data { + syn::Data::Enum(ref enum_data) => { + let name = &ast.ident; + impl_display(name, enum_data).into() + } + _ => panic!("#[derive(Display)] works only on enums"), + } +} + +fn impl_display(name: &syn::Ident, data: &syn::DataEnum) -> proc_macro2::TokenStream { + let variants = data + .variants + .iter() + .map(|variant| impl_display_for_variant(name, variant)); + + quote! { + impl ::std::fmt::Display for #name { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> { + match *self { + #(#variants)* + } + } + } + } +} + +fn impl_display_for_variant(name: &syn::Ident, variant: &syn::Variant) -> proc_macro2::TokenStream { + let id = &variant.ident; + match variant.fields { + syn::Fields::Unit => match &variant.discriminant { + // print true value of enummember + // enum { + // a = 1 + // + // } + Some((_, value)) => { + quote! { + #name::#id => { + f.write_str(stringify!(#value).to_lowercase().as_str()) + } + } + } + _ => { + // print lowercase name of enummember + quote! { + #name::#id => { + f.write_str(stringify!(#id).to_lowercase().as_str()) + } + } + } + }, + syn::Fields::Unnamed(ref fields) => match fields.unnamed.len() { + 0 => { + quote! { + #name::#id() => { + f.write_str(stringify!(#id))?; + f.write_str("()") + } + } + } + 1 => { + quote! { + #name::#id(ref inner) => { + ::std::fmt::Display::fmt(inner, f) + } + } + } + _ => { + panic!( + "#[derive(Display)] does not support tuple variants with more than one \ + fields" + ) + } + }, + _ => panic!("#[derive(Display)] works only with unit and tuple variants"), + } +} diff --git a/oab/src/api/app.rs b/oab/src/api/app.rs index 35b13e2..62ed87f 100644 --- a/oab/src/api/app.rs +++ b/oab/src/api/app.rs @@ -6,10 +6,14 @@ // // use actix_web::{delete, get, post, web, Responder}; +use proc::access_read; +use serde::{Deserialize, Serialize}; use crate::{models, Error, Result, CONFIG}; +use chrono::NaiveDateTime; #[get("/app/{id}")] +#[access_read("app")] pub async fn get(id: web::Path) -> Result { let n = id.into_inner(); if !n.is_empty() { @@ -23,11 +27,35 @@ pub async fn get(id: web::Path) -> Result { } } +#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] +pub struct App { + pub id: String, + pub created: Option, + pub updated: Option, + + pub name: Option, + pub des: Option, + pub icon: Option, + pub user_count: i64, + + pub hide: bool, + pub join_method: models::AppJoin, + pub role_id: Option, + pub redirect: Option, + + pub status: i64, + pub u_status: i64, +} + #[get("/app/")] +#[access_read("app")] pub async fn list() -> Result { - let result = sqlx::query_as::<_, models::App>("select * from app") + let result = sqlx::query_as::<_, App>( + "select app.id,app.created, app.updated, app.icon, app.name, app.des, app.user_count, app.hide,app.join_method, app.role_id, app.redirect, app.status, app_user.status as u_status from app left join app_user on app_user.user_id = ? && app_user.app_id = app.id", + ).bind(_auth_token.id) .fetch_all(CONFIG.db()) .await?; + Ok(web::Json(result)) } diff --git a/oab/src/api/user.rs b/oab/src/api/user.rs index 38a42d8..2534579 100644 --- a/oab/src/api/user.rs +++ b/oab/src/api/user.rs @@ -10,29 +10,60 @@ use std::fmt::Debug; use crate::{models, Error, Result, CONFIG}; use actix_web::{delete, get, head, http, post, web, HttpResponse, Responder}; use base64; +use proc::access_read; +use rand::Rng; use serde::{Deserialize, Serialize}; use tracing::info; #[get("/user/{id}")] -pub async fn get(id: web::Path) -> Result { +#[access_read("user", id = "&id.clone()")] +pub async fn get(id: web::Path) -> Result { let n = id.into_inner(); if !n.is_empty() { - let s = sqlx::query_as::<_, models::User>("select *& from user where id = ?") - .bind(n) - .fetch_one(CONFIG.db()) - .await?; - info!("{:#?}", s); - Ok(s) + let s = sqlx::query!( + "select id,updated,created,username,nickname,email,icon,status, used, space from user where id = ?",n + ).map(|row| models::User { + id: row.id, + created: row.created, + updated: row.updated, + username: row.username, + nickname: row.nickname, + email: row.email, + status: row.status, + used: row.used, + space: row.space.unwrap_or(0), + icon: row.icon, + ..Default::default() + }) + .fetch_one(CONFIG.db()) + .await?; + Ok(web::Json(s)) } else { Err(Error::Missing("id".to_string())) } } #[get("/user/")] +#[access_read("user")] pub async fn list() -> Result { - let result = sqlx::query_as::<_, models::User>("select * from user") - .fetch_all(CONFIG.db()) - .await?; + let result = sqlx::query!( + "select id,updated,created,username,nickname,email,icon,status, used, space from user", + ) + .map(|row| models::User { + id: row.id, + created: row.created, + updated: row.updated, + username: row.username, + nickname: row.nickname, + email: row.email, + status: row.status, + used: row.used, + space: row.space.unwrap_or(0), + icon: row.icon, + ..Default::default() + }) + .fetch_all(CONFIG.db()) + .await?; Ok(web::Json(result)) } @@ -126,8 +157,15 @@ values ( ?, ?, ? ) } }; if i == 0 { + 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(&u.id) + .bind(CONFIG.uuid.clone()) + .fetch_all(CONFIG.db()) + .await?; Ok(HttpResponse::build(http::StatusCode::OK) - .insert_header(("auth_token", u.token().to_string()?)) + .insert_header(("auth_token", u.token(result).to_string()?)) .body("".to_string())) } else { Ok(HttpResponse::build(http::StatusCode::OK) @@ -157,6 +195,7 @@ pub async fn register(q: web::Json) -> Result { None => { let mut u = models::User::default(); u.username = q.username.clone(); + u.id = uuid::Uuid::new_v4().to_string().replace("-", ""); let p = match base64::decode(q.password.as_bytes()) { Err(_) => return Err(Error::ArgInvalid("password".to_string())), Ok(p) => p, @@ -167,6 +206,9 @@ pub async fn register(q: web::Json) -> Result { }; info!("{}", p); u.update_pass(&p)?; + let mut rng = rand::thread_rng(); + let idx: i64 = rng.gen_range(1..221); + u.icon = Some(format!("/media/icon/usr/{:04}.jpg", idx)); u } }; @@ -180,22 +222,28 @@ pub async fn register(q: web::Json) -> Result { 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::Auto => { + au.status = models::app::AUStatus::OK; + } models::app::AppJoin::Applying => au.status = models::app::AUStatus::Applying, } let mut c = CONFIG.db().begin().await?; + // 创建用户 sqlx::query!( r#" -insert into user (id,username,real_code,check_code) -values ( ?, ?, ?, ?) +insert into user (id,username,real_code,check_code,icon) +values ( ?, ?, ?, ?, ?) "#, u.id, u.username, u.real_code, u.check_code, + u.icon, ) .execute(&mut c) .await?; + + // 关联应用 sqlx::query!( r#" insert into app_user ( app_id, user_id, status) @@ -207,6 +255,23 @@ values ( ?, ?, ?, ?) ) .execute(&mut c) .await?; + if oa.role_id.is_some() { + match au.status { + models::app::AUStatus::OK => { + sqlx::query!( + r#" + insert into user_role (user_id, role_id) + values (?, ?) + "#, + au.user_id, + oa.role_id.unwrap(), + ) + .execute(&mut c) + .await?; + } + _ => {} + } + } c.commit().await?; Ok("ok".to_string()) } diff --git a/oab/src/cfg.rs b/oab/src/cfg.rs index 8be8862..773d76f 100644 --- a/oab/src/cfg.rs +++ b/oab/src/cfg.rs @@ -62,6 +62,7 @@ pub struct ApplicationConfig { pub key: String, pub debug: bool, pub server_url: String, + pub media_path: String, pub db_url: String, pub db_user: String, pub db_pass: String, @@ -108,6 +109,7 @@ impl ApplicationConfig { key: "AMpjwQHwVjGsb1WC4WG6".to_string(), debug: true, server_url: "127.0.0.1:4001".to_string(), + media_path: "/Users/veypi/test/media/".to_string(), db_url: "127.0.0.1:3306".to_string(), db_user: "root".to_string(), db_pass: "123456".to_string(), diff --git a/oab/src/libs/auth.rs b/oab/src/libs/auth.rs new file mode 100644 index 0000000..ee5319c --- /dev/null +++ b/oab/src/libs/auth.rs @@ -0,0 +1,80 @@ +// +// auth.rs +// Copyright (C) 2022 veypi +// 2022-09-01 17:39 +// Distributed under terms of the Apache license. +// + +use std::cell::RefCell; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll}; + +use actix_web::body::MessageBody; +use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform}; +use actix_web::http::header::HeaderValue; +use actix_web::{Error, HttpMessage}; +use futures_util::future::{ok, Ready}; +use futures_util::Future; +use tracing::warn; + +use crate::models; + +// custom request auth middleware +pub struct Auth; + +impl Transform for Auth +where + S: Service, Error = Error> + 'static, + S::Future: 'static, + B: MessageBody + 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type Transform = AuthMiddleware; + type InitError = (); + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ok(AuthMiddleware { + service: Rc::new(RefCell::new(service)), + }) + } +} + +pub struct AuthMiddleware { + service: Rc>, +} + +impl Service for AuthMiddleware +where + S: Service, Error = Error> + 'static, + S::Future: 'static, + B: MessageBody + 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type Future = Pin>>>; + + fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx) + } + + fn call(&self, req: ServiceRequest) -> Self::Future { + let svc = self.service.clone(); + + Box::pin(async move { + let value = HeaderValue::from_str("").unwrap(); + let token = req.headers().get("auth_token").unwrap_or(&value); + let token = models::Token::from(token.to_str().unwrap_or("")); + match token { + Ok(t) => { + req.extensions_mut().insert(t.id.clone()); + req.extensions_mut().insert(t); + } + Err(e) => warn!("{}", e), + }; + Ok(svc.call(req).await?) + }) + } +} diff --git a/oab/src/libs/mod.rs b/oab/src/libs/mod.rs index 2f1686c..de67944 100644 --- a/oab/src/libs/mod.rs +++ b/oab/src/libs/mod.rs @@ -5,6 +5,8 @@ // Distributed under terms of the Apache license. // +pub mod auth; + use std::future::{ready, Ready}; use actix_web::{ diff --git a/oab/src/main.rs b/oab/src/main.rs index 045a7dd..7e91919 100644 --- a/oab/src/main.rs +++ b/oab/src/main.rs @@ -4,14 +4,17 @@ // 2022-07-07 23:51 // Distributed under terms of the Apache license. // +use actix_files as fs; use actix_web::{ - middleware, + dev, + http::StatusCode, + middleware::{self, ErrorHandlerResponse, ErrorHandlers}, web::{self, Data}, App, HttpServer, }; -use oab::{api, init_log, models, Clis, Result, CLI, CONFIG}; -use tracing::{info, warn}; +use oab::{api, init_log, libs, models, Clis, Result, CLI, CONFIG}; +use tracing::{error, info, warn}; #[tokio::main] async fn main() -> Result<()> { @@ -48,8 +51,14 @@ async fn web() -> Result<()> { let app = App::new(); app.wrap(logger) .wrap(middleware::Compress::default()) + .service(fs::Files::new("/media", CONFIG.media_path.clone()).show_files_listing()) .service( web::scope("api") + .wrap( + ErrorHandlers::new() + .handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_header), + ) + .wrap(libs::auth::Auth) .app_data(json_config) .app_data(Data::new(CONFIG.db())) .configure(api::routes), @@ -59,3 +68,11 @@ async fn web() -> Result<()> { serv.bind(CONFIG.server_url.clone())?.run().await?; Ok(()) } + +fn add_error_header( + res: dev::ServiceResponse, +) -> std::result::Result, actix_web::Error> { + error!("{}", res.response().error().unwrap()); + + Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) +} diff --git a/oab/src/models/mod.rs b/oab/src/models/mod.rs index 84f4b7d..07392cc 100644 --- a/oab/src/models/mod.rs +++ b/oab/src/models/mod.rs @@ -11,9 +11,9 @@ mod user; use tracing::info; -pub use app::{AUStatus, App, AppUser}; +pub use app::{AUStatus, App, AppJoin, AppUser}; pub use role::{Access, Resource, Role}; -pub use user::User; +pub use user::{AccessCore, AccessLevel, Token, User}; use crate::CONFIG; diff --git a/oab/src/models/user.rs b/oab/src/models/user.rs index 75b017f..c009863 100644 --- a/oab/src/models/user.rs +++ b/oab/src/models/user.rs @@ -20,8 +20,10 @@ use block_padding::{Padding, Pkcs7}; use generic_array::typenum::U32; use generic_array::GenericArray; +use proc::MyDisplay; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; +use serde_repr::*; fn rand_str(l: usize) -> String { thread_rng() @@ -43,7 +45,7 @@ fn rand_str(l: usize) -> String { // block // } -#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] +#[derive(Debug, Default, Serialize, Deserialize, sqlx::FromRow)] pub struct User { pub id: String, pub created: Option, @@ -63,13 +65,21 @@ pub struct User { } impl User { - pub fn token(&self) -> Token { + pub fn token(&self, ac: Vec) -> Token { + let default_ico = "/media/".to_string(); let t = Token { - iss: "oa".to_string(), + iss: "onedt".to_string(), aud: "".to_string(), exp: (Utc::now() + Duration::days(4)).timestamp(), iat: Utc::now().timestamp(), id: self.id.clone(), + ico: self.icon.as_ref().unwrap_or(&default_ico).to_string(), + access: Some(ac), + nickname: self + .nickname + .as_ref() + .unwrap_or(&self.username.clone()) + .to_string(), }; t } @@ -126,28 +136,6 @@ impl User { } } -impl Default for User { - fn default() -> Self { - Self { - id: uuid::Uuid::new_v4().to_string().replace("-", ""), - created: None, - updated: None, - delete_flag: false, - - 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; @@ -164,13 +152,55 @@ impl actix_web::Responder for User { } } } -#[derive(Debug, Serialize, Deserialize)] + +#[derive(Debug, Serialize, Deserialize, Clone, sqlx::FromRow)] +pub struct Access { + pub created: Option, + pub updated: Option, + pub user_id: String, + pub domain: String, + pub did: Option, + pub l: AccessLevel, +} + +#[derive(Debug, Serialize, Deserialize, Clone, sqlx::FromRow)] +pub struct AccessCore { + pub name: String, + pub rid: Option, + pub level: AccessLevel, +} + +#[derive( + MyDisplay, + Debug, + Deserialize_repr, + Serialize_repr, + Clone, + sqlx::Type, + PartialEq, + Eq, + PartialOrd, + Ord, +)] +#[repr(i64)] +pub enum AccessLevel { + No = 0, + Read = 1, + Create = 2, + Update = 3, + Delete = 4, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] 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 + pub nickname: String, + pub ico: String, + pub access: Option>, } impl Token { @@ -180,7 +210,19 @@ impl Token { &DecodingKey::from_secret("secret".as_ref()), &Validation::default(), )?; - Ok(token.claims) + if token.claims.is_valid() { + Ok(token.claims) + } else { + Err(Error::ExpiredToken) + } + } + pub fn is_valid(&self) -> bool { + info!("{}/{}", self.exp, Utc::now().timestamp()); + if self.exp > Utc::now().timestamp() { + true + } else { + false + } } pub fn to_string(&self) -> Result { let token = encode( @@ -190,4 +232,41 @@ impl Token { )?; Ok(token) } + + fn check(&self, domain: &str, did: &str, l: AccessLevel) -> bool { + println!("{:#?}|{:#?}|{}|", self.access, domain, did); + match &self.access { + Some(ac) => { + for ele in ac { + if ele.name == domain && ele.level >= l { + match &ele.rid { + Some(temp) => { + if temp == did { + return true; + } + } + None => return true, + } + } + } + false + } + None => false, + } + } + pub fn can_read(&self, domain: &str, did: &str) -> bool { + self.check(domain, did, AccessLevel::Read) + } + + pub fn can_create(&self, domain: &str, did: &str) -> bool { + self.check(domain, did, AccessLevel::Create) + } + + pub fn can_update(&self, domain: &str, did: &str) -> bool { + self.check(domain, did, AccessLevel::Update) + } + + pub fn can_delete(&self, domain: &str, did: &str) -> bool { + self.check(domain, did, AccessLevel::Delete) + } } diff --git a/oab/src/result.rs b/oab/src/result.rs index b3f189d..b7fe9df 100644 --- a/oab/src/result.rs +++ b/oab/src/result.rs @@ -5,11 +5,9 @@ // Distributed under terms of the Apache license. // -use actix_web::http::header; -use actix_web::middleware::ErrorHandlerResponse; use actix_web::ResponseError; use actix_web::{ - dev, error, + error, http::{header::ContentType, StatusCode}, HttpResponse, }; @@ -19,6 +17,28 @@ use thiserror::Error as ThisError; use tracing::info; pub type Result = std::result::Result; +pub type JsonResult = std::result::Result, Error>; + +#[derive(Serialize, Deserialize)] +pub struct JsonResponse { + pub content: T, +} +impl From for JsonResponse { + fn from(e: T) -> Self { + Self { content: e } + } +} +impl actix_web::Responder for JsonResponse +where + T: serde::Serialize, +{ + type Body = actix_web::body::BoxBody; + fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse { + HttpResponse::build(StatusCode::OK) + .insert_header(ContentType::json()) + .body(serde_json::to_string(&self.content).unwrap()) + } +} // pub type AsyncResult = std::result::Result>; @@ -65,6 +85,11 @@ pub enum Error { InvalidSessionId, #[error("invalid verify code")] InvalidVerifyCode, + #[error("invalid token")] + InvalidToken, + #[error("expired token")] + ExpiredToken, + #[error("no access")] NotAuthed, #[error("login failed")] @@ -98,6 +123,7 @@ pub enum Error { #[error("{0}")] BusinessException(String), + #[error("invalid header (expected {expected:?}, found {found:?})")] InvalidHeader { expected: String, found: String }, diff --git a/oaf/src/router/index.ts b/oaf/src/router/index.ts index 638a914..5b8ccbc 100644 --- a/oaf/src/router/index.ts +++ b/oaf/src/router/index.ts @@ -1,7 +1,7 @@ -import {createRouter, createWebHistory, RouteLocationNormalized} from 'vue-router' +import { createRouter, createWebHistory } from 'vue-router' import util from '@/libs/util' -import {Auths, R} from '@/auth' -import {store} from '@/store' +import { Auths, R } from '@/auth' +import { store } from '@/store' declare module 'vue-router' { @@ -29,7 +29,7 @@ const router = createRouter({ { path: '/app/:uuid?', component: () => import('@/views/app.vue'), - redirect: {name: 'app.main'}, + redirect: { name: 'app.main' }, children: [ { path: 'main', @@ -125,7 +125,7 @@ router.beforeEach((to, from) => { return { name: 'login', // 保存我们所在的位置,以便以后再来 - query: {redirect: to.fullPath}, + query: { redirect: to.fullPath }, } } if (to.meta.checkAuth) { diff --git a/oafAdmin/package.json b/oafAdmin/package.json index 8188c81..6f938b0 100644 --- a/oafAdmin/package.json +++ b/oafAdmin/package.json @@ -10,9 +10,26 @@ "dependencies": { "@veypi/one-icon": "2", "animate.css": "^4.1.1", + "axios": "^1.2.1", + "base-64": "^1.0.0", + "fast-xml-parser": "^3.19.0", + "he": "^1.2.0", + "hot-patcher": "^0.5.0", + "js-base64": "^3.7.3", + "layerr": "^0.1.2", + "less": "^4.1.3", + "md5": "^2.3.0", + "minimatch": "^5.1.2", "mitt": "^3.0.0", + "nested-property": "^4.0.0", + "path-posix": "^1.0.0", "pinia": "^2.0.13", + "seamless-scroll-polyfill": "^2.2.0", + "url-join": "^4.0.1", + "url-parse": "^1.5.3", + "validator": "^13.7.0", "vue": "^3.2.25", + "vue-i18n": "^9.2.2", "vue-router": "4" }, "devDependencies": { diff --git a/oafAdmin/src/App.vue b/oafAdmin/src/App.vue index 7516a1f..1d09640 100644 --- a/oafAdmin/src/App.vue +++ b/oafAdmin/src/App.vue @@ -1,6 +1,22 @@ @@ -24,7 +40,6 @@ import { useUserStore } from '@/store/user' import { onBeforeMount } from 'vue' let user = useUserStore() -user.setUser() onBeforeMount(() => { let loader = document.getElementById('loader-wrapper') if (loader && loader.parentElement) { @@ -32,5 +47,6 @@ onBeforeMount(() => { } // store.dispatch('fetchSelf') // store.dispatch('user/fetchUserData') + user.fetchUserData() }) diff --git a/oafAdmin/src/api/ajax.ts b/oafAdmin/src/api/ajax.ts new file mode 100644 index 0000000..a10cf48 --- /dev/null +++ b/oafAdmin/src/api/ajax.ts @@ -0,0 +1,82 @@ +import axios from 'axios' +import msg from '@/msg' + + +function getQueryVariable(variable: string) { + let query = window.location.search.substring(1) + let vars = query.split('&') + for (let i = 0; i < vars.length; i++) { + let pair = vars[i].split('=') + if (pair[0] == variable) { + return pair[1] + } + } + return '' +} + +function baseRequests(url: string, method: any = 'GET', query: any, data: any, success: any, fail?: Function, header?: any) { + let headers = { + auth_token: localStorage.auth_token || decodeURIComponent(getQueryVariable('token') as string), + } + if (header) { + headers = Object.assign(headers, header) + } + return axios({ + url: url, + params: query, + data: data, + method: method, + headers: headers, + }).then((res: any) => { + if ('redirect_url' in res.headers) { + window.location.href = res.headers.redirect_url + return + } + if (method === 'HEAD') { + success(res.headers) + } else { + success(res.data) + } + }) + .catch((e: any) => { + if (typeof fail === 'function') { + fail(e.response) + return + } + let code = e.response.status + if (code === 400) { + msg.Warn(e.response.headers.error) + return + } else if (code === 401) { + console.log(e) + // store.commit('user/logout') + return + } else if (code === 500) { + return + } + console.log(e) + }) +} + +const ajax = { + get(url: '', data = {}, success = {}, fail?: Function, header?: any) { + return baseRequests(url, 'GET', data, {}, success, fail, header) + }, + head(url: '', data = {}, success = {}, fail?: Function, header?: any) { + return baseRequests(url, 'HEAD', data, {}, success, fail, header) + }, + delete(url: '', data = {}, success = {}, fail?: Function, header?: any) { + return baseRequests(url, 'DELETE', data, {}, success, fail, header) + }, + post(url: '', data = {}, success = {}, fail?: Function, header?: any) { + return baseRequests(url, 'POST', {}, data, success, fail, header) + }, + put(url: '', data = {}, success = {}, fail?: Function, header?: any) { + return baseRequests(url, 'PUT', {}, data, success, fail, header) + }, + patch(url: '', data = {}, success = {}, fail?: Function, header?: any) { + return baseRequests(url, 'PATCH', {}, data, success, fail, header) + }, +} + +export default ajax diff --git a/oafAdmin/src/api/app.ts b/oafAdmin/src/api/app.ts new file mode 100644 index 0000000..bcd1198 --- /dev/null +++ b/oafAdmin/src/api/app.ts @@ -0,0 +1,49 @@ +/* +* @name: app +* @author: veypi +* @date: 2021-11-17 14:44 +* @description:ap +* @update: 2021-11-17 14:44 +*/ +import { Interface } from './interface' +import ajax from './ajax' +import { BaseUrl } from './setting' + +export default { + local: BaseUrl + 'app/', + self() { + return new Interface(ajax.get, this.local, { option: 'oa' }) + }, + getKey(uuid: string) { + return new Interface(ajax.get, this.local + uuid, { option: 'key' }) + }, + create(name: string, icon: string) { + return new Interface(ajax.post, this.local, { name, icon }) + }, + get(uuid: string) { + return new Interface(ajax.get, this.local + uuid) + }, + list() { + return new Interface(ajax.get, this.local) + }, + update(uuid: string, props: any) { + return new Interface(ajax.patch, this.local + uuid, props) + }, + user(uuid: string) { + if (uuid === '') { + uuid = '_' + } + return { + local: this.local + uuid + '/user/', + list(uid: number) { + return new Interface(ajax.get, this.local + uid) + }, + add(uid: number) { + return new Interface(ajax.post, this.local + uid) + }, + update(uid: number, status: string) { + return new Interface(ajax.patch, this.local + uid, { status }) + }, + } + }, +} diff --git a/oafAdmin/src/api/auth.ts b/oafAdmin/src/api/auth.ts new file mode 100644 index 0000000..6b65318 --- /dev/null +++ b/oafAdmin/src/api/auth.ts @@ -0,0 +1,38 @@ +import {Interface} from './interface' +import ajax from './ajax' +import {BaseUrl} from './setting' + +export default (uuid: string) => { + return { + local: BaseUrl + 'app/' + uuid + '/auth/', + get(id: number) { + return new Interface(ajax.get, this.local + id) + }, + del(id: number) { + return new Interface(ajax.delete, this.local + id) + }, + update(id: number, ResourceID: number, RUID: string, Level: number) { + return new Interface(ajax.patch, this.local + id, { + ResourceID, + RUID, + Level, + }) + }, + create(ResourceID: number, UserID: number | null, RoleID: number | null, RUID: string, Level: number) { + return new Interface(ajax.post, this.local, { + ResourceID, + UserID, + RoleID, + RUID, + Level, + }) + }, + listOfUser(user_id: number) { + return new Interface(ajax.get, this.local, {uid: user_id}) + }, + listOfRole(id: number) { + return new Interface(ajax.get, this.local, {rid: id}) + }, + } +} + diff --git a/oafAdmin/src/api/index.ts b/oafAdmin/src/api/index.ts new file mode 100644 index 0000000..d9abf7d --- /dev/null +++ b/oafAdmin/src/api/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 light + * + * Distributed under terms of the MIT license. + */ + +import {App} from 'vue' +import role from "./role"; +import app from './app' +import user from './user' +import auth from './auth' +import resource from './resource' +import token from './token' + + +const api = { + user: user, + token: token, + app: app, + auth: auth, + role: role, + resource: resource +} + +const Api = { + install(vue: App): void { + vue.config.globalProperties.$api = api + } +} +export {Api} +export default api diff --git a/oafAdmin/src/api/interface.ts b/oafAdmin/src/api/interface.ts new file mode 100644 index 0000000..da3bc9e --- /dev/null +++ b/oafAdmin/src/api/interface.ts @@ -0,0 +1,26 @@ + +export type SuccessFunction = (e: any) => void; +export type FailedFunction = (e: any) => void; + +const Code = { + 42011: '无操作权限', + 22031: '资源不存在 或 您无权操作该资源' +} + +export class Interface { + private readonly method: Function + private readonly api: string + private readonly data: any + private readonly header: any + + constructor(method: Function, api: string, data?: any, headers?: any) { + this.method = method + this.api = api + this.data = data + this.header = headers + } + + Start(success?: SuccessFunction, fail?: FailedFunction) { + this.method(this.api, this.data, success, fail, this.header) + } +} diff --git a/oafAdmin/src/api/resource.ts b/oafAdmin/src/api/resource.ts new file mode 100644 index 0000000..878553d --- /dev/null +++ b/oafAdmin/src/api/resource.ts @@ -0,0 +1,28 @@ +/* +* @name: resource +* @author: veypi +* @date: 2021-11-18 15:52 +* @description:resource +*/ + +import {Interface} from './interface' +import ajax from './ajax' +import {BaseUrl} from './setting' + +export default (uuid: string) => { + return { + local: BaseUrl + 'app/' + uuid + '/resource/', + list() { + return new Interface(ajax.get, this.local) + }, + update(id: number, props: {}) { + return new Interface(ajax.patch, this.local + id, props) + }, + create(Name: string, Des: string) { + return new Interface(ajax.post, this.local, {Name, Des}) + }, + delete(id: number) { + return new Interface(ajax.delete, this.local + id) + }, + } +} diff --git a/oafAdmin/src/api/role.ts b/oafAdmin/src/api/role.ts new file mode 100644 index 0000000..18b6018 --- /dev/null +++ b/oafAdmin/src/api/role.ts @@ -0,0 +1,40 @@ +import {BaseUrl} from './setting' +import {Interface} from './interface' +import ajax from './ajax' + +export default (uuid: string) => { + return { + local: BaseUrl + 'app/' + uuid + '/role/', + get(id: number) { + return new Interface(ajax.get, this.local + id) + }, + list() { + return new Interface(ajax.get, this.local) + }, + update(id: number, props) { + return new Interface(ajax.patch, this.local + id, props) + }, + create(Name: string, Tag: string) { + return new Interface(ajax.post, this.local, { + Name, Tag, + }) + }, + delete(id: number) { + return new Interface(ajax.delete, this.local + id) + }, + user(id: number) { + return { + local: this.local + id + '/user/', + list() { + return new Interface(ajax.get, this.local) + }, + create(uid: number) { + return new Interface(ajax.post, this.local + uid) + }, + delete(uid: number) { + return new Interface(ajax.delete, this.local + uid) + }, + } + }, + } +} diff --git a/oafAdmin/src/api/setting.ts b/oafAdmin/src/api/setting.ts new file mode 100644 index 0000000..c9a719a --- /dev/null +++ b/oafAdmin/src/api/setting.ts @@ -0,0 +1,10 @@ +/* +* @name: setting +* @author: veypi +* @date: 2021-11-17 15:45 +* @description:setting +* @update: 2021-11-17 15:45 +*/ + + +export const BaseUrl = '/api/' diff --git a/oafAdmin/src/api/token.ts b/oafAdmin/src/api/token.ts new file mode 100644 index 0000000..ea088a4 --- /dev/null +++ b/oafAdmin/src/api/token.ts @@ -0,0 +1,18 @@ +/* +* @name: token +* @author: veypi +* @date: 2021-11-26 19:22 +* @description:token +*/ + +import {Interface} from '@/api/interface' +import ajax from './ajax' + +export default (uuid: string) => { + return { + local: '/api/app/' + uuid + '/token/', + get() { + return new Interface(ajax.get, this.local) + }, + } +} diff --git a/oafAdmin/src/api/user.ts b/oafAdmin/src/api/user.ts new file mode 100644 index 0000000..156eb73 --- /dev/null +++ b/oafAdmin/src/api/user.ts @@ -0,0 +1,33 @@ +import { Base64 } from 'js-base64' +import { Interface } from './interface' +import ajax from './ajax' +import { BaseUrl } from './setting' + +export default { + local: BaseUrl + 'user/', + register(username: string, password: string, prop?: any) { + const data = Object.assign({ + username: username, + password: Base64.encode(password), + }, prop) + return new Interface(ajax.post, this.local, data) + }, + login(username: string, password: string) { + return new Interface(ajax.head, this.local + username, { + typ: 'username', + password: Base64.encode(password), + }) + }, + search(q: string) { + return new Interface(ajax.get, this.local, { username: q }) + }, + get(id: number) { + return new Interface(ajax.get, this.local + id) + }, + list() { + return new Interface(ajax.get, this.local) + }, + update(id: number, props: any) { + return new Interface(ajax.patch, this.local + id, props) + }, +} diff --git a/oafAdmin/src/assets/dark.css b/oafAdmin/src/assets/dark.css new file mode 100644 index 0000000..e9a90c5 --- /dev/null +++ b/oafAdmin/src/assets/dark.css @@ -0,0 +1,18 @@ +/* + * dark.css + * Copyright (C) 2022 veypi + * + * Distributed under terms of the Apache license. + */ + +:root[theme="dark"] { + --base-color: #fff; + --base-bg: #252525; + --base-bg-1: #303030; + --base-bg-2: #404040; + --base-bg-3: #505050; + + --header-bg: #404040; + + --L0: #63e2b7; +} diff --git a/oafAdmin/src/assets/icon.js b/oafAdmin/src/assets/icon.js index e22ce1a..47670c9 100644 --- a/oafAdmin/src/assets/icon.js +++ b/oafAdmin/src/assets/icon.js @@ -1 +1 @@ -!function(c){var l,t,a,h,o,e='',i=(i=document.getElementsByTagName("script"))[i.length-1].getAttribute("data-injectcss"),d=function(c,l){l.parentNode.insertBefore(c,l)};if(i&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function s(){o||(o=!0,a())}function n(){try{h.documentElement.doScroll("left")}catch(c){return void setTimeout(n,50)}s()}l=function(){var c,l;(l=document.createElement("div")).innerHTML=e,e=null,(c=l.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",l=c,(c=document.body).firstChild?d(l,c.firstChild):c.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),l()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(a=l,h=c.document,o=!1,n(),h.onreadystatechange=function(){"complete"==h.readyState&&(h.onreadystatechange=null,s())})}(window); \ No newline at end of file +window._iconfont_svg_string_3335115='',function(c){var t=(t=document.getElementsByTagName("script"))[t.length-1],l=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var e,o,a,h,i,n=function(t,l){l.parentNode.insertBefore(t,l)};if(l&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}e=function(){var t,l=document.createElement("div");l.innerHTML=c._iconfont_svg_string_3335115,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(t=document.body).firstChild?n(l,t.firstChild):t.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(e,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),e()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(a=e,h=c.document,i=!1,s(),h.onreadystatechange=function(){"complete"==h.readyState&&(h.onreadystatechange=null,d())})}function d(){i||(i=!0,a())}function s(){try{h.documentElement.doScroll("left")}catch(t){return void setTimeout(s,50)}d()}}(window); \ No newline at end of file diff --git a/oafAdmin/src/assets/light.css b/oafAdmin/src/assets/light.css new file mode 100644 index 0000000..d1b64f7 --- /dev/null +++ b/oafAdmin/src/assets/light.css @@ -0,0 +1,31 @@ +/* + * light.css + * Copyright (C) 2022 veypi + * + * Distributed under terms of the Apache license. + */ + +:root { + transition: all 0.2s linear; + --base-color: #000; + --base-bg: #f5f5f5; + --base-bg-1: #e0e0e0; + --base-bg-2: #d0d0d0; + --base-bg-3: #c0c0c0; + + --header-bg: #d0d0d0; + + --L0: #18a058; + + --color-primary: #2196f3; + --color-secondary: #03a9f4; + --color-accent: #ff9800; + --color-error: #f44336; + --color-warning: #ff5722; + --color-info: #ffc107; + --color-success: #4caf50; + + --input-line-default: #002f55; + --input-line-shine: #1467ff; + --input-line-error: var(--color-error); +} diff --git a/oafAdmin/src/assets/mybar.css b/oafAdmin/src/assets/mybar.css new file mode 100644 index 0000000..ccc9aa8 --- /dev/null +++ b/oafAdmin/src/assets/mybar.css @@ -0,0 +1,31 @@ +.mybar { + overflow: auto; +} + +.mybar::-webkit-scrollbar { + width: 6px; + height: 6px; + color: transparent; +} + +.mybar::-webkit-scrollbar-track { + border-radius: 3px; +} + +.mybar::-webkit-scrollbar-thumb { + border-radius: 3px; + background-color: transparent; +} +.mybar:hover::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .2); +} + +.mybar:hover::-webkit-scrollbar-thumb { + /*background: linear-gradient(to top, #fcf5ee, #faf7e6, #73d2f3, #eeeeee);*/ + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .8); +} + + +.mybar::-webkit-scrollbar-track-piece { + background: transparent; +} diff --git a/oafAdmin/src/auth/index.ts b/oafAdmin/src/auth/index.ts new file mode 100644 index 0000000..db83cc7 --- /dev/null +++ b/oafAdmin/src/auth/index.ts @@ -0,0 +1,74 @@ +import { modelsSimpleAuth } from '@/models' + +export const R = { + // 应用管理配置权限 + App: 'app', + // 用户管理和绑定应用权限 + User: 'user', + // 权限资源定义权限 + Resource: 'resource', + // 角色管理和绑定用户权限 + Role: 'role', + // 权限管理和绑定角色权限 + Auth: 'auth', +} + +const level = { + None: 0, + Do: 1, + Part: 1, + Read: 2, + Create: 3, + Update: 4, + Delete: 5, + All: 6 +} + +class authLevel { + level = level.None + constructor(level: number) { + this.level = level + } + CanDo(): boolean { + return this.level >= level.Do + } + CanRead(): boolean { + return this.level >= level.Read + } + CanCreate(): boolean { + return this.level >= level.Create + } + CanUpdate(): boolean { + return this.level >= level.Update + } + CanDelete(): boolean { + return this.level >= level.Delete + } + CanDoAny(): boolean { + return this.level >= level.All + } +} + +export class Auths { + readonly list: modelsSimpleAuth[] + + constructor(auths: modelsSimpleAuth[]) { + this.list = auths + } + + Get(name: string, rid: string): authLevel { + let l = level.None + for (let i of this.list) { + if (i.name == name && (!i.rid || i.rid === rid) && i.level > l) { + l = i.level + } + } + console.log(l) + return new authLevel(l) + } +} + + +export function NewAuths(a: modelsSimpleAuth[]) { + return new Auths(a) +} diff --git a/oafAdmin/src/components/WxLogin.vue b/oafAdmin/src/components/WxLogin.vue new file mode 100644 index 0000000..338c6a9 --- /dev/null +++ b/oafAdmin/src/components/WxLogin.vue @@ -0,0 +1,33 @@ + + + diff --git a/oafAdmin/src/components/app.vue b/oafAdmin/src/components/app.vue new file mode 100644 index 0000000..5120280 --- /dev/null +++ b/oafAdmin/src/components/app.vue @@ -0,0 +1,74 @@ + + + diff --git a/oafAdmin/src/components/connectors/roleauths.vue b/oafAdmin/src/components/connectors/roleauths.vue new file mode 100644 index 0000000..9171378 --- /dev/null +++ b/oafAdmin/src/components/connectors/roleauths.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/oafAdmin/src/components/connectors/roleusers.vue b/oafAdmin/src/components/connectors/roleusers.vue new file mode 100644 index 0000000..3ced962 --- /dev/null +++ b/oafAdmin/src/components/connectors/roleusers.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/oafAdmin/src/components/editor/resource.vue b/oafAdmin/src/components/editor/resource.vue new file mode 100644 index 0000000..980685a --- /dev/null +++ b/oafAdmin/src/components/editor/resource.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/oafAdmin/src/components/editor/role.vue b/oafAdmin/src/components/editor/role.vue new file mode 100644 index 0000000..c67d4f6 --- /dev/null +++ b/oafAdmin/src/components/editor/role.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/oafAdmin/src/components/frame.vue b/oafAdmin/src/components/frame.vue index 5cb8533..ae913d4 100644 --- a/oafAdmin/src/components/frame.vue +++ b/oafAdmin/src/components/frame.vue @@ -21,7 +21,7 @@ leave-active-class="animate__slideOutUp" >
-
- 统一认证系统 +
+
+ 统一认证系统
{{ app.title }} @@ -46,8 +47,8 @@
fullscreen
- - {{ app.isDark ? 'Daytimemode' : 'nightmode-fill' }} + + {{ app.isDark ? 'Daytimemode-fill' : 'night' }}
@@ -60,14 +61,14 @@
-
+
关于OA @@ -90,6 +91,9 @@ let isFullScreen = false + diff --git a/oafAdmin/src/components/myinput/index.ts b/oafAdmin/src/components/myinput/index.ts new file mode 100644 index 0000000..b0e3a19 --- /dev/null +++ b/oafAdmin/src/components/myinput/index.ts @@ -0,0 +1,9 @@ +/* +* @name: index +* @author: veypi +* @date: 2022-04-03 14:56 +* @description:index +*/ + +import index from './index.vue' +export default index diff --git a/oafAdmin/src/components/myinput/index.vue b/oafAdmin/src/components/myinput/index.vue new file mode 100644 index 0000000..1eed12c --- /dev/null +++ b/oafAdmin/src/components/myinput/index.vue @@ -0,0 +1,411 @@ + + + + + diff --git a/oafAdmin/src/components/siderframe.vue b/oafAdmin/src/components/siderframe.vue new file mode 100644 index 0000000..ca50356 --- /dev/null +++ b/oafAdmin/src/components/siderframe.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/oafAdmin/src/components/uploader/index.ts b/oafAdmin/src/components/uploader/index.ts new file mode 100644 index 0000000..7eada7d --- /dev/null +++ b/oafAdmin/src/components/uploader/index.ts @@ -0,0 +1,9 @@ +/* +* @name: index +* @author: veypi +* @date: 2021-12-04 14:32 +* @description:index +*/ +import upload from './uploader.vue' + +export default upload diff --git a/oafAdmin/src/components/uploader/uploader.vue b/oafAdmin/src/components/uploader/uploader.vue new file mode 100644 index 0000000..f5c6c89 --- /dev/null +++ b/oafAdmin/src/components/uploader/uploader.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/oafAdmin/src/components/userSelect.vue b/oafAdmin/src/components/userSelect.vue new file mode 100644 index 0000000..dc9b757 --- /dev/null +++ b/oafAdmin/src/components/userSelect.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/oafAdmin/src/components/vavator/index.ts b/oafAdmin/src/components/vavator/index.ts new file mode 100644 index 0000000..e21ed1f --- /dev/null +++ b/oafAdmin/src/components/vavator/index.ts @@ -0,0 +1,10 @@ +/* + * index.ts + * Copyright (C) 2022 veypi + * 2022-08-15 19:56 + * Distributed under terms of the Apache license. + */ + + +import main from './index.vue' +export default main diff --git a/oafAdmin/src/components/vavator/index.vue b/oafAdmin/src/components/vavator/index.vue new file mode 100644 index 0000000..7430967 --- /dev/null +++ b/oafAdmin/src/components/vavator/index.vue @@ -0,0 +1,39 @@ + + + + + + + diff --git a/oafAdmin/src/components/vmodal/index.ts b/oafAdmin/src/components/vmodal/index.ts new file mode 100644 index 0000000..b84069b --- /dev/null +++ b/oafAdmin/src/components/vmodal/index.ts @@ -0,0 +1,9 @@ +/* + * index.ts + * Copyright (C) 2022 veypi + * 2022-12-20 23:44 + * Distributed under terms of the Apache license. + */ + +import modal from './index.vue' +export default modal diff --git a/oafAdmin/src/components/vmodal/index.vue b/oafAdmin/src/components/vmodal/index.vue new file mode 100644 index 0000000..eda8dca --- /dev/null +++ b/oafAdmin/src/components/vmodal/index.vue @@ -0,0 +1,50 @@ + + + + + + diff --git a/oafAdmin/src/i18n/en.ts b/oafAdmin/src/i18n/en.ts new file mode 100644 index 0000000..7b04f89 --- /dev/null +++ b/oafAdmin/src/i18n/en.ts @@ -0,0 +1,33 @@ +/* + * en.ts + * Copyright (C) 2022 veypi + * 2022-08-15 17:53 + * Distributed under terms of the Apache license. + */ + + + +export default { + a: { + username: 'username', + password: 'password', + repeat_pass: 'repeat', + register: 'register', + login: 'login', + success: 'success', + failed: 'failed', + account: "account", + nickname: "nickname", + email: "email", + logout: "logout", + user: "user", + setting: "setting", + }, + m: { + home: "home", + editor: "editor", + }, + msg: { + e1: 'please conform your form', + } +} diff --git a/oafAdmin/src/i18n/index.ts b/oafAdmin/src/i18n/index.ts new file mode 100644 index 0000000..bcee70c --- /dev/null +++ b/oafAdmin/src/i18n/index.ts @@ -0,0 +1,26 @@ +/* + * index.ts + * Copyright (C) 2022 veypi + * 2022-08-15 17:39 + * Distributed under terms of the Apache license. + */ + + +import util from '@/libs/util'; +import { createI18n, useI18n } from 'vue-i18n' +// import { createI18n, useI18n } from 'vue-i18n/dist/vue-i18n.esm-bundler.js' +import en from './en' +import zh from './zh' + +const messages = { + en, zh +} + +export { useI18n } +export default createI18n({ + locale: util.getCookie('language') || 'zh', + legacy: false, + silentTranslationWarn: true, + fallbackLocale: 'en', // set fallback locale + messages, +}) diff --git a/oafAdmin/src/i18n/zh.ts b/oafAdmin/src/i18n/zh.ts new file mode 100644 index 0000000..a78a53e --- /dev/null +++ b/oafAdmin/src/i18n/zh.ts @@ -0,0 +1,33 @@ +/* + * zh.ts + * Copyright (C) 2022 veypi + * 2022-08-15 17:53 + * Distributed under terms of the Apache license. + */ + + +export default { + a: { + username: '用户名', + password: '密码', + repeat_pass: '重复密码', + register: '注册', + login: '登录', + success: '成功', + failed: '失败', + account: "我的账户", + nickname: "昵称", + email: "邮箱", + logout: "注销", + user: "用户", + setting: "设置", + }, + m: { + home: "首页", + editor: "编辑", + }, + msg: { + e1: '请完善表单', + }, + 'no access': '无权访问', +} diff --git a/oafAdmin/src/index.css b/oafAdmin/src/index.css index 5b58993..d8ec22f 100644 --- a/oafAdmin/src/index.css +++ b/oafAdmin/src/index.css @@ -1,14 +1,39 @@ +@import "./assets/mybar.css"; +@import "./assets/light.css"; +@import "./assets/dark.css"; + @tailwind base; @tailwind components; @tailwind utilities; -:root { +.animate__400ms { + --animate-duration: 400ms; +} + +.div-center { + @apply flex justify-center items-center; +} + +.div-btn { + @apply cursor-pointer transition duration-500 ease-in-out transform hover:scale-110 hover:opacity-50; +} + +.div-btn hr { + right: 50%; + position: absolute; + bottom: 1px; + width: 0; + border: var(--color-primary) solid 1px; + visibility: hidden; transition: all 0.2s linear; - --base-color: #000; - --base-bg: #fff; } -:root[theme=dark] { - --base-color: #fff; - --base-bg: #000; +.div-btn:hover { + /*border-bottom: #03a9f4 1px solid;*/ +} + +.div-btn:hover hr { + visibility: visible; + width: 80%; + right: 10%; } diff --git a/oafAdmin/src/main.ts b/oafAdmin/src/main.ts index 3376ffa..cd21897 100644 --- a/oafAdmin/src/main.ts +++ b/oafAdmin/src/main.ts @@ -3,12 +3,15 @@ import App from './App.vue' import OneIcon from '@veypi/one-icon' import router from '@/router' import { createPinia } from 'pinia' +import i18n from '@/i18n' import 'animate.css' import './assets/icon.js' +import './msg/index.css' import './index.css' let app = createApp(App) app.use(OneIcon) app.use(router) +app.use(i18n) app.use(createPinia()) app.mount('#app') diff --git a/oafAdmin/src/models/index.ts b/oafAdmin/src/models/index.ts new file mode 100644 index 0000000..7ed466e --- /dev/null +++ b/oafAdmin/src/models/index.ts @@ -0,0 +1,151 @@ +/* +* @name: index +* @author: veypi +* @date: 2021-11-18 17:36 +* @description:index +*/ + +export type Dict = { [key: string]: any } + +export enum ArgType { + Text = 'text', + Password = 'password', + Bool = 'bool', + Select = 'select', + Radio = 'radio', + Number = 'number', + Region = 'region', + NumList = 'numList', + StrList = 'strList', + Table = 'table', + Grid = 'grid', + File = 'file', + Img = 'img' +} + +export const ArgTypesTrans = { + [ArgType.Text]: '文本', + [ArgType.Password]: '密码', + [ArgType.Select]: '选择器', + [ArgType.Radio]: '单选框', + [ArgType.Number]: '数字', + [ArgType.Region]: '区间', + [ArgType.NumList]: '数组', + [ArgType.StrList]: '文本集合', + [ArgType.Table]: '表格', + [ArgType.Grid]: '矩阵', + [ArgType.File]: '文件', + [ArgType.Img]: '图片', + [ArgType.Bool]: '开关', +} +export interface modelsBread { + Index: number + Name: string + Type?: string + RName: string + RParams?: any + RQuery?: any +} + +export interface modelsApp { + created: string + updated: string + delete_flag: boolean + des: string + hide: boolean + icon: string + id: string + name: string + redirect: string + role_id: string + status: number + user_count: number + + // Creator: number + // Des: string + // EnableEmail: boolean + // EnablePhone: boolean + // EnableRegister: true + // EnableUser: boolean + // EnableUserKey: boolean + // EnableWx: boolean + // Hide: boolean + // Host: string + // Icon: string + // InitRole?: null + // InitRoleID: number + // Name: string + // UUID: string + // UserCount: number + // UserKeyUrl: string + // UserRefreshUrl: string + // UserStatus: string + // Users: null +} + +export interface modelsUser { + // Index 前端缓存 + Index?: number + Apps: modelsApp[] + Auths: null + CreatedAt: string + DeletedAt: null + ID: number + Icon: string + Position: string + Roles: null + Status: string + UpdatedAt: string + Username: string + Email: string + Nickname: string + Phone: string +} + +export interface modelsSimpleAuth { + level: number + name: string + rid: string +} + +export interface modelsAuth { + App?: modelsApp + AppUUID: string + CreatedAt: string + DeletedAt: null + ID: number + Level: number + RID: string + RUID: string + Resource?: modelsResource + ResourceID: number + Role?: modelsRole + RoleID: number + UpdatedAt: string + User?: modelsUser + UserID?: number +} + +export interface modelsRole { + App?: modelsApp + AppUUID: string + Auths: null + CreatedAt: string + DeletedAt: null + ID: number + Name: string + Tag: string + UpdatedAt: string + UserCount: number +} + +export interface modelsResource { + App?: modelsApp + AppUUID: string + CreatedAt: string + DeletedAt: null + Des: string + ID: number + Name: string + UpdatedAt: string +} diff --git a/oafAdmin/src/msg/index.css b/oafAdmin/src/msg/index.css new file mode 100644 index 0000000..d64f00a --- /dev/null +++ b/oafAdmin/src/msg/index.css @@ -0,0 +1,135 @@ +/* + * index.css + * Copyright (C) 2022 veypi + * + * Distributed under terms of the MIT license. + */ + +:root { + --msg-box-color: #fff; + --msg-box-info: #0a0; + --msg-box-warn: #f90; + --z-index: 99999; + --font-size: 1rem; +} + +.v-msg-box { + color: var(--msg-box-color); + position: fixed; + z-index: var(--z-index); + left: 0; + top: 0; + padding-top: 5vh; + width: 100vw; + height: 100vh; + color: var(--msg-box-color); + font-size: var(--font-size); + line-height: var(--font-size); + display: flex; + flex-direction: column; + justify-content: flex-start; + user-select: none; +} + +.v-msg-box-mask { + background: rgba(0, 0, 0, 0.2); + /* filter: blur(6px); */ +} + +.v-msg-box-none-event { + pointer-events: none; +} + +.v-msg-item { + min-width: 10rem; + margin: 0 auto; + text-align: center; + padding: 0.5rem 2rem; + border-radius: 0.5rem; + margin-top: 0.5rem; + transition: filter 0.2s; + animation: item-show 0.4s 1; +} + +.v-msg-item:hover { + filter: brightness(80%); +} + +.v-msg-item-remove { + animation: item-remove 0.4s 1; +} + +.v-msg-prompt { + margin: 0 auto; + text-align: center; + padding: 2rem 2rem; + border-radius: 0.5rem; + margin-top: 0.5rem; + transition: filter 0.2s; + /* animation: item-show 0.4s 0; */ + + background: #e9e9e9; + height: 12rem; + width: 25rem; + color: #000; +} + +.v-msg-prompt .v-msg-title { + text-align: left; + font-size: 1.2rem; +} +.v-msg-prompt .v-msg-input { + background: none; + margin: 1rem; + font-size: 1.2rem; + color: #000; + width: 100%; + height: 3rem; + outline: none; + border-bottom: 2px solid #000; + border-top: 0px; + border-left: 0px; + border-right: 0px; +} +.v-msg-prompt .v-msg-ok { + width: 6rem; + height: 2rem; + line-height: 2rem; + font-size: 1.2rem; + float: right; + border-radius: 4px; + background: #4caf50; +} + +.v-msg-info { + background: var(--msg-box-info); +} +.v-msg-warn { + background: var(--msg-box-warn); +} + +@keyframes item-show { + from { + opacity: 0; + margin-top: 2.5rem; + transform: scaleX(0); + } + to { + opacity: 1; + margin-top: 0.5rem; + transform: scaleX(1); + } +} + +@keyframes item-remove { + from { + opacity: 1; + margin-top: 0.5rem; + transform: scaleX(1); + } + to { + opacity: 0; + margin-top: -2.5rem; + transform: scaleX(0); + } +} diff --git a/oafAdmin/src/msg/index.ts b/oafAdmin/src/msg/index.ts new file mode 100644 index 0000000..c06ed70 --- /dev/null +++ b/oafAdmin/src/msg/index.ts @@ -0,0 +1,110 @@ +/* + * index.ts + * Copyright (C) 2022 veypi + * 2022-05-27 18:06 + * Distributed under terms of the MIT license. + */ + + +class Message { + + private box: HTMLDivElement + private timeout: number + private id: string + + constructor(id: string, timeout?: number) { + this.timeout = timeout || 1000 + this.box = document.getElementById(id) as HTMLDivElement + this.id = id + if (!this.box) { + console.error('can not found element ' + id) + return + } + this.box.classList.add('v-msg-box') + this.box.classList.add('v-msg-box-none-event') + } + private base(text: string, classList: string[], timeout: number) { + let msg = document.createElement('div') + msg.classList.add('v-msg-item') + msg.classList.add(...classList) + msg.innerText = text + this.box.appendChild(msg) + setTimeout(() => { + msg.classList.add('v-msg-item-remove') + msg.onanimationend = () => { + this.box.removeChild(msg) + } + }, timeout) + } + Prompt(text: string, placeholder?: string, defaul?: string) { + let that = this + this.box.classList.remove('v-msg-box-none-event') + this.box.classList.add('v-msg-box-mask') + return new Promise(function(resolve, reject) { + let msg = document.createElement('div') + let title = createElement('div', ['v-msg-title']) + let input = createElement('input', ['v-msg-input']) as HTMLInputElement + input.placeholder = placeholder || '' + + let btn = createElement('div', []) + let btn_ok = createElement('div', ['v-msg-ok']) + input.value = defaul || '' + btn_ok.innerText = "ok" + title.innerText = text + msg.classList.add('v-msg-prompt') + msg.appendChild(title) + msg.appendChild(input) + btn.appendChild(btn_ok) + msg.appendChild(btn) + that.box.appendChild(msg) + let cancel = () => { + msg.classList.add('v-msg-item-remove') + that.box.classList.add('v-msg-box-none-event') + that.box.classList.remove('v-msg-box-mask') + msg.onanimationend = () => { + that.box.removeChild(msg) + } + } + btn_ok.onclick = () => { + cancel() + resolve(input.value) + } + that.box.onclick = (e: any) => { + if (e.target.id === that.id) { + cancel() + reject() + } + } + }).catch(e => { + if (e) { + console.log(e) + } + }) + } + Warn(text: string) { + this.base(text, ['v-msg-warn'], this.timeout + 1500) + } + Info(text: string) { + this.base(text, ['v-msg-info'], this.timeout + 500) + } +} + +function createElement(name: string, classList: string[]) { + let d = document.createElement(name) + d.classList.add(...classList) + return d +} + +export { Message } + +let msg: Message + +function defaultMessage() { + console.log('init message') + if (!msg) { + msg = new Message('v-msg') + } + return msg +} + +export default defaultMessage() diff --git a/oafAdmin/src/router/index.ts b/oafAdmin/src/router/index.ts index f712a70..885ec69 100644 --- a/oafAdmin/src/router/index.ts +++ b/oafAdmin/src/router/index.ts @@ -6,13 +6,20 @@ */ -import {createRouter, createWebHistory} from 'vue-router' +import { createRouter, createWebHistory } from 'vue-router' +import { Auths, R } from '@/auth' +import { useAppStore } from '@/store/app' +import util from '@/libs/util' +import { useUserStore } from '@/store/user' +import msg from '@/msg' + declare module 'vue-router' { interface RouteMeta { title?: string + isAdmin?: boolean requiresAuth: boolean - checkAuth?: (r?: RouteLocationNormalized) => boolean + checkAuth?: (a: Auths, r?: RouteLocationNormalized) => boolean } } const router = createRouter({ @@ -21,7 +28,90 @@ const router = createRouter({ { path: '/', name: 'home', - component: () => import('../views/home.vue'), + meta: { + requiresAuth: true, + }, + component: () => import('@/views/home.vue'), + }, + { + path: '/app/:uuid?', + component: () => import('@/views/app.vue'), + redirect: { name: 'app.main' }, + children: [ + { + path: 'main', + name: 'app.main', + meta: { + title: '首页', + requiresAuth: true, + }, + component: () => import('@/views/app/main.vue'), + }, + { + path: 'users', + name: 'app.users', + meta: { + title: '用户', + requiresAuth: true, + checkAuth: (a, r) => { + return a.Get(R.User, r?.params.uuid as string).CanRead() + }, + }, + component: () => import('@/views/app/users.vue'), + }, + { + path: 'roles', + name: 'app.roles', + meta: { + title: '权限', + requiresAuth: true, + checkAuth: (a, r) => { + return a.Get(R.Role, r?.params.uuid as string).CanRead() + }, + }, + component: () => import('@/views/app/roles.vue'), + }, + { + path: 'setting', + name: 'app.setting', + meta: { + title: '应用设置', + requiresAuth: true, + checkAuth: (a, r) => { + return a.Get(R.App, r?.params.uuid as string).CanUpdate() + }, + }, + component: () => import('@/views/app/setting.vue'), + }, + ], + }, + { + path: '/user/setting', + name: 'user_setting', + meta: { + requiresAuth: true, + }, + component: () => import('@/views/user_setting.vue'), + }, + { + path: '/about', + name: 'about', + component: () => import('@/views/about.vue'), + }, + { + path: '/wx', + name: 'wx', + component: () => import('@/views/wx.vue'), + }, + { + path: '/login/:uuid?', + name: 'login', + component: () => import('@/views/login.vue'), + }, + { + path: '/register/:uuid?', + name: 'register', + component: () => import('@/views/register.vue'), }, { path: '/:path(.*)', @@ -31,12 +121,30 @@ const router = createRouter({ ], }) +let user: any = null router.beforeEach((to, from) => { - if (to.meta.requiresAuth && to.meta.checkAuth) { + // to.matched.some(record => record.meta.requiresAuth) + if (to.query.noh === '1') { + let app = useAppStore() + app.hideHeader = true + } + if (to.meta.requiresAuth && !util.checkLogin()) { + // 此路由需要授权,请检查是否已登录 + // 如果没有,则重定向到登录页面 return { name: 'login', // 保存我们所在的位置,以便以后再来 - query: {redirect: to.fullPath}, + query: { redirect: to.fullPath }, + } + } + if (to.meta.checkAuth) { + if (!user) { + user = useUserStore() + } + if (!to.meta.checkAuth(user.auth, to)) { + + msg.Warn('无权访问') + return from } } }) diff --git a/oafAdmin/src/store/app.ts b/oafAdmin/src/store/app.ts index 0d3cc6c..e229178 100644 --- a/oafAdmin/src/store/app.ts +++ b/oafAdmin/src/store/app.ts @@ -5,16 +5,30 @@ * @description:user */ +import { modelsBread } from '@/models' import { defineStore } from 'pinia' export const useAppStore = defineStore('app', { state: () => { return { + id: '', hideHeader: false, title: '', isDark: false, + breads: [] as modelsBread[], } }, actions: { + toggle_theme() { + this.isDark = !this.isDark + document.documentElement.setAttribute('theme', this.isDark ? 'dark' : '') + }, + setBreads(b: modelsBread) { + let l = this.breads.length + for (let i = l; i < b.Index; i++) { + this.breads.push({} as modelsBread) + } + this.breads[b.Index] = b + }, }, }) diff --git a/oafAdmin/src/store/index.ts b/oafAdmin/src/store/index.ts new file mode 100644 index 0000000..c17fa2a --- /dev/null +++ b/oafAdmin/src/store/index.ts @@ -0,0 +1,11 @@ +/* + * index.ts + * Copyright (C) 2022 veypi + * 2022-12-20 18:15 + * Distributed under terms of the Apache license. + */ + +import { useAppStore } from './app' +import { useUserStore } from './user' + +export { useAppStore, useUserStore } diff --git a/oafAdmin/src/store/user.ts b/oafAdmin/src/store/user.ts index 3006446..dbe0a36 100644 --- a/oafAdmin/src/store/user.ts +++ b/oafAdmin/src/store/user.ts @@ -5,12 +5,20 @@ * @description:user */ -import {defineStore} from 'pinia' +import api from '@/api' +import { Auths, NewAuths } from '@/auth' +import { util } from '@/libs' +import { modelsUser } from '@/models' +import { Base64 } from 'js-base64' +import router from "@/router"; +import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => { return { id: 0, + auth: {} as Auths, + local: {} as modelsUser, username: '', } }, @@ -19,5 +27,27 @@ export const useUserStore = defineStore('user', { this.id = 1 this.username = 'admin' }, + fetchUserData() { + let token = util.getToken()?.split('.'); + if (!token || token.length !== 3) { + return false + } + let data = JSON.parse(Base64.decode(token[1])) + console.log(data) + if (data.id) { + this.auth = NewAuths(data.access) + console.log(this.auth) + api.user.get(data.id).Start(e => { + console.log(e) + this.id = e.id + }, e => { + this.logout() + }) + } + }, + logout() { + localStorage.removeItem('auth_token') + router.push({ name: 'login' }) + } }, }) diff --git a/oafAdmin/src/views/404.vue b/oafAdmin/src/views/404.vue index c01ae34..21f286c 100644 --- a/oafAdmin/src/views/404.vue +++ b/oafAdmin/src/views/404.vue @@ -1,16 +1,24 @@ - + + diff --git a/oafAdmin/src/views/about.vue b/oafAdmin/src/views/about.vue new file mode 100644 index 0000000..a4aa17e --- /dev/null +++ b/oafAdmin/src/views/about.vue @@ -0,0 +1,12 @@ + + + + + diff --git a/oafAdmin/src/views/app.vue b/oafAdmin/src/views/app.vue new file mode 100644 index 0000000..74a5271 --- /dev/null +++ b/oafAdmin/src/views/app.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/oafAdmin/src/views/app/main.vue b/oafAdmin/src/views/app/main.vue new file mode 100644 index 0000000..e636163 --- /dev/null +++ b/oafAdmin/src/views/app/main.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/oafAdmin/src/views/app/roles.vue b/oafAdmin/src/views/app/roles.vue new file mode 100644 index 0000000..9ae6681 --- /dev/null +++ b/oafAdmin/src/views/app/roles.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/oafAdmin/src/views/app/setting.vue b/oafAdmin/src/views/app/setting.vue new file mode 100644 index 0000000..d60d03a --- /dev/null +++ b/oafAdmin/src/views/app/setting.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/oafAdmin/src/views/app/users.vue b/oafAdmin/src/views/app/users.vue new file mode 100644 index 0000000..dccf57d --- /dev/null +++ b/oafAdmin/src/views/app/users.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/oafAdmin/src/views/demo.vue b/oafAdmin/src/views/demo.vue index 28d32c1..cbc8f2b 100644 --- a/oafAdmin/src/views/demo.vue +++ b/oafAdmin/src/views/demo.vue @@ -1,6 +1,10 @@ - + + + diff --git a/oafAdmin/src/views/file.vue b/oafAdmin/src/views/file.vue new file mode 100644 index 0000000..cbc8f2b --- /dev/null +++ b/oafAdmin/src/views/file.vue @@ -0,0 +1,10 @@ + + + + + diff --git a/oafAdmin/src/views/home.vue b/oafAdmin/src/views/home.vue index 5023f43..2300252 100644 --- a/oafAdmin/src/views/home.vue +++ b/oafAdmin/src/views/home.vue @@ -1,7 +1,142 @@ - + + + diff --git a/oafAdmin/src/views/login.vue b/oafAdmin/src/views/login.vue new file mode 100644 index 0000000..aa55b69 --- /dev/null +++ b/oafAdmin/src/views/login.vue @@ -0,0 +1,155 @@ + + + + diff --git a/oafAdmin/src/views/register.vue b/oafAdmin/src/views/register.vue new file mode 100644 index 0000000..3705b5c --- /dev/null +++ b/oafAdmin/src/views/register.vue @@ -0,0 +1,94 @@ + + + + diff --git a/oafAdmin/src/views/user_setting.vue b/oafAdmin/src/views/user_setting.vue new file mode 100644 index 0000000..f02da78 --- /dev/null +++ b/oafAdmin/src/views/user_setting.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/oafAdmin/src/views/wx.vue b/oafAdmin/src/views/wx.vue new file mode 100644 index 0000000..814fa38 --- /dev/null +++ b/oafAdmin/src/views/wx.vue @@ -0,0 +1,51 @@ + + + diff --git a/oafAdmin/vite.config.ts b/oafAdmin/vite.config.ts index d77a274..fefc9b7 100644 --- a/oafAdmin/vite.config.ts +++ b/oafAdmin/vite.config.ts @@ -1,6 +1,6 @@ -import {defineConfig} from 'vite' +import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' -import {resolve} from 'path' +import { resolve } from 'path' // https://vitejs.dev/config/ export default defineConfig({ @@ -14,12 +14,12 @@ export default defineConfig({ port: 3000, proxy: { '/api': { - target: 'http://127.0.0.1:4000/', + target: 'http://127.0.0.1:4001/', changeOrigin: true, ws: true, }, '/media': { - target: 'http://127.0.0.1:4000/', + target: 'http://127.0.0.1:4001/', changeOrigin: true, ws: true, }, diff --git a/oafAdmin/yarn.lock b/oafAdmin/yarn.lock index 25ce594..2a14d34 100644 --- a/oafAdmin/yarn.lock +++ b/oafAdmin/yarn.lock @@ -12,6 +12,44 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== +"@intlify/core-base@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.2.2.tgz#5353369b05cc9fe35cab95fe20afeb8a4481f939" + integrity sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA== + dependencies: + "@intlify/devtools-if" "9.2.2" + "@intlify/message-compiler" "9.2.2" + "@intlify/shared" "9.2.2" + "@intlify/vue-devtools" "9.2.2" + +"@intlify/devtools-if@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/devtools-if/-/devtools-if-9.2.2.tgz#b13d9ac4b4e2fe6d2e7daa556517a8061fe8bd39" + integrity sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg== + dependencies: + "@intlify/shared" "9.2.2" + +"@intlify/message-compiler@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.2.2.tgz#e42ab6939b8ae5b3d21faf6a44045667a18bba1c" + integrity sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA== + dependencies: + "@intlify/shared" "9.2.2" + source-map "0.6.1" + +"@intlify/shared@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.2.2.tgz#5011be9ca2b4ab86f8660739286e2707f9abb4a5" + integrity sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q== + +"@intlify/vue-devtools@9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz#b95701556daf7ebb3a2d45aa3ae9e6415aed8317" + integrity sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg== + dependencies: + "@intlify/core-base" "9.2.2" + "@intlify/shared" "9.2.2" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -126,7 +164,7 @@ "@vue/compiler-dom" "3.2.45" "@vue/shared" "3.2.45" -"@vue/devtools-api@^6.4.5": +"@vue/devtools-api@^6.2.1", "@vue/devtools-api@^6.4.5": version "6.4.5" resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz#d54e844c1adbb1e677c81c665ecef1a2b4bb8380" integrity sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ== @@ -216,6 +254,11 @@ arg@^5.0.2: resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + autoprefixer@^10.4.4: version "10.4.13" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" @@ -228,11 +271,37 @@ autoprefixer@^10.4.4: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +axios@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.1.tgz#44cf04a3c9f0c2252ebd85975361c026cb9f864a" + integrity sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a" + integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -260,6 +329,11 @@ caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz#ab7371faeb4adff4b74dad1718a6fd122e45d9cb" integrity sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A== +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -280,6 +354,25 @@ color-name@^1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +copy-anything@^2.0.1: + version "2.0.6" + resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480" + integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw== + dependencies: + is-what "^3.14.1" + +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -290,11 +383,23 @@ csstype@^2.6.8: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== +debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + defined@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + detective@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" @@ -319,6 +424,13 @@ electron-to-chromium@^1.4.251: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +errno@^0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + esbuild-android-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" @@ -467,6 +579,13 @@ fast-glob@^3.2.12: merge2 "^1.3.0" micromatch "^4.0.4" +fast-xml-parser@^3.19.0: + version "3.21.1" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736" + integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg== + dependencies: + strnum "^1.0.4" + fastq@^1.6.0: version "1.14.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.14.0.tgz#107f69d7295b11e0fccc264e1fc6389f623731ce" @@ -481,6 +600,20 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fraction.js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" @@ -510,6 +643,11 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +graceful-fs@^4.1.2: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -517,6 +655,28 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hot-patcher@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/hot-patcher/-/hot-patcher-0.5.0.tgz#9d401424585aaf3a91646b816ceff40eb6a916b9" + integrity sha512-2Uu2W0s8+dnqXzdlg0MRsRzPoDCs1wVjOGSyMRRaMzLDX4bgHw6xDYKccsWafXPPxQpkQfEjgW6+17pwcg60bw== + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +image-size@~0.5.0: + version "0.5.5" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" + integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -524,6 +684,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-buffer@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" @@ -548,6 +713,38 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-what@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" + integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA== + +js-base64@^3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.3.tgz#2e784bb0851636bf1e99ef12e4f3a8a8c9b7639f" + integrity sha512-PAr6Xg2jvd7MCR6Ld9Jg3BmTcjYsHEBx1VlwEwULb/qowPf5VD9kEMagj23Gm7JRnSvE/Da/57nChZjnvL8v6A== + +layerr@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/layerr/-/layerr-0.1.2.tgz#16c8e7fb042d3595ab15492bdad088f31d7afd15" + integrity sha512-ob5kTd9H3S4GOG2nVXyQhOu9O8nBgP555XxWPkJI0tR0JeRilfyTp8WtPdIJHLXBmHMSdEq5+KMxiYABeScsIQ== + +less@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/less/-/less-4.1.3.tgz#175be9ddcbf9b250173e0a00b4d6920a5b770246" + integrity sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA== + dependencies: + copy-anything "^2.0.1" + parse-node-version "^1.0.1" + tslib "^2.3.0" + optionalDependencies: + errno "^0.1.1" + graceful-fs "^4.1.2" + image-size "~0.5.0" + make-dir "^2.1.0" + mime "^1.4.1" + needle "^3.1.0" + source-map "~0.6.0" + lilconfig@^2.0.5, lilconfig@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" @@ -560,6 +757,23 @@ magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.8" +make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -573,6 +787,30 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +minimatch@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.2.tgz#0939d7d6f0898acbd1508abe534d1929368a8fff" + integrity sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" @@ -583,11 +821,30 @@ mitt@^3.0.0: resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + nanoid@^3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== +needle@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-3.2.0.tgz#07d240ebcabfd65c76c03afae7f6defe6469df44" + integrity sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.6.3" + sax "^1.2.4" + +nested-property@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/nested-property/-/nested-property-4.0.0.tgz#a67b5a31991e701e03cdbaa6453bc5b1011bb88d" + integrity sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA== + node-releases@^2.0.6: version "2.0.8" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" @@ -608,11 +865,21 @@ object-hash@^3.0.0: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== +parse-node-version@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-posix@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-posix/-/path-posix-1.0.0.tgz#06b26113f56beab042545a23bfa88003ccac260f" + integrity sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -628,6 +895,11 @@ pify@^2.3.0: resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pinia@^2.0.13: version "2.0.28" resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.28.tgz#887c982d854972042d9bdfd5bc4fad3b9d6ab02a" @@ -689,6 +961,21 @@ postcss@^8.1.10, postcss@^8.4.12, postcss@^8.4.13, postcss@^8.4.18: picocolors "^1.0.0" source-map-js "^1.0.2" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -713,6 +1000,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve@^1.1.7, resolve@^1.22.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -741,12 +1033,32 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +seamless-scroll-polyfill@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/seamless-scroll-polyfill/-/seamless-scroll-polyfill-2.2.0.tgz#422f4a7d1b3a7d0ea86f8afad3d6abea2c66a000" + integrity sha512-c4KHfltYY8oFRt987Kl9i4xRLylIpg1YHWsOyH1kj4SP+W+8PtOiKrLyv8L6xbHvwQpofOZ98AWpGPEJK++udQ== + +semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map@^0.6.1: +source-map@0.6.1, source-map@^0.6.1, source-map@~0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -756,6 +1068,11 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +strnum@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -797,6 +1114,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tslib@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + typescript@^4.6.2: version "4.9.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" @@ -810,11 +1132,29 @@ update-browserslist-db@^1.0.9: escalade "^3.1.1" picocolors "^1.0.0" +url-join@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +validator@^13.7.0: + version "13.7.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" + integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== + vite@^2.9.2: version "2.9.15" resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.15.tgz#2858dd5b2be26aa394a283e62324281892546f0b" @@ -832,6 +1172,16 @@ vue-demi@*: resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99" integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A== +vue-i18n@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.2.2.tgz#aeb49d9424923c77e0d6441e3f21dafcecd0e666" + integrity sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ== + dependencies: + "@intlify/core-base" "9.2.2" + "@intlify/shared" "9.2.2" + "@intlify/vue-devtools" "9.2.2" + "@vue/devtools-api" "^6.2.1" + vue-router@4: version "4.1.6" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.1.6.tgz#b70303737e12b4814578d21d68d21618469375a1"