WSL/SLF GitLab Repository

Commit 5ec1b75a authored by Sam's avatar Sam
Browse files

style: add eslint-config-prettier, run pre-commit hooks

parent 325e12d1
......@@ -12,7 +12,12 @@ module.exports = {
// env: {
// browser: true,
// },
extends: ['@vue/airbnb', 'eslint:recommended', 'plugin:vue/essential'],
extends: [
'@vue/airbnb',
'eslint:recommended',
'plugin:vue/essential',
'prettier',
],
// // required to lint *.vue files
plugins: [
......
......@@ -13,11 +13,13 @@ repos:
- id: prettier
args:
[
"--ignore-unknown",
"--no-error-on-unmatched-pattern",
"!chart/**",
"--ignore-path",
".dockerignore",
'--single-quote',
'--trailing-comma=all',
'--ignore-unknown',
'--no-error-on-unmatched-pattern',
'!chart/**',
'--ignore-path',
'.dockerignore',
]
# Lint: Javascript
......@@ -25,11 +27,11 @@ repos:
rev: v8.17.0
hooks:
- id: eslint
args: ["--ext", ".js,.ts,.vue"]
args: ['--ext', '.js,.ts,.vue']
# Lint: Markdown
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.31.1
hooks:
- id: markdownlint
args: ["--fix", "--disable", "MD024"]
args: ['--fix', '--disable', 'MD024']
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/07867b83bc9244c8ada67e5f7df03ac4)](https://www.codacy.com/gh/EnviDat/S3_Browsing/dashboard?utm_source=github.com&utm_medium=referral&utm_content=EnviDat/S3_Browsing&utm_campaign=Badge_Grade)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/07867b83bc9244c8ada67e5f7df03ac4)](https://www.codacy.com/gh/EnviDat/S3_Browsing/dashboard?utm_source=github.com&utm_medium=referral&utm_content=EnviDat/S3_Browsing&utm_campaign=Badge_Grade)
[![DeepScan grade](https://deepscan.io/api/teams/6114/projects/13957/branches/248737/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=6114&pid=13957&bid=248737)
# S3 Browsing Web Frontend
Web file browser for a S3 backend. Browse and download files in a
Web file browser for a S3 backend. Browse and download files in a
file tree structure.
## Features
......@@ -13,11 +13,9 @@ file tree structure.
- prefix query parameter to start from a certain folder
- It works **only for public S3 Buckets** (by version 1.2.x)
Check https://envicloud.wsl.ch/ as a demo.
See the prefix parameter in action: https://envicloud.wsl.ch/#/?prefix=slf/
## Install
After cloning use
......@@ -33,17 +31,17 @@ To create a build use `npm run build` or `npm run build --modern`
the different urls to point to your backend.**
## Local development
However you make the config, depending on your server you might still get CORS Network Error for backend calls. Since default server setups usually don't allow the any origin.
To still be able to test locally your can run [chrome with disabled web security](https://stackoverflow.com/questions/3102819/disable-same-origin-policy-in-chrome)
## Configuration
There are two ways to you can adjust the configurations
of this app. With a config.json as the main
of this app. With a config.json as the main
Either change the VUE_APP_\* variables in the .env.development and .env.production files. These are the "fallback variables" when the config.json
Either change the VUE*APP*\* variables in the .env.development and .env.production files. These are the "fallback variables" when the config.json
can't be loaded from the backend.
The simplest setup is to change these variables, however for any changes of them
......@@ -53,7 +51,7 @@ you have to re-build to frontend and re-deploy it. To avoid that just set the VU
#### Minimal .env.production config for the contentUrl
Two ways to setup, either only use the VUE_APP_\* variables to
Two ways to setup, either only use the VUE*APP*\* variables to
set the contentUrl to load the S3 Content to browse through, like this:
VUE_APP_CONTENT_URL=http://www.domain.something/possibleSubdomain/
......@@ -76,7 +74,6 @@ This file would need to have at least the contentUrl defined, like so:
"contentUrl": "http://www.domain.something/possibleSubdomain/"
}
#### Minimal .env.development config.json file
For this setup to work the public folder in the root folder of this projects
......@@ -90,7 +87,6 @@ Here are exmaples for the full definition either in the .env files or in the con
Aside from the contentUrl the other options are mostly relevant for the "Other Protocols" Card and are only going to take effect when the `VUE_APP_SHOW_PROTOCOLS` or `showProtocols` is set to `true`
#### Example a full .env.development / .env.production config only
VUE_APP_USE_TESTDATA=false
......@@ -104,7 +100,6 @@ Aside from the contentUrl the other options are mostly relevant for the "Other P
VUE_APP_CYBERDUCK_PROFILE_NAME=envicloud.cyberduck.profile
VUE_APP_WEBDAV_DOMAIN_HTTPS=https://envicloud.wsl.ch/webdav/
For more information about [how the .env files work check the dotenv package](https://www.npmjs.com/package/dotenv)
#### About the VUE_APP_USE_TESTDATA variable
......@@ -154,36 +149,32 @@ The `urlPrefix` is a query parameter from the url
[More about Cyberduck ](https://www.cyberduck.io/)
### Configuration Options
| Option | Usage | Type | Required |
| ------ | ----- | ----- | ----- |
| contentUrl | The backend end point from where the S3 XML is served. | String | True |
| downloadDomain | The downloadDomain is used for the url of a file in case of download and copy the link. It falls back to the `contentUrl` if not filled. | String | Optional |
| defaultMaxKeys | Is used as the max-keys parameter for the S3 request. Might be needed when making a pagination. Defaults to `10000`. | String | Optional |
| showProtocols | If `true` the "Other Protocols" Card will be shown. | String | Optional |
| vendorUrl | This is the institution or company which provides the cyberduck profile. | String | Optional, needs `showProtocols` to be `true` |
| cyberduckHostName | The main domain for the cyberduck connection. Should include no subdomains. | String | Optional, needs `showProtocols` to be `true` |
| cyberduckProfileName | The name of the downloaded file. It will be [cyberduckProfileName].cyberduckprofile . | String | Optional, needs `showProtocols` to be `true` |
| webDAVDomainHttps | The webDAV https url for browsing via webDAV. | String | Optional, needs `showProtocols` to be `true` |
| Option | Usage | Type | Required |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------ | -------------------------------------------- |
| contentUrl | The backend end point from where the S3 XML is served. | String | True |
| downloadDomain | The downloadDomain is used for the url of a file in case of download and copy the link. It falls back to the `contentUrl` if not filled. | String | Optional |
| defaultMaxKeys | Is used as the max-keys parameter for the S3 request. Might be needed when making a pagination. Defaults to `10000`. | String | Optional |
| showProtocols | If `true` the "Other Protocols" Card will be shown. | String | Optional |
| vendorUrl | This is the institution or company which provides the cyberduck profile. | String | Optional, needs `showProtocols` to be `true` |
| cyberduckHostName | The main domain for the cyberduck connection. Should include no subdomains. | String | Optional, needs `showProtocols` to be `true` |
| cyberduckProfileName | The name of the downloaded file. It will be [cyberduckProfileName].cyberduckprofile . | String | Optional, needs `showProtocols` to be `true` |
| webDAVDomainHttps | The webDAV https url for browsing via webDAV. | String | Optional, needs `showProtocols` to be `true` |
Here you get further information about the [cyberduck profile xml](https://trac.cyberduck.io/wiki/help/en/howto/profiles)
## Testing
Use
Use
npm run test:unit
for running the unit tests.
For now only the s3Factory methods are being tested.
## Know Issues
- Rendering large amounts of folder and files is still pretty slow. For the https://envicloud.wsl.ch we are having folders which have >1k or even >4k files which makes the rendering from the v-tree-view component of vuetify very slow. To handle such large amounts of entries a virtual list is needed, which will probably be implemented in the future.
- Bulk downloading files, for downloading mutliple files at once you need to use a different protocol / client, make sure to enable the `showProtocols` option.
- Multiple entires >10k as mentioned the rendering isn't performant, so this issue isn't tackled yet, but if for any folder there are more than 10k entires the needs to be a pagniation of sorts or at least multiple requests to the backend. The default server side maximum seems to be 10k, this might be configurable, so how. For a multiple request scenario the `Marker` parameter can be used make any futher calls. The `Marker` would be the last key which was provided from the last request and from there the new request should provide again the amount given with the max-keys parameter or the server side maxium. (As of version 1.2.x such a scenario isn't implemented yet.)
......@@ -13,5 +13,5 @@ module.exports = {
'@babel/plugin-proposal-optional-chaining',
],
},
},
},
};
......@@ -37,6 +37,7 @@
"@vue/test-utils": "^1.0.3",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.15.0",
"eslint-import-resolver-babel-module": "^5.3.1",
"eslint-plugin-vue": "^6.2.2",
"eslint-plugin-vuetify": "^1.1.0",
......@@ -11184,6 +11185,21 @@
"eslint-plugin-import": "^2.21.2"
}
},
"node_modules/eslint-config-prettier": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz",
"integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==",
"dev": true,
"dependencies": {
"get-stdin": "^6.0.0"
},
"bin": {
"eslint-config-prettier-check": "bin/cli.js"
},
"peerDependencies": {
"eslint": ">=3.14.1"
}
},
"node_modules/eslint-import-resolver-babel-module": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-babel-module/-/eslint-import-resolver-babel-module-5.3.1.tgz",
......@@ -12829,6 +12845,15 @@
"node": ">=4"
}
},
"node_modules/get-stdin": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
"integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
......@@ -35534,6 +35559,15 @@
"object.entries": "^1.1.2"
}
},
"eslint-config-prettier": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz",
"integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==",
"dev": true,
"requires": {
"get-stdin": "^6.0.0"
}
},
"eslint-import-resolver-babel-module": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-babel-module/-/eslint-import-resolver-babel-module-5.3.1.tgz",
......@@ -36774,6 +36808,12 @@
"npm-conf": "^1.1.0"
}
},
"get-stdin": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
"integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
"dev": true
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900&display=swap">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900&display=swap"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css"
/>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
......
<template>
<v-app>
<v-app-bar app
color="secondary"
dark >
<v-row no-gutters
align="center"
justify="space-between">
<v-app-bar app color="secondary" dark>
<v-row no-gutters align="center" justify="space-between">
<v-col>
<v-row no-gutters
align="center" >
<v-col class="shrink"
style="cursor: pointer;"
@click="navigateTo('/')" >
<v-img :src="this.imagesPng('./S3-Logo.png')" />
<v-row no-gutters align="center">
<v-col
class="shrink"
style="cursor: pointer"
@click="navigateTo('/')"
>
<v-img :src="this.imagesPng('./S3-Logo.png')" />
</v-col>
<v-col class="hidden-xs-only ml-4"
style="cursor: pointer;"
@click="navigateTo('/')" >
<v-col
class="hidden-xs-only ml-4"
style="cursor: pointer"
@click="navigateTo('/')"
>
<v-row no-gutters>
<v-col class="text-h5">{{ appTitle }}</v-col>
<v-col class="text-h5">{{ appTitle }}</v-col>
</v-row>
<v-row no-gutters>
<v-col style="line-height: 0.5rem; font-size: 0.5rem !important;" class="text-body-2">{{ version }}</v-col>
<v-col
style="line-height: 0.5rem; font-size: 0.5rem !important"
class="text-body-2"
>{{ version }}</v-col
>
</v-row>
</v-col>
</v-row>
</v-col>
<v-col v-if="loading"
style="text-align: center;">
<v-col v-if="loading" style="text-align: center">
<span class="text-sm-body-1 text-body-2">{{ loadingText }}</span>
</v-col>
<v-col v-if="!loading && contentBucketName"
style="text-align: center;">
<span class="text-sm-body-1 text-body-2">Bucket: {{ contentBucketName }}</span>
<v-col v-if="!loading && contentBucketName" style="text-align: center">
<span class="text-sm-body-1 text-body-2"
>Bucket: {{ contentBucketName }}</span
>
</v-col>
<v-col >
<v-col>
<v-row no-gutters justify="end">
<v-col class="shrink" >
<IconButton icon="mdi-help-circle"
tooltipText="About the S3 Browser"
:clickCallback="() => { navigateTo('About'); }" />
<v-col class="shrink">
<IconButton
icon="mdi-help-circle"
tooltipText="About the S3 Browser"
:clickCallback="
() => {
navigateTo('About');
}
"
/>
</v-col>
<v-col class="shrink" >
<IconButton icon="mdi-code-tags"
tooltipText="Source Code on GitHub"
url="https://github.com/EnviDat/S3_Browsing" />
<v-col class="shrink">
<IconButton
icon="mdi-code-tags"
tooltipText="Source Code on GitHub"
url="https://github.com/EnviDat/S3_Browsing"
/>
</v-col>
</v-row>
</v-col>
</v-row>
</v-app-bar>
<v-main>
<router-view @showSnack="catchShowSnack" />
</v-main>
<v-snackbar v-model="snackbar"
:timeout="timeout"
top
right
:color="snackColor"
elevation="5" >
<v-snackbar
v-model="snackbar"
:timeout="timeout"
top
right
:color="snackColor"
elevation="5"
>
{{ snackText }}
<template v-slot:action="{ attrs }">
<v-btn color="white"
icon
v-bind="attrs"
@click="snackbar = false" >
<v-btn color="white" icon v-bind="attrs" @click="snackbar = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</template>
......@@ -88,18 +89,16 @@
<v-footer>
<v-spacer></v-spacer>
<div class="text-caption" >Developed by the <a href="https://www.envidat.ch/"
target="_blank" >EnviDat</a> team
<div class="text-caption">
Developed by the
<a href="https://www.envidat.ch/" target="_blank">EnviDat</a> team
</div>
</v-footer>
</v-app>
</template>
<script>
import {
mapState,
mapGetters,
} from 'vuex';
import { mapState, mapGetters } from 'vuex';
import IconButton from '@/components/IconButton';
......@@ -110,23 +109,16 @@ const configURL = process.env.VUE_APP_CONFIG_URL;
export default {
name: 'App',
computed: {
...mapGetters([
'contentUrl',
'contentBucketName',
]),
...mapState([
'configLoading',
'contentLoading',
'imagesPng',
]),
...mapGetters(['contentUrl', 'contentBucketName']),
...mapState(['configLoading', 'contentLoading', 'imagesPng']),
loading() {
return this.configLoading || this.contentLoading;
},
loadingText() {
if (this.configLoading) {
return `Loading config from ${configURL}`;
}
}
if (this.contentLoading) {
return `Loading S3 Bucket from ${this.contentUrl}`;
}
......
<template>
<v-card max-height="100%">
<v-card-title primary-title>
<v-row no-gutters align="center">
<v-col v-if="expanded">
{{ title }}
</v-col>
<v-col class="shrink"
cols="1">
<v-btn icon
:disabled="loading"
@click="$emit('expand')">
<v-col class="shrink" cols="1">
<v-btn icon :disabled="loading" @click="$emit('expand')">
<v-icon>mdi-information</v-icon>
</v-btn>
</v-col>
</v-row>
</v-card-title>
<v-card-text v-if="expanded"
:style="`max-height: ${height}; overflow:auto;`">
<v-text-field :value="name"
label="name"
id="0"
readonly />
<v-text-field class="pt-1"
:value="url"
label="url"
id="1"
readonly />
<v-card-text
v-if="expanded"
:style="`max-height: ${height}; overflow:auto;`"
>
<v-text-field :value="name" label="name" id="0" readonly />
<v-text-field class="pt-1"
:value="prefix"
label="prefix"
id="2"
readonly />
<v-text-field class="pt-1" :value="url" label="url" id="1" readonly />
<v-text-field class="pt-1"
:value="maxKeys"
label="maxKeys"
id="3"
readonly />
<v-text-field
class="pt-1"
:value="prefix"
label="prefix"
id="2"
readonly
/>
<v-text-field class="pt-1"
:value="delimiter"
label="delimiter"
id="4"
readonly />
<v-text-field
class="pt-1"
:value="maxKeys"
label="maxKeys"
id="3"
readonly
/>
<v-checkbox class="pt-1"
label="isTruncated"
:value="isTruncated"
id="5"
readonly />
<v-text-field
class="pt-1"
:value="delimiter"
label="delimiter"
id="4"
readonly
/>
<v-text-field class="pt-1"
:value="marker"
label="marker"
id="6"
readonly />
<v-checkbox
class="pt-1"
label="isTruncated"
:value="isTruncated"
id="5"
readonly
/>
<v-text-field
class="pt-1"
:value="marker"
label="marker"
id="6"
readonly
/>
</v-card-text>
</v-card>
</template>
<script>
export default {
name: 'BucketCard',
props: {
......@@ -91,7 +88,6 @@ export default {
data: () => ({
title: 'List Bucket Infos',
}),
computed: {
},
computed: {},
};
</script>
<template>
<v-card :loading="loading" >
<v-card-title primary-title
:style="`background-color:${highlightTitle ? $vuetify.theme.themes.light.secondary : ''};
transition: 0.5s all; line-height: 1.6rem;`" >
{{ `Entire Directory Download of "${selectedFolder ? selectedFolder : 'root'}"` }}
<v-card :loading="loading">
<v-card-title
primary-title
:style="`background-color:${
highlightTitle ? $vuetify.theme.themes.light.secondary : ''
};
transition: 0.5s all; line-height: 1.6rem;`"
>
{{
`Entire Directory Download of "${
selectedFolder ? selectedFolder : 'root'
}"`
}}
</v-card-title>
<v-card-text class="pt-2">
<v-row v-for="(tool, index) in tools"
:key="`${tool.title}_${index}`"
no-gutters >