From 5ae9c6eb502cf83766532755e7af82ac57b571af Mon Sep 17 00:00:00 2001 From: veypi Date: Fri, 6 Oct 2023 15:28:11 +0800 Subject: [PATCH] remove oaer and fix cors permissive --- oab/src/libs/cors.rs | 70 +++++ oab/src/libs/fs.rs | 76 ++++- oab/src/libs/mod.rs | 66 +--- oab/src/main.rs | 39 ++- oaweb/quasar.config.js | 3 + oaweb/src/boot/pack.ts | 4 +- oaweb/src/layouts/MainLayout.vue | 2 +- oaweb/src/oaer/api/app.ts | 48 --- oaweb/src/oaer/api/axios.ts | 86 ------ oaweb/src/oaer/api/index.ts | 18 -- oaweb/src/oaer/api/setting.ts | 28 -- oaweb/src/oaer/api/user.ts | 44 --- oaweb/src/oaer/assets/icon.js | 1 - oaweb/src/oaer/components/app.vue | 26 -- oaweb/src/oaer/components/file.vue | 47 --- oaweb/src/oaer/components/user.vue | 7 - oaweb/src/oaer/evt/index.ts | 12 - oaweb/src/oaer/frame.vue | 90 ------ oaweb/src/oaer/index.ts | 14 - oaweb/src/oaer/libs/webdav/auth/basic.ts | 7 - oaweb/src/oaer/libs/webdav/auth/digest.ts | 82 ----- oaweb/src/oaer/libs/webdav/auth/index.ts | 36 --- oaweb/src/oaer/libs/webdav/auth/oauth.ts | 5 - .../oaer/libs/webdav/compat/arrayBuffer.ts | 10 - oaweb/src/oaer/libs/webdav/compat/buffer.ts | 8 - oaweb/src/oaer/libs/webdav/compat/patcher.ts | 10 - oaweb/src/oaer/libs/webdav/factory.ts | 114 ------- oaweb/src/oaer/libs/webdav/index.ts | 5 - .../oaer/libs/webdav/operations/copyFile.ts | 26 -- .../libs/webdav/operations/createDirectory.ts | 81 ----- .../libs/webdav/operations/createStream.ts | 109 ------- .../libs/webdav/operations/customRequest.ts | 19 -- .../oaer/libs/webdav/operations/deleteFile.ts | 22 -- .../webdav/operations/directoryContents.ts | 78 ----- .../src/oaer/libs/webdav/operations/exists.ts | 18 -- .../libs/webdav/operations/getFileContents.ts | 102 ------- .../oaer/libs/webdav/operations/getQuota.ts | 30 -- oaweb/src/oaer/libs/webdav/operations/lock.ts | 79 ----- .../oaer/libs/webdav/operations/moveFile.ts | 26 -- .../libs/webdav/operations/putFileContents.ts | 94 ------ oaweb/src/oaer/libs/webdav/operations/stat.ts | 32 -- oaweb/src/oaer/libs/webdav/request.ts | 108 ------- oaweb/src/oaer/libs/webdav/response.ts | 46 --- oaweb/src/oaer/libs/webdav/tools/crypto.ts | 16 - oaweb/src/oaer/libs/webdav/tools/dav.ts | 171 ----------- oaweb/src/oaer/libs/webdav/tools/encode.ts | 24 -- oaweb/src/oaer/libs/webdav/tools/headers.ts | 18 -- oaweb/src/oaer/libs/webdav/tools/merge.ts | 62 ---- oaweb/src/oaer/libs/webdav/tools/path.ts | 36 --- oaweb/src/oaer/libs/webdav/tools/quota.ts | 22 -- oaweb/src/oaer/libs/webdav/tools/size.ts | 22 -- oaweb/src/oaer/libs/webdav/tools/url.ts | 32 -- oaweb/src/oaer/libs/webdav/tools/xml.ts | 55 ---- oaweb/src/oaer/libs/webdav/types.ts | 288 ------------------ oaweb/src/oaer/main.vue | 135 -------- oaweb/src/oaer/models/index.ts | 53 ---- oaweb/src/oaer/shims-vue.d.ts | 10 - oaweb/src/pages/IndexPage.vue | 2 +- oaweb/yarn.lock | 31 +- 59 files changed, 178 insertions(+), 2627 deletions(-) create mode 100644 oab/src/libs/cors.rs delete mode 100644 oaweb/src/oaer/api/app.ts delete mode 100644 oaweb/src/oaer/api/axios.ts delete mode 100644 oaweb/src/oaer/api/index.ts delete mode 100644 oaweb/src/oaer/api/setting.ts delete mode 100644 oaweb/src/oaer/api/user.ts delete mode 100644 oaweb/src/oaer/assets/icon.js delete mode 100644 oaweb/src/oaer/components/app.vue delete mode 100644 oaweb/src/oaer/components/file.vue delete mode 100644 oaweb/src/oaer/components/user.vue delete mode 100644 oaweb/src/oaer/evt/index.ts delete mode 100644 oaweb/src/oaer/frame.vue delete mode 100644 oaweb/src/oaer/index.ts delete mode 100644 oaweb/src/oaer/libs/webdav/auth/basic.ts delete mode 100644 oaweb/src/oaer/libs/webdav/auth/digest.ts delete mode 100644 oaweb/src/oaer/libs/webdav/auth/index.ts delete mode 100644 oaweb/src/oaer/libs/webdav/auth/oauth.ts delete mode 100644 oaweb/src/oaer/libs/webdav/compat/arrayBuffer.ts delete mode 100644 oaweb/src/oaer/libs/webdav/compat/buffer.ts delete mode 100644 oaweb/src/oaer/libs/webdav/compat/patcher.ts delete mode 100644 oaweb/src/oaer/libs/webdav/factory.ts delete mode 100644 oaweb/src/oaer/libs/webdav/index.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/copyFile.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/createDirectory.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/createStream.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/customRequest.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/deleteFile.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/directoryContents.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/exists.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/getFileContents.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/getQuota.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/lock.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/moveFile.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/putFileContents.ts delete mode 100644 oaweb/src/oaer/libs/webdav/operations/stat.ts delete mode 100644 oaweb/src/oaer/libs/webdav/request.ts delete mode 100644 oaweb/src/oaer/libs/webdav/response.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/crypto.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/dav.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/encode.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/headers.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/merge.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/path.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/quota.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/size.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/url.ts delete mode 100644 oaweb/src/oaer/libs/webdav/tools/xml.ts delete mode 100644 oaweb/src/oaer/libs/webdav/types.ts delete mode 100644 oaweb/src/oaer/main.vue delete mode 100644 oaweb/src/oaer/models/index.ts delete mode 100644 oaweb/src/oaer/shims-vue.d.ts diff --git a/oab/src/libs/cors.rs b/oab/src/libs/cors.rs new file mode 100644 index 0000000..5e1ef24 --- /dev/null +++ b/oab/src/libs/cors.rs @@ -0,0 +1,70 @@ +// +// cors.rs +// Copyright (C) 2023 veypi +// 2023-10-06 15:02 +// Distributed under terms of the MIT license. +// + +use std::future::{ready, Ready}; + +use actix_web::{ + dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, + Error, +}; +use futures_util::future::LocalBoxFuture; + +// There are two steps in middleware processing. +// 1. Middleware initialization, middleware factory gets called with +// next service in chain as parameter. +// 2. Middleware's call method gets called with normal request. +pub struct cors; + +// Middleware factory is `Transform` trait +// `S` - type of the next service +// `B` - type of response's body +impl Transform for cors +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = corsMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ready(Ok(corsMiddleware { service })) + } +} + +pub struct corsMiddleware { + service: S, +} + +impl Service for corsMiddleware +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type Future = LocalBoxFuture<'static, Result>; + + forward_ready!(service); + + fn call(&self, req: ServiceRequest) -> Self::Future { + println!("Hi from start. You requested: {}", req.path()); + + let fut = self.service.call(req); + + Box::pin(async move { + let res = fut.await?; + + println!("Hi from response"); + Ok(res) + }) + } +} diff --git a/oab/src/libs/fs.rs b/oab/src/libs/fs.rs index c879892..cd15fa3 100644 --- a/oab/src/libs/fs.rs +++ b/oab/src/libs/fs.rs @@ -18,7 +18,7 @@ use dav_server::{ DavConfig, DavHandler, }; -use http::Response; +use http::{header, Method, Response}; use http_auth_basic::Credentials; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use tracing::{info, warn}; @@ -34,6 +34,32 @@ pub fn core() -> DavHandler { .strip_prefix("/file/") .build_handler() } +/// Try to parse header value as HTTP method. +fn header_value_try_into_method(hdr: &header::HeaderValue) -> Option { + hdr.to_str() + .ok() + .and_then(|meth| Method::try_from(meth).ok()) +} + +fn is_request_preflight(req: &DavRequest) -> bool { + // check request method is OPTIONS + if req.request.method() != Method::OPTIONS { + return false; + } + + // check follow-up request method is present and valid + if req + .request + .headers() + .get(header::ACCESS_CONTROL_REQUEST_METHOD) + .and_then(header_value_try_into_method) + .is_none() + { + return false; + } + + true +} pub async fn dav_handler( req: DavRequest, @@ -41,8 +67,8 @@ pub async fn dav_handler( stat: web::Data, ) -> DavResponse { let root = stat.fs_root.clone(); - match handle_file(req, stat).await { - Ok((p, req)) => { + match handle_file(&req, stat).await { + Ok(p) => { let p = Path::new(&root).join(p); if !p.exists() { match fs::create_dir_all(p.clone()) { @@ -58,17 +84,41 @@ pub async fn dav_handler( } Err(e) => { warn!("handle file failed: {}", e); - Response::builder() - .status(401) - .header("WWW-Authenticate", "Basic realm=\"file\"") - .body(Body::from("please auth".to_string())) - .unwrap() - .into() + if is_request_preflight(&req) { + let origin = match req.request.headers().get("Origin") { + Some(o) => o.to_str().unwrap().clone(), + None => "", + }; + Response::builder() + .status(200) + .header("WWW-Authenticate", "Basic realm=\"file\"") + .header("Access-Control-Allow-Origin", origin) + .header("Access-Control-Allow-Credentials", "true") + .header("Access-Control-Allow-Headers", "auth_token, depth") + .header( + "Access-Control-Allow-Methods", + "OPTIONS, DELETE, GET, POST, PUT, HEAD, TRACE, PATCH, CONNECT, PROPFIND", + ) + .header( + "Access-Control-Expose-Headers", + "access-control-allow-origin, content-type", + ) + .body(Body::from("please auth".to_string())) + .unwrap() + .into() + } else { + Response::builder() + .status(401) + .header("WWW-Authenticate", "Basic realm=\"file\"") + .body(Body::from("please auth".to_string())) + .unwrap() + .into() + } } } } -async fn handle_file(req: DavRequest, stat: web::Data) -> Result<(String, DavRequest)> { +async fn handle_file(req: &DavRequest, stat: web::Data) -> Result { let p = req.request.uri(); let headers = req.request.headers(); let m = req.request.method(); @@ -87,10 +137,10 @@ async fn handle_file(req: DavRequest, stat: web::Data) -> Result<(Stri if app_id != "" { // 只有秘钥才能访问app数据 if t.can_read("app", app_id) { - return Ok((format!("app/{}/", app_id), req)); + return Ok(format!("app/{}/", app_id)); } } else { - return Ok((format!("user/{}/", t.id), req)); + return Ok(format!("user/{}/", t.id)); } } } @@ -110,7 +160,7 @@ async fn handle_file(req: DavRequest, stat: web::Data) -> Result<(Stri { Some(u) => { u.check_pass(&credentials.password)?; - return Ok((format!("user/{}/", u.id), req)); + return Ok(format!("user/{}/", u.id)); } None => {} } diff --git a/oab/src/libs/mod.rs b/oab/src/libs/mod.rs index abdb53a..f448e04 100644 --- a/oab/src/libs/mod.rs +++ b/oab/src/libs/mod.rs @@ -6,68 +6,6 @@ // pub mod auth; -pub mod user; +pub mod cors; pub mod fs; - -use std::future::{ready, Ready}; - -use actix_web::{ - dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, - Error, -}; -use futures_util::future::LocalBoxFuture; - -// There are two steps in middleware processing. -// 1. Middleware initialization, middleware factory gets called with -// next service in chain as parameter. -// 2. Middleware's call method gets called with normal request. -pub struct SayHi; - -// Middleware factory is `Transform` trait -// `S` - type of the next service -// `B` - type of response's body -impl Transform for SayHi -where - S: Service, Error = Error>, - S::Future: 'static, - B: 'static, -{ - type Response = ServiceResponse; - type Error = Error; - type InitError = (); - type Transform = SayHiMiddleware; - type Future = Ready>; - - fn new_transform(&self, service: S) -> Self::Future { - ready(Ok(SayHiMiddleware { service })) - } -} - -pub struct SayHiMiddleware { - service: S, -} - -impl Service for SayHiMiddleware -where - S: Service, Error = Error>, - S::Future: 'static, - B: 'static, -{ - type Response = ServiceResponse; - type Error = Error; - type Future = LocalBoxFuture<'static, Result>; - - forward_ready!(service); - - fn call(&self, req: ServiceRequest) -> Self::Future { - println!("Hi from start. You requested: {}", req.path()); - - let fut = self.service.call(req); - - Box::pin(async move { - let res = fut.await?; - println!("Hi from response"); - Ok(res) - }) - } -} +pub mod user; diff --git a/oab/src/main.rs b/oab/src/main.rs index c193f9c..cf3da71 100644 --- a/oab/src/main.rs +++ b/oab/src/main.rs @@ -1,3 +1,4 @@ +use actix_cors::Cors; // // main.rs // Copyright (C) 2022 veypi @@ -6,13 +7,15 @@ // use actix_files as fs; use actix_web::{ - dev, + dev::{self, Service}, http::StatusCode, middleware::{self, ErrorHandlerResponse, ErrorHandlers}, web::{self}, App, HttpServer, }; +use futures_util::future::FutureExt; +use http::{HeaderName, HeaderValue}; use oab::{api, init_log, libs, models, AppState, Clis, Result, CLI}; use tracing::{error, info, warn}; @@ -53,18 +56,15 @@ async fn web(data: AppState) -> Result<()> { ) .into() }); - let cors = actix_cors::Cors::default() - .allow_any_header() - .allow_any_origin() - .allow_any_method(); + let cors = actix_cors::Cors::permissive(); let app = App::new(); app.wrap(logger) - .wrap(cors) .wrap(middleware::Compress::default()) .app_data(web::Data::new(data.clone())) .service(fs::Files::new("/media", data.media_path.clone()).show_files_listing()) .service( web::scope("api") + .wrap(cors) .wrap( ErrorHandlers::new() .handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_header), @@ -75,6 +75,33 @@ async fn web(data: AppState) -> Result<()> { ) .service( web::scope("file") + .wrap_fn(|req, srv| { + let headers = &req.headers().clone(); + let origin = match headers.get("Origin") { + Some(o) => o.to_str().unwrap().clone().to_string(), + None => "".to_string(), + }; + srv.call(req).map(move |res| { + let res = match res { + Ok(mut expr) => { + let headers = expr.headers_mut(); + headers.insert( + HeaderName::try_from("Access-Control-Allow-Origin") + .unwrap(), + HeaderValue::from_str(&origin).unwrap(), + ); + headers.insert( + HeaderName::try_from("123").unwrap(), + HeaderValue::from_str("asd").unwrap(), + ); + Ok(expr) + } + Err(e) => Err(e), + }; + println!("Hi from response"); + res + }) + }) .app_data(web::Data::new(dav.clone())) .service(web::resource("/{tail:.*}").to(libs::fs::dav_handler)), ) diff --git a/oaweb/quasar.config.js b/oaweb/quasar.config.js index 02ba8c7..5362074 100644 --- a/oaweb/quasar.config.js +++ b/oaweb/quasar.config.js @@ -86,6 +86,9 @@ module.exports = configure(function(/* ctx */) { extendViteConf(viteConf) { viteConf.resolve.preserveSymlinks = true; + viteConf.optimizeDeps.exclude = [ + '@veypi/oaer' + ]; }, // viteVuePluginOptions: {}, diff --git a/oaweb/src/boot/pack.ts b/oaweb/src/boot/pack.ts index ed96d88..ca81d57 100644 --- a/oaweb/src/boot/pack.ts +++ b/oaweb/src/boot/pack.ts @@ -10,9 +10,9 @@ import { boot } from 'quasar/wrappers' import '@veypi/msg/index.css' import '../assets/icon.js' -// import { Cfg } from '@veypi/oaer' + +import '@veypi/oaer/dist/index.css' import { Cfg } from '@veypi/oaer' -// import { Cfg } from '/Users/veypi/test/oaer' Cfg.host.value = 'http://' + window.location.host Cfg.token.value = localStorage.getItem('auth_token') || '' diff --git a/oaweb/src/layouts/MainLayout.vue b/oaweb/src/layouts/MainLayout.vue index 6ea46c6..1259773 100644 --- a/oaweb/src/layouts/MainLayout.vue +++ b/oaweb/src/layouts/MainLayout.vue @@ -56,7 +56,7 @@ import { useRouter } from 'vue-router'; import Menu from 'src/components/menu.vue' import { useAppStore } from 'src/stores/app'; import { useUserStore } from 'src/stores/user'; -import { OAer } from "src/oaer"; +import { OAer } from "@veypi/oaer"; const app = useAppStore() const user = useUserStore() diff --git a/oaweb/src/oaer/api/app.ts b/oaweb/src/oaer/api/app.ts deleted file mode 100644 index 7e69dda..0000000 --- a/oaweb/src/oaer/api/app.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* -* @name: app -* @author: veypi -* @date: 2021-11-17 14:44 -* @description:ap -* @update: 2021-11-17 14:44 -*/ -import ajax from './axios' -import { Cfg } from './setting' - -export default { - local: () => Cfg.BaseUrl() + '/app/', - self() { - return ajax.get(this.local(), { option: 'oa' }) - }, - getKey(uuid: string) { - return ajax.get(this.local() + uuid, { option: 'key' }) - }, - create(name: string, icon: string) { - return ajax.post(this.local(), { name, icon }) - }, - get(uuid: string) { - return ajax.get(this.local() + uuid) - }, - list() { - return ajax.get(this.local()) - }, - update(uuid: string, props: any) { - return ajax.patch(this.local() + uuid, props) - }, - user(uuid: string) { - if (uuid === '') { - uuid = '-' - } - return { - local: () => this.local() + uuid + '/user/', - list(id: string) { - return ajax.get(this.local() + id) - }, - add(uid: number) { - return ajax.post(this.local() + uid) - }, - update(uid: number, status: string) { - return ajax.patch(this.local() + uid, { status }) - }, - } - }, -} diff --git a/oaweb/src/oaer/api/axios.ts b/oaweb/src/oaer/api/axios.ts deleted file mode 100644 index b2d1edc..0000000 --- a/oaweb/src/oaer/api/axios.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * axios.ts - * Copyright (C) 2023 veypi - * 2023-09-22 20:22 - * Distributed under terms of the MIT license. - */ - -import axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; -import msg from '@veypi/msg' - -// Be careful when using SSR for cross-request state pollution -// due to creating a Singleton instance here; -// If any client changes this (global) instance, it might be a -// good idea to move this instance creation inside of the -// "export default () => {}" function below (which runs individually -// for each client) - -const proxy = axios.create({ - withCredentials: true, - headers: { - 'content-type': 'application/json;charset=UTF-8', - }, -}); - - -// 请求拦截 -const beforeRequest = (config: any) => { - // 设置 token - const token = localStorage.getItem('auth_token') - // NOTE 添加自定义头部 - token && (config.headers.auth_token = token) - // config.headers['auth_token'] = '' - return config -} -proxy.interceptors.request.use(beforeRequest) - -// 响应拦截器 -const responseSuccess = (response: AxiosResponse) => { - // eslint-disable-next-line yoda - // 这里没有必要进行判断,axios 内部已经判断 - // const isOk = 200 <= response.status && response.status < 300 - let data = response.data - if (response.config.method === 'head') { - data = JSON.parse(JSON.stringify(response.headers)) - } - return Promise.resolve(data) -} - -const responseFailed = (error: AxiosError) => { - const { response } = error - if (!window.navigator.onLine) { - - alert('没有网络') - return Promise.reject(new Error('请检查网络连接')) - } - console.log(response) - return Promise.reject(response?.data || response?.headers.error) -} - -proxy.interceptors.response.use(responseSuccess, responseFailed) - - -const ajax = { - get(url: string, data = {}, header?: any) { - return proxy.get(url, { params: data, headers: header }) - }, - head(url: string, data = {}, header?: any) { - return proxy.head(url, { params: data, headers: header }) - }, - delete(url: string, data = {}, header?: any) { - return proxy.delete(url, { params: data, headers: header }) - }, - - post(url: string, data = {}, header?: any) { - return proxy.post(url, data, { headers: header }) - }, - put(url: string, data = {}, header?: any) { - return proxy.put(url, data, { headers: header }) - }, - patch(url: string, data = {}, header?: any) { - return proxy.patch(url, data, { headers: header }) - }, -} - -export default ajax - diff --git a/oaweb/src/oaer/api/index.ts b/oaweb/src/oaer/api/index.ts deleted file mode 100644 index 9ba7de8..0000000 --- a/oaweb/src/oaer/api/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2019 light - * - * Distributed under terms of the MIT license. - */ - -import user from './user' -import app from './app' -import {Cfg} from './setting' - - -const api = { - user: user, - app: app, -} - -export {api, Cfg} -export default api diff --git a/oaweb/src/oaer/api/setting.ts b/oaweb/src/oaer/api/setting.ts deleted file mode 100644 index 3816da0..0000000 --- a/oaweb/src/oaer/api/setting.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* -* @name: setting -* @author: veypi -* @date: 2021-11-17 15:45 -* @description:setting -* @update: 2021-11-17 15:45 -*/ - -import { ref } from 'vue' - -export let Cfg = { - token: ref(''), - uuid: ref(''), - host: ref(''), - prefix: '/api', - BaseUrl() { - return this.host.value + this.prefix - }, - goto(url: string) { - if (!url.startsWith('/')) { - url = '/' + url - } - window.location.href = this.host.value + '/#' + url - }, - userFileUrl() { - return (this.host.value || window.location.host) + '/file/' - }, -} diff --git a/oaweb/src/oaer/api/user.ts b/oaweb/src/oaer/api/user.ts deleted file mode 100644 index c456a52..0000000 --- a/oaweb/src/oaer/api/user.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * user.ts - * Copyright (C) 2023 veypi - * 2023-10-05 15:37 - * Distributed under terms of the MIT license. - */ - - - -import { Base64 } from 'js-base64' -import ajax from './axios' -import { Cfg } from './setting' - - - -export default { - local: () => Cfg.BaseUrl() + '/user/', - register(username: string, password: string, prop?: any) { - const data = Object.assign({ - username: username, - password: Base64.encode(password), - }, prop) - return ajax.post(this.local(), data) - }, - login(username: string, password: string) { - return ajax.head(this.local() + username, { - typ: 'username', - password: Base64.encode(password), - }) - }, - search(q: string) { - return ajax.get(this.local(), { username: q }) - }, - get(id: number) { - return ajax.get(this.local() + id) - }, - list() { - return ajax.get(this.local()) - }, - update(id: number, props: any) { - return ajax.patch(this.local() + id, props) - }, -} - diff --git a/oaweb/src/oaer/assets/icon.js b/oaweb/src/oaer/assets/icon.js deleted file mode 100644 index ceec101..0000000 --- a/oaweb/src/oaer/assets/icon.js +++ /dev/null @@ -1 +0,0 @@ -!function(t){var e,n,o,i,c,d='',s=(s=document.getElementsByTagName("script"))[s.length-1].getAttribute("data-injectcss"),l=function(t,e){e.parentNode.insertBefore(t,e)};if(s&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}function a(){c||(c=!0,o())}function r(){try{i.documentElement.doScroll("left")}catch(t){return void setTimeout(r,50)}a()}e=function(){var t,e;(e=document.createElement("div")).innerHTML=d,d=null,(t=e.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",e=t,(t=document.body).firstChild?l(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(e,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),e()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(o=e,i=t.document,c=!1,r(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,a())})}(window); \ No newline at end of file diff --git a/oaweb/src/oaer/components/app.vue b/oaweb/src/oaer/components/app.vue deleted file mode 100644 index 1782234..0000000 --- a/oaweb/src/oaer/components/app.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - diff --git a/oaweb/src/oaer/components/file.vue b/oaweb/src/oaer/components/file.vue deleted file mode 100644 index 3cbf4f5..0000000 --- a/oaweb/src/oaer/components/file.vue +++ /dev/null @@ -1,47 +0,0 @@ - - - diff --git a/oaweb/src/oaer/components/user.vue b/oaweb/src/oaer/components/user.vue deleted file mode 100644 index 91c714f..0000000 --- a/oaweb/src/oaer/components/user.vue +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/oaweb/src/oaer/evt/index.ts b/oaweb/src/oaer/evt/index.ts deleted file mode 100644 index b4a4679..0000000 --- a/oaweb/src/oaer/evt/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* -* @name: index -* @author: veypi -* @date: 2021-12-18 14:24 -* @description:index -*/ - -import mitt from 'mitt' - -const emitter = mitt() - -export default emitter diff --git a/oaweb/src/oaer/frame.vue b/oaweb/src/oaer/frame.vue deleted file mode 100644 index d60685a..0000000 --- a/oaweb/src/oaer/frame.vue +++ /dev/null @@ -1,90 +0,0 @@ - - - - - diff --git a/oaweb/src/oaer/index.ts b/oaweb/src/oaer/index.ts deleted file mode 100644 index ed9d4ee..0000000 --- a/oaweb/src/oaer/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* -* @name: index -* @author: veypi -* @date: 2021-12-18 13:16 -* @description:index -*/ - -import 'animate.css' -import OAer from './main.vue' -import './assets/icon.js' -import { Cfg, api } from './api' - -export { OAer, Cfg, api } - diff --git a/oaweb/src/oaer/libs/webdav/auth/basic.ts b/oaweb/src/oaer/libs/webdav/auth/basic.ts deleted file mode 100644 index 98ea430..0000000 --- a/oaweb/src/oaer/libs/webdav/auth/basic.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { toBase64 } from "../tools/encode"; -import { AuthHeader } from "../types"; - -export function generateBasicAuthHeader(username: string, password: string): AuthHeader { - const encoded = toBase64(`${username}:${password}`); - return `Basic ${encoded}`; -} diff --git a/oaweb/src/oaer/libs/webdav/auth/digest.ts b/oaweb/src/oaer/libs/webdav/auth/digest.ts deleted file mode 100644 index 63abdaa..0000000 --- a/oaweb/src/oaer/libs/webdav/auth/digest.ts +++ /dev/null @@ -1,82 +0,0 @@ -import md5 from "md5"; -import { ha1Compute } from "../tools/crypto"; -import { DigestContext, Response } from "../types"; - -const NONCE_CHARS = "abcdef0123456789"; -const NONCE_SIZE = 32; - -export function createDigestContext(username: string, password: string): DigestContext { - return { username, password, nc: 0, algorithm: "md5", hasDigestAuth: false }; -} - -export function generateDigestAuthHeader(options, digest: DigestContext): string { - const url = options.url.replace("//", ""); - const uri = url.indexOf("/") == -1 ? "/" : url.slice(url.indexOf("/")); - const method = options.method ? options.method.toUpperCase() : "GET"; - const qop = /(^|,)\s*auth\s*($|,)/.test(digest.qop) ? "auth" : false; - const ncString = `00000000${digest.nc}`.slice(-8); - const ha1 = ha1Compute( - digest.algorithm, - digest.username, - digest.realm, - digest.password, - digest.nonce, - digest.cnonce - ); - const ha2 = md5(`${method}:${uri}`); - const digestResponse = qop - ? md5(`${ha1}:${digest.nonce}:${ncString}:${digest.cnonce}:${qop}:${ha2}`) - : md5(`${ha1}:${digest.nonce}:${ha2}`); - - const authValues = { - username: digest.username, - realm: digest.realm, - nonce: digest.nonce, - uri, - qop, - response: digestResponse, - nc: ncString, - cnonce: digest.cnonce, - algorithm: digest.algorithm, - opaque: digest.opaque - }; - - const authHeader = []; - for (const k in authValues) { - if (authValues[k]) { - if (k === "qop" || k === "nc" || k === "algorithm") { - authHeader.push(`${k}=${authValues[k]}`); - } else { - authHeader.push(`${k}="${authValues[k]}"`); - } - } - } - - return `Digest ${authHeader.join(", ")}`; -} - -function makeNonce(): string { - let uid = ""; - for (let i = 0; i < NONCE_SIZE; ++i) { - uid = `${uid}${NONCE_CHARS[Math.floor(Math.random() * NONCE_CHARS.length)]}`; - } - return uid; -} - -export function parseDigestAuth(response: Response, _digest: DigestContext): boolean { - const authHeader = response.headers["www-authenticate"] || ""; - if (authHeader.split(/\s/)[0].toLowerCase() !== "digest") { - return false; - } - const re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi; - for (;;) { - const match = re.exec(authHeader); - if (!match) { - break; - } - _digest[match[1]] = match[2] || match[3]; - } - _digest.nc += 1; - _digest.cnonce = makeNonce(); - return true; -} diff --git a/oaweb/src/oaer/libs/webdav/auth/index.ts b/oaweb/src/oaer/libs/webdav/auth/index.ts deleted file mode 100644 index 3758b29..0000000 --- a/oaweb/src/oaer/libs/webdav/auth/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Layerr } from "layerr"; -import { createDigestContext } from "./digest"; -import { generateBasicAuthHeader } from "./basic"; -import { generateTokenAuthHeader } from "./oauth"; -import { AuthType, ErrorCode, OAuthToken, WebDAVClientContext } from "../types"; - -export function setupAuth( - context: WebDAVClientContext, - username: string, - password: string, - oauthToken: OAuthToken -): void { - switch (context.authType) { - case AuthType.Digest: - context.digest = createDigestContext(username, password); - break; - case AuthType.None: - // Do nothing - break; - case AuthType.Password: - context.headers.Authorization = generateBasicAuthHeader(username, password); - break; - case AuthType.Token: - context.headers.Authorization = generateTokenAuthHeader(oauthToken); - break; - default: - throw new Layerr( - { - info: { - code: ErrorCode.InvalidAuthType - } - }, - `Invalid auth type: ${context.authType}` - ); - } -} diff --git a/oaweb/src/oaer/libs/webdav/auth/oauth.ts b/oaweb/src/oaer/libs/webdav/auth/oauth.ts deleted file mode 100644 index 7261c91..0000000 --- a/oaweb/src/oaer/libs/webdav/auth/oauth.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { AuthHeader, OAuthToken } from "../types"; - -export function generateTokenAuthHeader(token: OAuthToken): AuthHeader { - return `${token.token_type} ${token.access_token}`; -} diff --git a/oaweb/src/oaer/libs/webdav/compat/arrayBuffer.ts b/oaweb/src/oaer/libs/webdav/compat/arrayBuffer.ts deleted file mode 100644 index 750dcfc..0000000 --- a/oaweb/src/oaer/libs/webdav/compat/arrayBuffer.ts +++ /dev/null @@ -1,10 +0,0 @@ -const hasArrayBuffer = typeof ArrayBuffer === "function"; -const { toString: objToString } = Object.prototype; - -// Taken from: https://github.com/fengyuanchen/is-array-buffer/blob/master/src/index.js -export function isArrayBuffer(value: any): boolean { - return ( - hasArrayBuffer && - (value instanceof ArrayBuffer || objToString.call(value) === "[object ArrayBuffer]") - ); -} diff --git a/oaweb/src/oaer/libs/webdav/compat/buffer.ts b/oaweb/src/oaer/libs/webdav/compat/buffer.ts deleted file mode 100644 index 5133dd9..0000000 --- a/oaweb/src/oaer/libs/webdav/compat/buffer.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function isBuffer(value: any): boolean { - return ( - value != null && - value.constructor != null && - typeof value.constructor.isBuffer === "function" && - value.constructor.isBuffer(value) - ); -} diff --git a/oaweb/src/oaer/libs/webdav/compat/patcher.ts b/oaweb/src/oaer/libs/webdav/compat/patcher.ts deleted file mode 100644 index adfb722..0000000 --- a/oaweb/src/oaer/libs/webdav/compat/patcher.ts +++ /dev/null @@ -1,10 +0,0 @@ -import HotPatcher from "hot-patcher"; - -let __patcher: HotPatcher = null; - -export function getPatcher(): HotPatcher { - if (!__patcher) { - __patcher = new HotPatcher(); - } - return __patcher; -} diff --git a/oaweb/src/oaer/libs/webdav/factory.ts b/oaweb/src/oaer/libs/webdav/factory.ts deleted file mode 100644 index 6a4ee6e..0000000 --- a/oaweb/src/oaer/libs/webdav/factory.ts +++ /dev/null @@ -1,114 +0,0 @@ -import Stream from "stream"; -import { extractURLPath } from "./tools/url"; -import { setupAuth } from "./auth/index"; -import { copyFile } from "./operations/copyFile"; -import { createDirectory } from "./operations/createDirectory"; -import { createReadStream, createWriteStream } from "./operations/createStream"; -import { customRequest } from "./operations/customRequest"; -import { deleteFile } from "./operations/deleteFile"; -import { exists } from "./operations/exists"; -import { getDirectoryContents } from "./operations/directoryContents"; -import { getFileContents, getFileDownloadLink } from "./operations/getFileContents"; -import { lock, unlock } from "./operations/lock"; -import { getQuota } from "./operations/getQuota"; -import { getStat } from "./operations/stat"; -import { moveFile } from "./operations/moveFile"; -import { getFileUploadLink, putFileContents } from "./operations/putFileContents"; -import { - AuthType, - BufferLike, - CreateReadStreamOptions, - CreateWriteStreamCallback, - CreateWriteStreamOptions, - GetDirectoryContentsOptions, - GetFileContentsOptions, - GetQuotaOptions, - Headers, - LockOptions, - PutFileContentsOptions, - RequestOptionsCustom, - StatOptions, - WebDAVClient, - WebDAVClientContext, - WebDAVClientOptions, - WebDAVMethodOptions -} from "./types"; - -const DEFAULT_CONTACT_HREF = - "https://github.com/perry-mitchell/webdav-client/blob/master/LOCK_CONTACT.md"; - -export function createClient(remoteURL: string, options: WebDAVClientOptions = {}): WebDAVClient { - const { - authType: authTypeRaw = null, - contactHref = DEFAULT_CONTACT_HREF, - headers = {}, - httpAgent, - httpsAgent, - maxBodyLength, - maxContentLength, - password, - token, - username, - withCredentials - } = options; - let authType = authTypeRaw; - if (!authType) { - authType = username || password ? AuthType.Password : AuthType.None; - } - const context: WebDAVClientContext = { - authType, - contactHref, - headers: Object.assign({}, headers), - httpAgent, - httpsAgent, - maxBodyLength, - maxContentLength, - remotePath: extractURLPath(remoteURL), - remoteURL, - password, - token, - username, - withCredentials - }; - setupAuth(context, username, password, token); - return { - copyFile: (filename: string, destination: string, options?: WebDAVMethodOptions) => - copyFile(context, filename, destination, options), - createDirectory: (path: string, options?: WebDAVMethodOptions) => - createDirectory(context, path, options), - createReadStream: (filename: string, options?: CreateReadStreamOptions) => - createReadStream(context, filename, options), - createWriteStream: ( - filename: string, - options?: CreateWriteStreamOptions, - callback?: CreateWriteStreamCallback - ) => createWriteStream(context, filename, options, callback), - customRequest: (path: string, requestOptions: RequestOptionsCustom) => - customRequest(context, path, requestOptions), - deleteFile: (filename: string, options?: WebDAVMethodOptions) => - deleteFile(context, filename, options), - exists: (path: string, options?: WebDAVMethodOptions) => exists(context, path, options), - getDirectoryContents: (path: string, options?: GetDirectoryContentsOptions) => - getDirectoryContents(context, path, options), - getFileContents: (filename: string, options?: GetFileContentsOptions) => - getFileContents(context, filename, options), - getFileDownloadLink: (filename: string) => getFileDownloadLink(context, filename), - getFileUploadLink: (filename: string) => getFileUploadLink(context, filename), - getHeaders: () => Object.assign({}, context.headers), - getQuota: (options?: GetQuotaOptions) => getQuota(context, options), - lock: (path: string, options?: LockOptions) => lock(context, path, options), - moveFile: (filename: string, destinationFilename: string, options?: WebDAVMethodOptions) => - moveFile(context, filename, destinationFilename, options), - putFileContents: ( - filename: string, - data: string | BufferLike | Stream.Readable, - options?: PutFileContentsOptions - ) => putFileContents(context, filename, data, options), - setHeaders: (headers: Headers) => { - context.headers = Object.assign({}, headers); - }, - stat: (path: string, options?: StatOptions) => getStat(context, path, options), - unlock: (path: string, token: string, options?: WebDAVMethodOptions) => - unlock(context, path, token, options) - }; -} diff --git a/oaweb/src/oaer/libs/webdav/index.ts b/oaweb/src/oaer/libs/webdav/index.ts deleted file mode 100644 index defd0f6..0000000 --- a/oaweb/src/oaer/libs/webdav/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { createClient } from "./factory"; -export { getPatcher } from "./compat/patcher"; -export * from "./types"; - -export { parseStat, parseXML } from "./tools/dav"; diff --git a/oaweb/src/oaer/libs/webdav/operations/copyFile.ts b/oaweb/src/oaer/libs/webdav/operations/copyFile.ts deleted file mode 100644 index cd12627..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/copyFile.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { joinURL } from "../tools/url"; -import { encodePath } from "../tools/path"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode } from "../response"; -import { WebDAVClientContext, WebDAVMethodOptions } from "../types"; - -export async function copyFile( - context: WebDAVClientContext, - filename: string, - destination: string, - options: WebDAVMethodOptions = {} -): Promise { - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(filename)), - method: "COPY", - headers: { - Destination: joinURL(context.remoteURL, encodePath(destination)) - } - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); -} diff --git a/oaweb/src/oaer/libs/webdav/operations/createDirectory.ts b/oaweb/src/oaer/libs/webdav/operations/createDirectory.ts deleted file mode 100644 index f75b06b..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/createDirectory.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { joinURL } from "../tools/url"; -import { encodePath, getAllDirectories, normalisePath } from "../tools/path"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode } from "../response"; -import { getStat } from "./stat"; -import { CreateDirectoryOptions, FileStat, WebDAVClientContext, WebDAVClientError } from "../types"; - -export async function createDirectory( - context: WebDAVClientContext, - dirPath: string, - options: CreateDirectoryOptions = {} -): Promise { - if (options.recursive === true) return createDirectoryRecursively(context, dirPath, options); - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, ensureCollectionPath(encodePath(dirPath))), - method: "MKCOL" - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); -} - -/** - * Ensure the path is a proper "collection" path by ensuring it has a trailing "/". - * The proper format of collection according to the specification does contain the trailing slash. - * http://www.webdav.org/specs/rfc4918.html#rfc.section.5.2 - * @param path Path of the collection - * @return string Path of the collection with appended trailing "/" in case the `path` does not have it. - */ -function ensureCollectionPath(path: string): string { - if (!path.endsWith("/")) { - return path + "/"; - } - return path; -} - -async function createDirectoryRecursively( - context: WebDAVClientContext, - dirPath: string, - options: CreateDirectoryOptions = {} -): Promise { - const paths = getAllDirectories(normalisePath(dirPath)); - paths.sort((a, b) => { - if (a.length > b.length) { - return 1; - } else if (b.length > a.length) { - return -1; - } - return 0; - }); - let creating: boolean = false; - for (const testPath of paths) { - if (creating) { - await createDirectory(context, testPath, { - ...options, - recursive: false - }); - continue; - } - try { - const testStat = (await getStat(context, testPath)) as FileStat; - if (testStat.type !== "directory") { - throw new Error(`Path includes a file: ${dirPath}`); - } - } catch (err) { - const error = err as WebDAVClientError; - if (error.status === 404) { - creating = true; - await createDirectory(context, testPath, { - ...options, - recursive: false - }); - } else { - throw err; - } - } - } -} diff --git a/oaweb/src/oaer/libs/webdav/operations/createStream.ts b/oaweb/src/oaer/libs/webdav/operations/createStream.ts deleted file mode 100644 index eea29a8..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/createStream.ts +++ /dev/null @@ -1,109 +0,0 @@ -import Stream from "stream"; -import { joinURL } from "../tools/url"; -import { encodePath } from "../tools/path"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode } from "../response"; -import { - CreateReadStreamOptions, - CreateWriteStreamCallback, - CreateWriteStreamOptions, - Headers, - WebDAVClientContext, - WebDAVClientError -} from "../types"; - -const NOOP = () => {}; - -export function createReadStream( - context: WebDAVClientContext, - filePath: string, - options: CreateReadStreamOptions = {} -): Stream.Readable { - const PassThroughStream = Stream.PassThrough; - const outStream = new PassThroughStream(); - getFileStream(context, filePath, options) - .then(stream => { - stream.pipe(outStream); - }) - .catch(err => { - outStream.emit("error", err); - }); - return outStream; -} - -export function createWriteStream( - context: WebDAVClientContext, - filePath: string, - options: CreateWriteStreamOptions = {}, - callback: CreateWriteStreamCallback = NOOP -): Stream.Writable { - const PassThroughStream = Stream.PassThrough; - const writeStream = new PassThroughStream(); - const headers = {}; - if (options.overwrite === false) { - headers["If-None-Match"] = "*"; - } - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(filePath)), - method: "PUT", - headers, - data: writeStream, - maxRedirects: 0 - }, - context, - options - ); - request(requestOptions) - .then(response => handleResponseCode(context, response)) - .then(response => { - // Fire callback asynchronously to avoid errors - setTimeout(() => { - callback(response); - }, 0); - }) - .catch(err => { - writeStream.emit("error", err); - }); - return writeStream; -} - -async function getFileStream( - context: WebDAVClientContext, - filePath: string, - options: CreateReadStreamOptions = {} -): Promise { - const headers: Headers = {}; - if (typeof options.range === "object" && typeof options.range.start === "number") { - let rangeHeader = `bytes=${options.range.start}-`; - if (typeof options.range.end === "number") { - rangeHeader = `${rangeHeader}${options.range.end}`; - } - headers.Range = rangeHeader; - } - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(filePath)), - method: "GET", - headers, - responseType: "stream" - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); - if (headers.Range && response.status !== 206) { - const responseError: WebDAVClientError = new Error( - `Invalid response code for partial request: ${response.status}` - ); - responseError.status = response.status; - throw responseError; - } - if (options.callback) { - setTimeout(() => { - options.callback(response); - }, 0); - } - return response.data as Stream.Readable; -} diff --git a/oaweb/src/oaer/libs/webdav/operations/customRequest.ts b/oaweb/src/oaer/libs/webdav/operations/customRequest.ts deleted file mode 100644 index cc31dba..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/customRequest.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { joinURL } from "../tools/url"; -import { encodePath } from "../tools/path"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode } from "../response"; -import { RequestOptionsCustom, Response, WebDAVClientContext } from "../types"; - -export async function customRequest( - context: WebDAVClientContext, - remotePath: string, - requestOptions: RequestOptionsCustom -): Promise { - if (!requestOptions.url) { - requestOptions.url = joinURL(context.remoteURL, encodePath(remotePath)); - } - const finalOptions = prepareRequestOptions(requestOptions, context, {}); - const response = await request(finalOptions); - handleResponseCode(context, response); - return response; -} diff --git a/oaweb/src/oaer/libs/webdav/operations/deleteFile.ts b/oaweb/src/oaer/libs/webdav/operations/deleteFile.ts deleted file mode 100644 index fd3f509..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/deleteFile.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { joinURL } from "../tools/url"; -import { encodePath } from "../tools/path"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode } from "../response"; -import { WebDAVClientContext, WebDAVMethodOptions } from "../types"; - -export async function deleteFile( - context: WebDAVClientContext, - filename: string, - options: WebDAVMethodOptions = {} -): Promise { - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(filename)), - method: "DELETE" - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); -} diff --git a/oaweb/src/oaer/libs/webdav/operations/directoryContents.ts b/oaweb/src/oaer/libs/webdav/operations/directoryContents.ts deleted file mode 100644 index 8988e16..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/directoryContents.ts +++ /dev/null @@ -1,78 +0,0 @@ -import pathPosix from "path-posix"; -import { joinURL, normaliseHREF } from "../tools/url"; -import { encodePath, normalisePath } from "../tools/path"; -import { parseXML, prepareFileFromProps } from "../tools/dav"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode, processGlobFilter, processResponsePayload } from "../response"; -import { - DAVResult, - FileStat, - GetDirectoryContentsOptions, - ResponseDataDetailed, - WebDAVClientContext -} from "../types"; - -export async function getDirectoryContents( - context: WebDAVClientContext, - remotePath: string, - options: GetDirectoryContentsOptions = {} -): Promise | ResponseDataDetailed>> { - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(remotePath), "/"), - method: "PROPFIND", - headers: { - Accept: "text/plain", - Depth: options.deep ? "infinity" : "1" - }, - responseType: "text" - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); - const davResp = await parseXML(response.data as string); - let files = getDirectoryFiles(davResp, context.remotePath, remotePath, options.details); - if (options.glob) { - files = processGlobFilter(files, options.glob); - } - return processResponsePayload(response, files, options.details); -} - -function getDirectoryFiles( - result: DAVResult, - serverBasePath: string, - requestPath: string, - isDetailed: boolean = false -): Array { - const serverBase = pathPosix.join(serverBasePath, "/"); - // Extract the response items (directory contents) - const { - multistatus: { response: responseItems } - } = result; - return ( - responseItems - // Map all items to a consistent output structure (results) - .map(item => { - // HREF is the file path (in full) - const href = normaliseHREF(item.href); - // Each item should contain a stat object - const { - propstat: { prop: props } - } = item; - // Process the true full filename (minus the base server path) - const filename = - serverBase === "/" - ? decodeURIComponent(normalisePath(href)) - : decodeURIComponent(normalisePath(pathPosix.relative(serverBase, href))); - return prepareFileFromProps(props, filename, isDetailed); - }) - // Filter out the item pointing to the current directory (not needed) - .filter( - item => - item.basename && - (item.type === "file" || item.filename !== requestPath.replace(/\/$/, "")) - ) - ); -} diff --git a/oaweb/src/oaer/libs/webdav/operations/exists.ts b/oaweb/src/oaer/libs/webdav/operations/exists.ts deleted file mode 100644 index 6e79b45..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/exists.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getStat } from "./stat"; -import { WebDAVClientContext, WebDAVMethodOptions } from "../types"; - -export async function exists( - context: WebDAVClientContext, - remotePath: string, - options: WebDAVMethodOptions = {} -): Promise { - try { - await getStat(context, remotePath, options); - return true; - } catch (err) { - if (err.status === 404) { - return false; - } - throw err; - } -} diff --git a/oaweb/src/oaer/libs/webdav/operations/getFileContents.ts b/oaweb/src/oaer/libs/webdav/operations/getFileContents.ts deleted file mode 100644 index 4373f78..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/getFileContents.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Layerr } from "layerr"; -import { joinURL } from "../tools/url"; -import { encodePath } from "../tools/path"; -import { fromBase64 } from "../tools/encode"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode, processResponsePayload } from "../response"; -import { - AuthType, - BufferLike, - ErrorCode, - GetFileContentsOptions, - ResponseDataDetailed, - WebDAVClientContext -} from "../types"; - -const TRANSFORM_RETAIN_FORMAT = (v: any) => v; - -export async function getFileContents( - context: WebDAVClientContext, - filePath: string, - options: GetFileContentsOptions = {} -): Promise> { - const { format = "binary" } = options; - if (format !== "binary" && format !== "text") { - throw new Layerr( - { - info: { - code: ErrorCode.InvalidOutputFormat - } - }, - `Invalid output format: ${format}` - ); - } - return format === "text" - ? getFileContentsString(context, filePath, options) - : getFileContentsBuffer(context, filePath, options); -} - -async function getFileContentsBuffer( - context: WebDAVClientContext, - filePath: string, - options: GetFileContentsOptions = {} -): Promise> { - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(filePath)), - method: "GET", - responseType: "arraybuffer" - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); - return processResponsePayload(response, response.data as BufferLike, options.details); -} - -async function getFileContentsString( - context: WebDAVClientContext, - filePath: string, - options: GetFileContentsOptions = {} -): Promise> { - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(filePath)), - method: "GET", - responseType: "text", - transformResponse: [TRANSFORM_RETAIN_FORMAT] - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); - return processResponsePayload(response, response.data as string, options.details); -} - -export function getFileDownloadLink(context: WebDAVClientContext, filePath: string): string { - let url = joinURL(context.remoteURL, encodePath(filePath)); - const protocol = /^https:/i.test(url) ? "https" : "http"; - switch (context.authType) { - case AuthType.None: - // Do nothing - break; - case AuthType.Password: { - const authPart = context.headers.Authorization.replace(/^Basic /i, "").trim(); - const authContents = fromBase64(authPart); - url = url.replace(/^https?:\/\//, `${protocol}://${authContents}@`); - break; - } - default: - throw new Layerr( - { - info: { - code: ErrorCode.LinkUnsupportedAuthType - } - }, - `Unsupported auth type for file link: ${context.authType}` - ); - } - return url; -} diff --git a/oaweb/src/oaer/libs/webdav/operations/getQuota.ts b/oaweb/src/oaer/libs/webdav/operations/getQuota.ts deleted file mode 100644 index 70b8206..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/getQuota.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { prepareRequestOptions, request } from "../request"; -import { handleResponseCode, processResponsePayload } from "../response"; -import { parseXML } from "../tools/dav"; -import { joinURL } from "../tools/url"; -import { parseQuota } from "../tools/quota"; -import { DiskQuota, GetQuotaOptions, ResponseDataDetailed, WebDAVClientContext } from "../types"; - -export async function getQuota( - context: WebDAVClientContext, - options: GetQuotaOptions = {} -): Promise> { - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, "/"), - method: "PROPFIND", - headers: { - Accept: "text/plain", - Depth: "0" - }, - responseType: "text" - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); - const result = await parseXML(response.data as string); - const quota = parseQuota(result); - return processResponsePayload(response, quota, options.details); -} diff --git a/oaweb/src/oaer/libs/webdav/operations/lock.ts b/oaweb/src/oaer/libs/webdav/operations/lock.ts deleted file mode 100644 index 316a357..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/lock.ts +++ /dev/null @@ -1,79 +0,0 @@ -import nestedProp from "nested-property"; -import { joinURL } from "../tools/url"; -import { encodePath } from "../tools/path"; -import { generateLockXML, parseGenericResponse } from "../tools/xml"; -import { request, prepareRequestOptions } from "../request"; -import { createErrorFromResponse, handleResponseCode } from "../response"; -import { - Headers, - LockOptions, - LockResponse, - WebDAVClientContext, - WebDAVMethodOptions -} from "../types"; - -const DEFAULT_TIMEOUT = "Infinite, Second-4100000000"; - -export async function lock( - context: WebDAVClientContext, - path: string, - options: LockOptions = {} -): Promise { - const { refreshToken, timeout = DEFAULT_TIMEOUT } = options; - const headers: Headers = { - Accept: "text/plain,application/xml", - Timeout: timeout - }; - if (refreshToken) { - headers.If = refreshToken; - } - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(path)), - method: "LOCK", - headers, - data: generateLockXML(context.contactHref), - responseType: "text" - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); - const lockPayload = parseGenericResponse(response.data as string); - const token = nestedProp.get(lockPayload, "prop.lockdiscovery.activelock.locktoken.href"); - const serverTimeout = nestedProp.get(lockPayload, "prop.lockdiscovery.activelock.timeout"); - if (!token) { - const err = createErrorFromResponse(response, "No lock token received: "); - throw err; - } - return { - token, - serverTimeout - }; -} - -export async function unlock( - context: WebDAVClientContext, - path: string, - token: string, - options: WebDAVMethodOptions = {} -): Promise { - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(path)), - method: "UNLOCK", - headers: { - "Lock-Token": token - } - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); - if (response.status !== 204 && response.status !== 200) { - const err = createErrorFromResponse(response); - throw err; - } -} diff --git a/oaweb/src/oaer/libs/webdav/operations/moveFile.ts b/oaweb/src/oaer/libs/webdav/operations/moveFile.ts deleted file mode 100644 index f0d67c5..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/moveFile.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { joinURL } from "../tools/url"; -import { encodePath } from "../tools/path"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode } from "../response"; -import { WebDAVClientContext, WebDAVMethodOptions } from "../types"; - -export async function moveFile( - context: WebDAVClientContext, - filename: string, - destination: string, - options: WebDAVMethodOptions = {} -): Promise { - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(filename)), - method: "MOVE", - headers: { - Destination: joinURL(context.remoteURL, encodePath(destination)) - } - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); -} diff --git a/oaweb/src/oaer/libs/webdav/operations/putFileContents.ts b/oaweb/src/oaer/libs/webdav/operations/putFileContents.ts deleted file mode 100644 index b4aa784..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/putFileContents.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Layerr } from "layerr"; -import Stream from "stream"; -import { fromBase64 } from "../tools/encode"; -import { joinURL } from "../tools/url"; -import { encodePath } from "../tools/path"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode } from "../response"; -import { calculateDataLength } from "../tools/size"; -import { - AuthType, - BufferLike, - ErrorCode, - Headers, - PutFileContentsOptions, - WebDAVClientContext, - WebDAVClientError -} from "../types"; - -declare var WEB: boolean; - -export async function putFileContents( - context: WebDAVClientContext, - filePath: string, - data: string | BufferLike | Stream.Readable, - options: PutFileContentsOptions = {} -): Promise { - const { contentLength = true, overwrite = true } = options; - const headers: Headers = { - "Content-Type": "application/octet-stream" - }; - if (typeof WEB === "undefined") { - // Skip, no content-length - } else if (contentLength === false) { - // Skip, disabled - } else if (typeof contentLength === "number") { - headers["Content-Length"] = `${contentLength}`; - } else { - headers["Content-Length"] = `${calculateDataLength(data as string | BufferLike)}`; - } - if (!overwrite) { - headers["If-None-Match"] = "*"; - } - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(filePath)), - method: "PUT", - headers, - data - }, - context, - options - ); - const response = await request(requestOptions); - try { - handleResponseCode(context, response); - } catch (err) { - const error = err as WebDAVClientError; - if (error.status === 412 && !overwrite) { - return false; - } else { - throw error; - } - } - return true; -} - -export function getFileUploadLink(context: WebDAVClientContext, filePath: string): string { - let url: string = `${joinURL( - context.remoteURL, - encodePath(filePath) - )}?Content-Type=application/octet-stream`; - const protocol = /^https:/i.test(url) ? "https" : "http"; - switch (context.authType) { - case AuthType.None: - // Do nothing - break; - case AuthType.Password: { - const authPart = context.headers.Authorization.replace(/^Basic /i, "").trim(); - const authContents = fromBase64(authPart); - url = url.replace(/^https?:\/\//, `${protocol}://${authContents}@`); - break; - } - default: - throw new Layerr( - { - info: { - code: ErrorCode.LinkUnsupportedAuthType - } - }, - `Unsupported auth type for file link: ${context.authType}` - ); - } - return url; -} diff --git a/oaweb/src/oaer/libs/webdav/operations/stat.ts b/oaweb/src/oaer/libs/webdav/operations/stat.ts deleted file mode 100644 index 8ad7367..0000000 --- a/oaweb/src/oaer/libs/webdav/operations/stat.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { parseStat, parseXML } from "../tools/dav"; -import { joinURL } from "../tools/url"; -import { encodePath } from "../tools/path"; -import { request, prepareRequestOptions } from "../request"; -import { handleResponseCode, processResponsePayload } from "../response"; -import { FileStat, ResponseDataDetailed, StatOptions, WebDAVClientContext } from "../types"; - -export async function getStat( - context: WebDAVClientContext, - filename: string, - options: StatOptions = {} -): Promise> { - const { details: isDetailed = false } = options; - const requestOptions = prepareRequestOptions( - { - url: joinURL(context.remoteURL, encodePath(filename)), - method: "PROPFIND", - headers: { - Accept: "text/plain,application/xml", - Depth: "0" - }, - responseType: "text" - }, - context, - options - ); - const response = await request(requestOptions); - handleResponseCode(context, response); - const result = await parseXML(response.data as string); - const stat = parseStat(result, filename, isDetailed); - return processResponsePayload(response, stat, isDetailed); -} diff --git a/oaweb/src/oaer/libs/webdav/request.ts b/oaweb/src/oaer/libs/webdav/request.ts deleted file mode 100644 index 0f60421..0000000 --- a/oaweb/src/oaer/libs/webdav/request.ts +++ /dev/null @@ -1,108 +0,0 @@ -import axios from "axios"; -import { getPatcher } from "./compat/patcher"; -import { generateDigestAuthHeader, parseDigestAuth } from "./auth/digest"; -import { cloneShallow, merge } from "./tools/merge"; -import { mergeHeaders } from "./tools/headers"; -import { - RequestOptionsCustom, - RequestOptionsWithState, - RequestOptions, - Response, - WebDAVClientContext, - WebDAVMethodOptions -} from "./types"; - -function _request(requestOptions: RequestOptions) { - return getPatcher().patchInline( - "request", - (options: RequestOptions) => axios(options as any), - requestOptions - ); -} - -export function prepareRequestOptions( - requestOptions: RequestOptionsCustom | RequestOptionsWithState, - context: WebDAVClientContext, - userOptions: WebDAVMethodOptions -): RequestOptionsWithState { - const finalOptions = cloneShallow(requestOptions) as RequestOptionsWithState; - finalOptions.headers = mergeHeaders( - context.headers, - finalOptions.headers || {}, - userOptions.headers || {} - ); - if (typeof userOptions.data !== "undefined") { - finalOptions.data = userOptions.data; - } - if (context.httpAgent) { - finalOptions.httpAgent = context.httpAgent; - } - if (context.httpsAgent) { - finalOptions.httpsAgent = context.httpsAgent; - } - if (context.digest) { - finalOptions._digest = context.digest; - } - if (typeof context.withCredentials === "boolean") { - finalOptions.withCredentials = context.withCredentials; - } - if (context.maxContentLength) { - finalOptions.maxContentLength = context.maxContentLength; - } - if (context.maxBodyLength) { - finalOptions.maxBodyLength = context.maxBodyLength; - } - if (userOptions.hasOwnProperty("onUploadProgress")) { - finalOptions.onUploadProgress = userOptions["onUploadProgress"]; - } - // Take full control of all response status codes - finalOptions.validateStatus = () => true; - return finalOptions; -} - -export function request(requestOptions: RequestOptionsWithState): Promise { - // Client not configured for digest authentication - if (!requestOptions._digest) { - return _request(requestOptions); - } - - // Remove client's digest authentication object from request options - const _digest = requestOptions._digest; - delete requestOptions._digest; - - // If client is already using digest authentication, include the digest authorization header - if (_digest.hasDigestAuth) { - requestOptions = merge(requestOptions, { - headers: { - Authorization: generateDigestAuthHeader(requestOptions, _digest) - } - }); - } - - // Perform the request and handle digest authentication - return _request(requestOptions).then(function(response: Response) { - if (response.status == 401) { - _digest.hasDigestAuth = parseDigestAuth(response, _digest); - - if (_digest.hasDigestAuth) { - requestOptions = merge(requestOptions, { - headers: { - Authorization: generateDigestAuthHeader(requestOptions, _digest) - } - }); - - return _request(requestOptions).then(function(response2: Response) { - if (response2.status == 401) { - _digest.hasDigestAuth = false; - } else { - _digest.nc++; - } - return response2; - }); - } - } else { - _digest.nc++; - } - return response; - }); -} diff --git a/oaweb/src/oaer/libs/webdav/response.ts b/oaweb/src/oaer/libs/webdav/response.ts deleted file mode 100644 index cca7caa..0000000 --- a/oaweb/src/oaer/libs/webdav/response.ts +++ /dev/null @@ -1,46 +0,0 @@ -import minimatch from "minimatch"; -import { - FileStat, - Response, - ResponseDataDetailed, - WebDAVClientContext, - WebDAVClientError -} from "./types"; - -export function createErrorFromResponse(response: Response, prefix: string = ""): Error { - const err: WebDAVClientError = new Error( - `${prefix}Invalid response: ${response.status} ${response.statusText}` - ) as WebDAVClientError; - err.status = response.status; - err.response = response; - return err; -} - -export function handleResponseCode(context: WebDAVClientContext, response: Response): Response { - const { status } = response; - if (status === 401 && context.digest) return response; - if (status >= 400) { - const err = createErrorFromResponse(response); - throw err; - } - return response; -} - -export function processGlobFilter(files: Array, glob: string): Array { - return files.filter(file => minimatch(file.filename, glob, { matchBase: true })); -} - -export function processResponsePayload( - response: Response, - data: T, - isDetailed: boolean = false -): ResponseDataDetailed | T { - return isDetailed - ? { - data, - headers: response.headers || {}, - status: response.status, - statusText: response.statusText - } - : data; -} diff --git a/oaweb/src/oaer/libs/webdav/tools/crypto.ts b/oaweb/src/oaer/libs/webdav/tools/crypto.ts deleted file mode 100644 index 10bee66..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/crypto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import md5 from "md5"; - -export function ha1Compute( - algorithm: string, - user: string, - realm: string, - pass: string, - nonce: string, - cnonce: string -): string { - const ha1 = md5(`${user}:${realm}:${pass}`) as string; - if (algorithm && algorithm.toLowerCase() === "md5-sess") { - return md5(`${ha1}:${nonce}:${cnonce}`) as string; - } - return ha1; -} diff --git a/oaweb/src/oaer/libs/webdav/tools/dav.ts b/oaweb/src/oaer/libs/webdav/tools/dav.ts deleted file mode 100644 index 8267fa4..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/dav.ts +++ /dev/null @@ -1,171 +0,0 @@ -import path from "path-posix"; -import xmlParser from "fast-xml-parser"; -import nestedProp from "nested-property"; -import { decodeHTMLEntities } from "./encode"; -import { normalisePath } from "./path"; -import { - DAVResult, - DAVResultRaw, - DAVResultResponse, - DAVResultResponseProps, - DiskQuotaAvailable, - FileStat, - WebDAVClientError -} from "../types"; - -enum PropertyType { - Array = "array", - Object = "object", - Original = "original" -} - -function getPropertyOfType( - obj: Object, - prop: string, - type: PropertyType = PropertyType.Original -): any { - const val = nestedProp.get(obj, prop); - if (type === "array" && Array.isArray(val) === false) { - return [val]; - } else if (type === "object" && Array.isArray(val)) { - return val[0]; - } - return val; -} - -function normaliseResponse(response: any): DAVResultResponse { - const output = Object.assign({}, response); - nestedProp.set(output, "propstat", getPropertyOfType(output, "propstat", PropertyType.Object)); - nestedProp.set( - output, - "propstat.prop", - getPropertyOfType(output, "propstat.prop", PropertyType.Object) - ); - return output; -} - -function normaliseResult(result: DAVResultRaw): DAVResult { - const { multistatus } = result; - if (multistatus === "") { - return { - multistatus: { - response: [] - } - }; - } - if (!multistatus) { - throw new Error("Invalid response: No root multistatus found"); - } - const output: any = { - multistatus: Array.isArray(multistatus) ? multistatus[0] : multistatus - }; - nestedProp.set( - output, - "multistatus.response", - getPropertyOfType(output, "multistatus.response", PropertyType.Array) - ); - nestedProp.set( - output, - "multistatus.response", - nestedProp.get(output, "multistatus.response").map(response => normaliseResponse(response)) - ); - return output as DAVResult; -} - -export function parseXML(xml: string): Promise { - return new Promise(resolve => { - const result = xmlParser.parse(xml, { - arrayMode: false, - ignoreNameSpace: true - // // We don't use the processors here as decoding is done manually - // // later on - decoding early would break some path checks. - // attrValueProcessor: val => decodeHTMLEntities(decodeURIComponent(val)), - // tagValueProcessor: val => decodeHTMLEntities(decodeURIComponent(val)) - }); - resolve(normaliseResult(result)); - }); -} - -export function prepareFileFromProps( - props: DAVResultResponseProps, - rawFilename: string, - isDetailed: boolean = false -): FileStat { - // Last modified time, raw size, item type and mime - const { - getlastmodified: lastMod = null, - getcontentlength: rawSize = "0", - resourcetype: resourceType = null, - getcontenttype: mimeType = null, - getetag: etag = null - } = props; - const type = - resourceType && - typeof resourceType === "object" && - typeof resourceType.collection !== "undefined" - ? "directory" - : "file"; - const filename = decodeHTMLEntities(rawFilename); - const stat: FileStat = { - filename, - basename: path.basename(filename), - lastmod: lastMod, - size: parseInt(rawSize, 10), - type, - etag: typeof etag === "string" ? etag.replace(/"/g, "") : null - }; - if (type === "file") { - stat.mime = mimeType && typeof mimeType === "string" ? mimeType.split(";")[0] : ""; - } - if (isDetailed) { - stat.props = props; - } - return stat; -} - -export function parseStat( - result: DAVResult, - filename: string, - isDetailed: boolean = false -): FileStat { - let responseItem: DAVResultResponse = null; - try { - responseItem = result.multistatus.response[0]; - } catch (e) { - /* ignore */ - } - if (!responseItem) { - throw new Error("Failed getting item stat: bad response"); - } - const { - propstat: { prop: props, status: statusLine } - } = responseItem; - - // As defined in https://tools.ietf.org/html/rfc2068#section-6.1 - const [_, statusCodeStr, statusText] = statusLine.split(" ", 3); - const statusCode = parseInt(statusCodeStr, 10); - if (statusCode >= 400) { - const err: WebDAVClientError = new Error( - `Invalid response: ${statusCode} ${statusText}` - ) as WebDAVClientError; - err.status = statusCode; - throw err; - } - - const filePath = normalisePath(filename); - return prepareFileFromProps(props, filePath, isDetailed); -} - -export function translateDiskSpace(value: string | number): DiskQuotaAvailable { - switch (value.toString()) { - case "-3": - return "unlimited"; - case "-2": - /* falls-through */ - case "-1": - // -1 is non-computed - return "unknown"; - default: - return parseInt(value as string, 10); - } -} diff --git a/oaweb/src/oaer/libs/webdav/tools/encode.ts b/oaweb/src/oaer/libs/webdav/tools/encode.ts deleted file mode 100644 index 9b3e34a..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/encode.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { decode, encode } from "base-64"; - -declare var WEB: boolean; - -export function decodeHTMLEntities(text: string): string { - if (typeof WEB === "undefined") { - // Node - const he = require("he"); - return he.decode(text); - } else { - // Nasty browser way - const txt = document.createElement("textarea"); - txt.innerHTML = text; - return txt.value; - } -} - -export function fromBase64(text: string): string { - return decode(text); -} - -export function toBase64(text: string): string { - return encode(text); -} diff --git a/oaweb/src/oaer/libs/webdav/tools/headers.ts b/oaweb/src/oaer/libs/webdav/tools/headers.ts deleted file mode 100644 index 65236f0..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/headers.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Headers } from "../types"; - -export function mergeHeaders(...headerPayloads: Headers[]): Headers { - if (headerPayloads.length === 0) return {}; - const headerKeys = {}; - return headerPayloads.reduce((output: Headers, headers: Headers) => { - Object.keys(headers).forEach(header => { - const lowerHeader = header.toLowerCase(); - if (headerKeys.hasOwnProperty(lowerHeader)) { - output[headerKeys[lowerHeader]] = headers[header]; - } else { - headerKeys[lowerHeader] = header; - output[header] = headers[header]; - } - }); - return output; - }, {}); -} diff --git a/oaweb/src/oaer/libs/webdav/tools/merge.ts b/oaweb/src/oaer/libs/webdav/tools/merge.ts deleted file mode 100644 index a92f708..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/merge.ts +++ /dev/null @@ -1,62 +0,0 @@ -export function cloneShallow(obj: T): T { - return isPlainObject(obj) - ? Object.assign({}, obj) - : Object.setPrototypeOf(Object.assign({}, obj), Object.getPrototypeOf(obj)); -} - -function isPlainObject(obj: Object | any): boolean { - if ( - typeof obj !== "object" || - obj === null || - Object.prototype.toString.call(obj) != "[object Object]" - ) { - // Not an object - return false; - } - if (Object.getPrototypeOf(obj) === null) { - return true; - } - let proto = obj; - // Find the prototype - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - return Object.getPrototypeOf(obj) === proto; -} - -export function merge(...args: Object[]) { - let output = null, - items = [...args]; - while (items.length > 0) { - const nextItem = items.shift(); - if (!output) { - output = cloneShallow(nextItem); - } else { - output = mergeObjects(output, nextItem); - } - } - return output; -} - -function mergeObjects(obj1: Object, obj2: Object): Object { - const output = cloneShallow(obj1); - Object.keys(obj2).forEach(key => { - if (!output.hasOwnProperty(key)) { - output[key] = obj2[key]; - return; - } - if (Array.isArray(obj2[key])) { - output[key] = Array.isArray(output[key]) - ? [...output[key], ...obj2[key]] - : [...obj2[key]]; - } else if (typeof obj2[key] === "object" && !!obj2[key]) { - output[key] = - typeof output[key] === "object" && !!output[key] - ? mergeObjects(output[key], obj2[key]) - : cloneShallow(obj2[key]); - } else { - output[key] = obj2[key]; - } - }); - return output; -} diff --git a/oaweb/src/oaer/libs/webdav/tools/path.ts b/oaweb/src/oaer/libs/webdav/tools/path.ts deleted file mode 100644 index bb786fc..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/path.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { dirname } from "path-posix"; - -const SEP_PATH_POSIX = "__PATH_SEPARATOR_POSIX__"; -const SEP_PATH_WINDOWS = "__PATH_SEPARATOR_WINDOWS__"; - -export function encodePath(path) { - const replaced = path.replace(/\//g, SEP_PATH_POSIX).replace(/\\\\/g, SEP_PATH_WINDOWS); - const formatted = encodeURIComponent(replaced); - return formatted - .split(SEP_PATH_WINDOWS) - .join("\\\\") - .split(SEP_PATH_POSIX) - .join("/"); -} - -export function getAllDirectories(path: string): Array { - if (!path || path === "/") return []; - let currentPath = path; - const output: Array = []; - do { - output.push(currentPath); - currentPath = dirname(currentPath); - } while (currentPath && currentPath !== "/"); - return output; -} - -export function normalisePath(pathStr: string): string { - let normalisedPath = pathStr; - if (normalisedPath[0] !== "/") { - normalisedPath = "/" + normalisedPath; - } - if (/^.+\/$/.test(normalisedPath)) { - normalisedPath = normalisedPath.substr(0, normalisedPath.length - 1); - } - return normalisedPath; -} diff --git a/oaweb/src/oaer/libs/webdav/tools/quota.ts b/oaweb/src/oaer/libs/webdav/tools/quota.ts deleted file mode 100644 index 4adf650..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/quota.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { translateDiskSpace } from "./dav"; -import { DAVResult, DiskQuota } from "../types"; - -export function parseQuota(result: DAVResult): DiskQuota | null { - try { - const [responseItem] = result.multistatus.response; - const { - propstat: { - prop: { "quota-used-bytes": quotaUsed, "quota-available-bytes": quotaAvail } - } - } = responseItem; - return typeof quotaUsed !== "undefined" && typeof quotaAvail !== "undefined" - ? { - used: parseInt(quotaUsed, 10), - available: translateDiskSpace(quotaAvail) - } - : null; - } catch (err) { - /* ignore */ - } - return null; -} diff --git a/oaweb/src/oaer/libs/webdav/tools/size.ts b/oaweb/src/oaer/libs/webdav/tools/size.ts deleted file mode 100644 index f5b09f0..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/size.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Layerr } from "layerr"; -import { isArrayBuffer } from "../compat/arrayBuffer"; -import { isBuffer } from "../compat/buffer"; -import { BufferLike, ErrorCode } from "../types"; - -export function calculateDataLength(data: string | BufferLike): number { - if (isArrayBuffer(data)) { - return (data).byteLength; - } else if (isBuffer(data)) { - return (data).length; - } else if (typeof data === "string") { - return (data).length; - } - throw new Layerr( - { - info: { - code: ErrorCode.DataTypeNoLength - } - }, - "Cannot calculate data length: Invalid type" - ); -} diff --git a/oaweb/src/oaer/libs/webdav/tools/url.ts b/oaweb/src/oaer/libs/webdav/tools/url.ts deleted file mode 100644 index 94ca9d4..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/url.ts +++ /dev/null @@ -1,32 +0,0 @@ -import URL from "url-parse"; -import _joinURL from "url-join"; -import { normalisePath } from "./path"; - -export function extractURLPath(fullURL: string): string { - const url = new URL(fullURL); - let urlPath = url.pathname; - if (urlPath.length <= 0) { - urlPath = "/"; - } - return normalisePath(urlPath); -} - -export function joinURL(...parts: Array): string { - return _joinURL( - parts.reduce((output, nextPart, partIndex) => { - if ( - partIndex === 0 || - nextPart !== "/" || - (nextPart === "/" && output[output.length - 1] !== "/") - ) { - output.push(nextPart); - } - return output; - }, []) - ); -} - -export function normaliseHREF(href: string): string { - const normalisedHref = href.replace(/^https?:\/\/[^\/]+/, ""); - return normalisedHref; -} diff --git a/oaweb/src/oaer/libs/webdav/tools/xml.ts b/oaweb/src/oaer/libs/webdav/tools/xml.ts deleted file mode 100644 index ba2e588..0000000 --- a/oaweb/src/oaer/libs/webdav/tools/xml.ts +++ /dev/null @@ -1,55 +0,0 @@ -import xmlParser, { j2xParser as XMLParser } from "fast-xml-parser"; - -export function generateLockXML(ownerHREF: string): string { - return getParser().parse( - namespace( - { - lockinfo: { - "@_xmlns:d": "DAV:", - lockscope: { - exclusive: {} - }, - locktype: { - write: {} - }, - owner: { - href: ownerHREF - } - } - }, - "d" - ) - ); -} - -function getParser(): XMLParser { - return new XMLParser({ - attributeNamePrefix: "@_", - format: true, - ignoreAttributes: false, - supressEmptyNode: true - }); -} - -function namespace(obj: T, ns: string): T { - const copy = { ...obj }; - for (const key in copy) { - if (copy[key] && typeof copy[key] === "object" && key.indexOf(":") === -1) { - copy[`${ns}:${key}`] = namespace(copy[key], ns); - delete copy[key]; - } else if (/^@_/.test(key) === false) { - copy[`${ns}:${key}`] = copy[key]; - delete copy[key]; - } - } - return copy; -} - -export function parseGenericResponse(xml: string): Object { - return xmlParser.parse(xml, { - arrayMode: false, - ignoreNameSpace: true, - parseAttributeValue: true, - parseNodeValue: true - }); -} diff --git a/oaweb/src/oaer/libs/webdav/types.ts b/oaweb/src/oaer/libs/webdav/types.ts deleted file mode 100644 index e0d6c1e..0000000 --- a/oaweb/src/oaer/libs/webdav/types.ts +++ /dev/null @@ -1,288 +0,0 @@ -import Stream from "stream"; - -export type AuthHeader = string; - -export enum AuthType { - Digest = "digest", - None = "none", - Password = "password", - Token = "token" -} - -export type BufferLike = Buffer | ArrayBuffer; - -export interface CreateDirectoryOptions extends WebDAVMethodOptions { - recursive?: boolean; -} - -export interface CreateReadStreamOptions extends WebDAVMethodOptions { - callback?: (response: Response) => void; - range?: { - start: number; - end?: number; - }; -} - -export type CreateWriteStreamCallback = (response: Response) => void; - -export interface CreateWriteStreamOptions extends WebDAVMethodOptions { - overwrite?: boolean; -} - -export interface DAVResultResponse { - href: string; - propstat: { - prop: DAVResultResponseProps; - status: string; - }; -} - -export interface DAVResultResponseProps { - displayname: string; - resourcetype: { - collection?: boolean; - }; - getlastmodified?: string; - getetag?: string; - getcontentlength?: string; - getcontenttype?: string; - "quota-available-bytes"?: any; - "quota-used-bytes"?: string; -} - -export interface DAVResult { - multistatus: { - response: Array; - }; -} - -export interface DAVResultRawMultistatus { - response: DAVResultResponse | [DAVResultResponse]; -} - -export interface DAVResultRaw { - multistatus: "" | DAVResultRawMultistatus | [DAVResultRawMultistatus]; -} - -export interface DigestContext { - username: string; - password: string; - nc: number; - algorithm: string; - hasDigestAuth: boolean; - cnonce?: string; - nonce?: string; - realm?: string; - qop?: string; - opaque?: string; -} - -export interface DiskQuota { - used: number; - available: DiskQuotaAvailable; -} - -export type DiskQuotaAvailable = "unknown" | "unlimited" | number; - -export enum ErrorCode { - DataTypeNoLength = "data-type-no-length", - InvalidAuthType = "invalid-auth-type", - InvalidOutputFormat = "invalid-output-format", - LinkUnsupportedAuthType = "link-unsupported-auth" -} - -export interface FileStat { - filename: string; - basename: string; - lastmod: string; - size: number; - type: "file" | "directory"; - etag: string | null; - mime?: string; - props?: DAVResultResponseProps; -} - -export interface GetDirectoryContentsOptions extends WebDAVMethodOptions { - deep?: boolean; - details?: boolean; - glob?: string; -} - -export interface GetFileContentsOptions extends WebDAVMethodOptions { - details?: boolean; - format?: "binary" | "text"; -} - -export interface GetQuotaOptions extends WebDAVMethodOptions { - details?: boolean; -} - -export interface Headers { - [key: string]: string; -} - -export interface LockOptions extends WebDAVMethodOptions { - refreshToken?: string; - timeout?: string; -} - -export interface LockResponse { - serverTimeout: string; - token: string; -} - -export interface OAuthToken { - access_token: string; - token_type: string; - refresh_token?: string; -} - -export interface PutFileContentsOptions extends WebDAVMethodOptions { - contentLength?: boolean | number; - overwrite?: boolean; - onUploadProgress?: UploadProgressCallback; -} - -export type RequestDataPayload = string | Buffer | ArrayBuffer | { [key: string]: any }; - -interface RequestOptionsBase { - data?: RequestDataPayload; - headers?: Headers; - httpAgent?: any; - httpsAgent?: any; - maxBodyLength?: number; - maxContentLength?: number; - maxRedirects?: number; - method: string; - onUploadProgress?: UploadProgressCallback; - responseType?: string; - transformResponse?: Array<(value: any) => any>; - url?: string; - validateStatus?: (status: number) => boolean; - withCredentials?: boolean; -} - -export interface RequestOptionsCustom extends RequestOptionsBase {} - -export interface RequestOptions extends RequestOptionsBase { - url: string; -} - -export interface RequestOptionsWithState extends RequestOptions { - _digest?: DigestContext; -} - -export interface Response { - data: ResponseData; - status: number; - headers: Headers; - statusText: string; -} - -export type ResponseData = string | Buffer | ArrayBuffer | Object | Array; - -export interface ResponseDataDetailed { - data: T; - headers: Headers; - status: number; - statusText: string; -} - -export interface ResponseStatusValidator { - (status: number): boolean; -} - -export interface StatOptions extends WebDAVMethodOptions { - details?: boolean; -} - -export interface UploadProgress { - loaded: number; - total: number; -} - -export interface UploadProgressCallback { - (progress: UploadProgress): void; -} - -export interface WebDAVClient { - copyFile: (filename: string, destination: string) => Promise; - createDirectory: (path: string, options?: CreateDirectoryOptions) => Promise; - createReadStream: (filename: string, options?: CreateReadStreamOptions) => Stream.Readable; - createWriteStream: ( - filename: string, - options?: CreateWriteStreamOptions, - callback?: CreateWriteStreamCallback - ) => Stream.Writable; - customRequest: (path: string, requestOptions: RequestOptionsCustom) => Promise; - deleteFile: (filename: string) => Promise; - exists: (path: string) => Promise; - getDirectoryContents: ( - path: string, - options?: GetDirectoryContentsOptions - ) => Promise | ResponseDataDetailed>>; - getFileContents: ( - filename: string, - options?: GetFileContentsOptions - ) => Promise>; - getFileDownloadLink: (filename: string) => string; - getFileUploadLink: (filename: string) => string; - getHeaders: () => Headers; - getQuota: ( - options?: GetQuotaOptions - ) => Promise>; - lock: (path: string, options?: LockOptions) => Promise; - moveFile: (filename: string, destinationFilename: string) => Promise; - putFileContents: ( - filename: string, - data: string | BufferLike | Stream.Readable, - options?: PutFileContentsOptions - ) => Promise; - setHeaders: (headers: Headers) => void; - stat: ( - path: string, - options?: StatOptions - ) => Promise>; - unlock: (path: string, token: string, options?: WebDAVMethodOptions) => Promise; -} - -export interface WebDAVClientContext { - authType: AuthType; - contactHref: string; - digest?: DigestContext; - headers: Headers; - httpAgent?: any; - httpsAgent?: any; - maxBodyLength?: number; - maxContentLength?: number; - password?: string; - remotePath: string; - remoteURL: string; - token?: OAuthToken; - username?: string; - withCredentials?: boolean; -} - -export interface WebDAVClientError extends Error { - status?: number; - response?: Response; -} - -export interface WebDAVClientOptions { - authType?: AuthType; - contactHref?: string; - headers?: Headers; - httpAgent?: any; - httpsAgent?: any; - maxBodyLength?: number; - maxContentLength?: number; - password?: string; - token?: OAuthToken; - username?: string; - withCredentials?: boolean; -} - -export interface WebDAVMethodOptions { - data?: RequestDataPayload; - headers?: Headers; -} diff --git a/oaweb/src/oaer/main.vue b/oaweb/src/oaer/main.vue deleted file mode 100644 index 101aaf1..0000000 --- a/oaweb/src/oaer/main.vue +++ /dev/null @@ -1,135 +0,0 @@ - - - - diff --git a/oaweb/src/oaer/models/index.ts b/oaweb/src/oaer/models/index.ts deleted file mode 100644 index 64df416..0000000 --- a/oaweb/src/oaer/models/index.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* -* @name: index -* @author: veypi -* @date: 2021-11-18 17:36 -* @description:index -*/ - - -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 - - au: modelsAppUser -} - -export enum AUStatus { - OK = 0, - Disabled = 1, - Applying = 2, - Deny = 3, -} - -export interface modelsAppUser { - app_id: string - user_id: string - status: AUStatus -} - -export interface modelsUser { - id: string - created: string - updated: string - delete_flag: boolean - username: string - nickname: string - email: string - phone: string - icon: string - status: number - used: number - space: number -} - diff --git a/oaweb/src/oaer/shims-vue.d.ts b/oaweb/src/oaer/shims-vue.d.ts deleted file mode 100644 index 4e6894b..0000000 --- a/oaweb/src/oaer/shims-vue.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable */ - -/// - -// Mocks all files ending in `.vue` showing them as plain Vue instances -declare module '*.vue' { - import type { DefineComponent } from 'vue'; - const component: DefineComponent<{}, {}, any>; - export default component; -} diff --git a/oaweb/src/pages/IndexPage.vue b/oaweb/src/pages/IndexPage.vue index 32f1e7d..2409f25 100644 --- a/oaweb/src/pages/IndexPage.vue +++ b/oaweb/src/pages/IndexPage.vue @@ -16,7 +16,7 @@

应用中心

-
+
diff --git a/oaweb/yarn.lock b/oaweb/yarn.lock index a9dfc2f..54d073c 100644 --- a/oaweb/yarn.lock +++ b/oaweb/yarn.lock @@ -465,8 +465,10 @@ resolved "https://registry.yarnpkg.com/@veypi/msg/-/msg-0.1.0.tgz#2ebe899527a11ed11f68c2c96f468cfcc66ad3d4" integrity sha512-58dj5nnpHsxaiK5sbPiDK5t8OF4uvN+kAmWhU0BRAgXHpkxkZNZ8rn7hXvSVybG1BbM8EuMNkq0lIxGYNKl8aw== -"@veypi/oaer@file:../../../test/oaer": +"@veypi/oaer@^0.0.1": version "0.0.1" + resolved "https://registry.yarnpkg.com/@veypi/oaer/-/oaer-0.0.1.tgz#b22ebaf72a7bfd5abf62f099b72b533a8cf27abd" + integrity sha512-ILY8SXK7yihH2/qhUFfPbD2FNE7O4IHh7Q9Z1A+Ild6zonCb3RM7LcOBM4/9WriEi3mPdBNmFfuoP8YFCIxHuA== dependencies: "@veypi/msg" "^0.1.0" "@veypi/one-icon" "2" @@ -474,8 +476,6 @@ autoprefixer "^10.4.16" axios "^1.5.1" js-base64 "^3.7.5" - mitt "^3.0.1" - path "^0.12.7" postcss "^8.4.31" tailwindcss "^3.3.3" vue "^3.3.4" @@ -2098,11 +2098,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - inquirer@^8.2.1: version "8.2.6" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" @@ -2744,14 +2739,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -path@^0.12.7: - version "0.12.7" - resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" - integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== - dependencies: - process "^0.11.1" - util "^0.10.3" - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -2857,11 +2844,6 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@^0.11.1: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -3477,13 +3459,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -util@^0.10.3: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== - dependencies: - inherits "2.0.3" - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"