├── SPLab talk.pdf
├── server
├── requirments.txt
└── server.py
├── web-app
├── public
│ ├── favicon.ico
│ ├── manifest.json
│ └── index.html
├── src
│ ├── assets
│ │ ├── img
│ │ │ ├── icons
│ │ │ │ └── common
│ │ │ │ │ ├── folder_open.svg
│ │ │ │ │ ├── left-arrow.svg
│ │ │ │ │ ├── search.svg
│ │ │ │ │ ├── delete.svg
│ │ │ │ │ ├── analytics.svg
│ │ │ │ │ ├── github.svg
│ │ │ │ │ └── yml.svg
│ │ │ └── brand
│ │ │ │ ├── logos_ZHAW.svg
│ │ │ │ └── SPLab.svg
│ │ ├── components
│ │ │ ├── SimpleDialog.js
│ │ │ ├── Analyze.js
│ │ │ ├── GithubButton.js
│ │ │ ├── Resault.js
│ │ │ ├── UploadFileButton.scss
│ │ │ ├── UploadFileButton.js
│ │ │ ├── FilterButton.js
│ │ │ ├── Main.scss
│ │ │ ├── GithubButton.scss
│ │ │ ├── Resault.scss
│ │ │ ├── FileUploader.scss
│ │ │ ├── Main.js
│ │ │ ├── FileUploder.js
│ │ │ └── FilterButton.scss
│ │ └── lotties
│ │ │ ├── loader.json
│ │ │ ├── clock.json
│ │ │ └── done.json
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── App.scss
│ ├── App.js
│ └── serviceWorker.js
├── .gitignore
├── package.json
└── README.md
├── .gitignore
├── package.json
├── yamlreader.py
├── validator-cli.py
├── ThingsThatCanGoWrong
└── ThingsThatCanGoWrong.py
├── README.md
├── server.js
├── typoMistake.py
├── validator.py
└── yarn.lock
/SPLab talk.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serviceprototypinglab/dcvalidator/HEAD/SPLab talk.pdf
--------------------------------------------------------------------------------
/server/requirments.txt:
--------------------------------------------------------------------------------
1 | flask
2 | werkzeug
3 | flask_cors
4 | pyYaml
5 | ruamel.yaml
6 | python-Levenshtein
--------------------------------------------------------------------------------
/web-app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serviceprototypinglab/dcvalidator/HEAD/web-app/public/favicon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | /node_modules
3 |
4 | #Undr construction :)
5 | /_cache
6 | /node-server
7 | server.js
8 | validator-cli-test.py
9 | validatortest.py
10 | Note.txt
11 |
12 | _*
13 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/icons/common/folder_open.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web-app/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/web-app/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Validator",
3 | "name": "Validator",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/web-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/web-app/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docker-compose-validator",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "client": "cd web-app && yarn start",
6 | "server": "cd server && python server.py",
7 | "dev": "concurrently --kill-others-on-fail \"yarn server\" \"yarn client\"",
8 | "install-client": "cd web-app && yarn install",
9 | "install-server": "cd server && pip install --no-cache-dir -r requirements.txt"
10 | },
11 | "dependencies": {
12 | },
13 | "devDependencies": {
14 | "concurrently": "^4.0.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/web-app/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 | import 'typeface-roboto';
7 |
8 | ReactDOM.render(, document.getElementById('root'));
9 |
10 | // If you want your app to work offline and load faster, you can change
11 | // unregister() to register() below. Note this comes with some pitfalls.
12 | // Learn more about service workers: https://bit.ly/CRA-PWA
13 | serviceWorker.unregister();
14 |
--------------------------------------------------------------------------------
/web-app/src/assets/components/SimpleDialog.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Dialog from '@material-ui/core/Dialog';
3 |
4 |
5 | export default function SimpleDialog(props) {
6 | const {
7 | onClose,
8 | open,
9 | component,
10 | onEscapeKeyDown,
11 | } = props;
12 |
13 | function handleClose() {
14 | onClose();
15 | }
16 |
17 | return (
18 |
27 | );
28 | }
--------------------------------------------------------------------------------
/web-app/src/assets/components/Analyze.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Lottie from 'react-lottie';
3 |
4 | import animationData from '../lotties/clock.json';
5 |
6 |
7 |
8 | class Analyze extends Component {
9 | constructor(props) {
10 | super(props);
11 | this.defaultOptions = {
12 | loop: true,
13 | autoplay: true,
14 | animationData: animationData,
15 | rendererSettings: {
16 | preserveAspectRatio: 'xMidYMid slice'
17 | }
18 | };
19 | }
20 | render() {
21 | return (
22 |
23 |
28 |
29 | )
30 | }
31 | }
32 |
33 | export default Analyze;
--------------------------------------------------------------------------------
/web-app/src/assets/components/GithubButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import InputBase from '@material-ui/core/InputBase';
3 |
4 | import { ReactComponent as SearchIcon } from '../img/icons/common/analytics.svg';
5 | import { ReactComponent as GithubIcon } from '../img/icons/common/github.svg';
6 |
7 | import './GithubButton.scss';
8 |
9 | function GithubButton(props) {
10 | const { style, searchClicked, gitURL, urlInputChange, handleKeyDown, placeholder } = props
11 |
12 | return (
13 |
14 | <>
15 |
16 |
27 |
28 | >
29 |
30 |
31 | );
32 | }
33 | export default GithubButton;
--------------------------------------------------------------------------------
/web-app/src/App.scss:
--------------------------------------------------------------------------------
1 |
2 | .App-logo {
3 | font-size: calc(10px + 2vmin);
4 | height: 40vmin;
5 | pointer-events: none;
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | justify-content: center;
10 | // z-index:: 10;
11 | // overflow: hidden;
12 | }
13 |
14 | .App-footer {
15 | width: 100%;
16 | // height: calc(14px + 6.5rem);
17 | // align-self: flex-start;
18 | display: flex;
19 | flex-direction: row;
20 | align-self: flex-start;
21 | // justify-items: flex-start;
22 | // justify-self: flex-end;
23 | // background-color: aqua;
24 | margin-top: 91vh;
25 | position: absolute;
26 | // z-index:: 1;
27 | // overflow: hidden;
28 |
29 |
30 | }
31 | .logo {
32 | height: 5vmin;
33 | pointer-events: none;
34 | display: flex;
35 | margin-top: 1.95rem;
36 | margin-left: 1.95rem;
37 | // z-index:: 10;
38 | // overflow: hidden;
39 | }
40 | .container {
41 | // height: 100vh;
42 | display: flex;
43 | justify-content: center;
44 | }
--------------------------------------------------------------------------------
/web-app/src/assets/img/icons/common/left-arrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
39 |
--------------------------------------------------------------------------------
/web-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^4.3.1",
7 | "@material-ui/icons": "^4.2.1",
8 | "axios": "^0.19.0",
9 | "node-sass": "^4.12.0",
10 | "react": "^16.8.6",
11 | "react-dom": "^16.8.6",
12 | "react-dropzone": "^10.1.7",
13 | "react-lottie": "^1.2.3",
14 | "react-router-dom": "^5.0.1",
15 | "react-scripts": "3.0.1",
16 | "typeface-roboto": "0.0.75",
17 | "websocket": "^1.0.29"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": "react-app"
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | },
40 | "proxy": "http://localhost:5000/"
41 | }
42 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/icons/common/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
40 |
--------------------------------------------------------------------------------
/web-app/src/assets/components/Resault.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './Resault.scss'
3 | import { ReactComponent as LeftArrow } from '../img/icons/common/left-arrow.svg'
4 |
5 | function Resault(props) {
6 | const resault = props.resault
7 | const downloadTxtFile = () => {
8 | const element = document.createElement("a");
9 | const file = new Blob(resault.data.logs, { type: 'text/plain' });
10 | element.href = URL.createObjectURL(file);
11 | element.download = "resault.txt";
12 | document.body.appendChild(element); // Required for this to work in FireFox
13 | element.click();
14 | }
15 | return (
16 |
17 |
Resault
18 |
19 | <>
20 | {resault.data.logs.map(line => {line})}
21 | >
22 |
23 |
24 |
27 |
30 |
31 |
32 | );
33 | }
34 |
35 | export default Resault;
--------------------------------------------------------------------------------
/web-app/src/assets/components/UploadFileButton.scss:
--------------------------------------------------------------------------------
1 | .uploadFileButton {
2 | margin: 2vh;
3 | width: 50vh;
4 | height: 8vh;
5 | border-radius: 25px;
6 | display: flex;
7 | border-style: solid;
8 | border-color: rgb(13, 32, 58);
9 | background-color: transparent;
10 | border-width: medium;
11 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
12 | position: relative;
13 | align-items: center;
14 | justify-content: space-around;
15 | flex-direction: row;
16 | cursor: pointer;
17 | & .uploadFileIcon {
18 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
19 | margin: 0vh 0vh 0vh 2vh;
20 | }
21 | & .uploadFileText {
22 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
23 | margin: 0vh 5vh 0vh 0vh;
24 | font-size: 3vh;
25 | }
26 | }
27 | .uploadFileButton:hover {
28 | border-width: 0;
29 | background-color: rgba(64, 120, 192, 0.39);
30 | }
31 | .uploadFileButton:active {
32 | border-width: 0;
33 | width: 50vh;
34 | background-color: rgba(64, 120, 192, 0.39);
35 | }
36 | .uploadFileButton:focus-within {
37 | border-width: 0;
38 | width: 50vh;
39 | height: 8vh;
40 | outline: none;
41 |
42 | background-color: rgba(64, 119, 192, 0.39);
43 |
44 | .label {
45 | color: rgb(3, 41, 90);
46 | width: 4vh;
47 | margin: 0vh 0vh 0vh -14vh;
48 | font-size: 1.5vh;
49 | }
50 | }
--------------------------------------------------------------------------------
/yamlreader.py:
--------------------------------------------------------------------------------
1 | from ruamel.yaml import YAML
2 | from ruamel.yaml.constructor import SafeConstructor
3 | # def construct_yaml_map(self, node):
4 | # # test if there are duplicate node keys
5 | # data = []
6 | # yield data
7 | # for key_node, value_node in node.value:
8 | # key = self.construct_object(key_node, deep=True)
9 | # val = self.construct_object(value_node, deep=True)
10 | # data.append((key, val))
11 | def construct_yaml_map(self, node):
12 | # test if there are duplicate node keys
13 | keys = set()
14 | for key_node, value_node in node.value:
15 | key = self.construct_object(key_node, deep=True)
16 | if key in keys:
17 | break
18 | keys.add(key)
19 | else:
20 | data = {} # type: Dict[Any, Any]
21 | yield data
22 | value = self.construct_mapping(node)
23 | data.update(value)
24 | return
25 | data = []
26 | yield data
27 | for key_node, value_node in node.value:
28 | key = self.construct_object(key_node, deep=True)
29 | val = self.construct_object(value_node, deep=True)
30 | data.append((key, val))
31 |
32 | SafeConstructor.add_constructor(u'tag:yaml.org,2002:map', construct_yaml_map)
33 | yaml_overwrited = YAML(typ='safe')
34 |
35 | def reader(content):
36 | try:
37 | parsed = yaml_overwrited.load(content)
38 | print("syntax is ok")
39 | return parsed
40 | except:
41 | print("yaml syntax error")
--------------------------------------------------------------------------------
/web-app/src/assets/components/UploadFileButton.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import { ReactComponent as UploadFileIcon } from '../img/icons/common/folder_open.svg';
3 | import FileUploder from './FileUploder';
4 | import SimpleDialog from './SimpleDialog';
5 |
6 | import './UploadFileButton.scss';
7 |
8 |
9 | export default function UploadFileButton(props) {
10 | const [open, setOpen] = useState(false);
11 | const {fileSelectedHandler, draged, selectedFiles, uploading, uploadPercentage, fileUploadHandler } = props;
12 | function handleClickOpen() {
13 | setOpen(true);
14 | }
15 |
16 | const handleClose = () => {
17 | setOpen(false);
18 | };
19 |
20 | return (
21 |
22 | <>
23 |
29 |
41 | }
42 | onEscapeKeyDown={handleClose}
43 | />
44 | >
45 |
46 |
47 | );
48 | }
--------------------------------------------------------------------------------
/web-app/src/assets/components/FilterButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import FormControlLabel from '@material-ui/core/FormControlLabel';
3 | import Checkbox from '@material-ui/core/Checkbox';
4 | import './FilterButton.scss'
5 |
6 | export default function FilterButton(props) {
7 | const { expand, handlePlusClick, filterIcon, expanded, filters, filtersName, handleChangeFilters } = props
8 | // const [expanded, setExpanded] = useState(false)
9 |
10 |
11 | return (
12 |
13 |
19 | {expanded &&
20 | {filtersName.map(
21 | filter =>
22 |
23 |
32 | }
33 | label={filter}
34 | />
35 |
36 | )}
37 |
}
38 |
39 |
40 | );
41 | }
--------------------------------------------------------------------------------
/web-app/src/assets/components/Main.scss:
--------------------------------------------------------------------------------
1 | // @import url("https://cssicon.space/css/icons.css");
2 | $icon-width: 20px;
3 | $icon-height: 3px;
4 | .App {
5 | text-align: center;
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | justify-content: space-between;
10 | // overflow: hidden;
11 | // height: 100vh;
12 |
13 | }
14 |
15 | .App-logo {
16 | font-size: calc(10px + 2vmin);
17 | height: 40vmin;
18 | pointer-events: none;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | // z-index:: 10;
24 | // overflow: hidden;
25 | }
26 |
27 | .App-header {
28 | color: #282c34;
29 | min-height: 65rem;
30 | // background-color: white;
31 | // z-index:: 10;
32 | // overflow: hidden;
33 |
34 | }
35 |
36 | // .App-footer {
37 | // width: 100%;
38 | // // height: calc(14px + 6.5rem);
39 | // // align-self: flex-start;
40 | // display: flex;
41 | // flex-direction: row;
42 | // align-self: flex-start;
43 | // // justify-items: flex-start;
44 | // // justify-self: flex-end;
45 | // // background-color: aqua;
46 | // margin-top: 91vh;
47 | // position: absolute;
48 | // // z-index:: 1;
49 | // // overflow: hidden;
50 |
51 |
52 | // }
53 |
54 |
55 | // .logo {
56 | // height: 5vmin;
57 | // pointer-events: none;
58 | // display: flex;
59 | // margin-top: 1.95rem;
60 | // margin-left: 1.95rem;
61 | // // z-index:: 10;
62 | // // overflow: hidden;
63 | // }
64 |
65 | .boddy {
66 | // width: 70%;
67 | display: flex;
68 | flex-direction: column;
69 | align-items: center;
70 | justify-content: center;
71 | // z-index:: 10;
72 | // overflow: hidden;
73 | }
--------------------------------------------------------------------------------
/web-app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | Validator
23 |
24 |
25 |
26 |
27 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/icons/common/delete.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/icons/common/analytics.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
53 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/icons/common/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web-app/src/assets/components/GithubButton.scss:
--------------------------------------------------------------------------------
1 | .githubButton {
2 | margin: 2vh;
3 | width: 50vh;
4 | height: 8vh;
5 | border-radius: 25px;
6 | display: flex;
7 | border-style: solid;
8 | border-color: rgb(64, 119, 192);
9 | background-color: transparent;
10 | border-width: medium;
11 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
12 | position: relative;
13 | align-items: center;
14 | justify-content: space-between;
15 | flex-direction: row;
16 | cursor: pointer;
17 | & .textInput{
18 | transition: width 0.45s cubic-bezier(0.65, 0, 0.076, 1);
19 | width: 0vh;
20 | }
21 | & .githubIcon {
22 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
23 | margin: 0vh 3vh 0vh 2vh;
24 | }
25 |
26 | & .label {
27 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
28 | margin: 0vh 0vh 0vh -5vh;
29 | font-size: 3vh;
30 | }
31 | & .searchIcon {
32 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
33 | margin: 0vh 0vh 0vh 0vh;
34 | height: 0vh;
35 | width: 0vh;
36 | }
37 | }
38 | .githubButton:hover {
39 | border-width: 0;
40 | background-color: rgba(64, 120, 192, 0.39);
41 | }
42 | .githubButton:active {
43 | border-width: 0;
44 | width: 100vh;
45 | background-color: rgba(64, 120, 192, 0.39);
46 | }
47 | .githubButton:focus-within {
48 | border-width: 0;
49 | width: 100vh;
50 | height: 8vh;
51 | outline: none;
52 | cursor: default;
53 |
54 | background-color: rgba(64, 119, 192, 0.39);
55 | .textInput{
56 | width: 65%;
57 | display: flex;
58 | cursor: text;
59 | }
60 | .label {
61 | color: rgb(3, 41, 90);
62 | width: 4vh;
63 | margin: 0vh 0vh 0vh -14vh;
64 | font-size: 1.5vh;
65 | }
66 | & .searchIcon {
67 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
68 | margin: 0vh 2vh 0vh 0vh;
69 | height: 3vh;
70 | width: 3vh;
71 |
72 | }
73 | & .searchIcon:hover{
74 | fill: rgb(64, 119, 192);
75 | cursor: pointer;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/validator-cli.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import sys
4 | from validator import Validator
5 | if __name__ == "__main__":
6 | if len(sys.argv) == 1:
7 | print("Syntax: {} [-a /] [-f ] [-u ] [-e //]".format(sys.argv[0]), file=sys.stderr)
8 | print(" -a: autosearch; find appropriate compose files on GitHub")
9 | print(" -f: filebased; load docker-compose from a docker-compose file")
10 | print(" -fl: filebased list; load paths or URLs as lines from a text file")
11 | print(" -u: urlbased; direct URL or path specification")
12 | print(" -e: eventing; send results to Kafka endpoint with space and series selection")
13 | print(" -fi: filters; you can select filters as any as you want:\n for more info flag --fih might be helpful!")
14 | print("Example: {} -a elastest/deploy -e kafka.cloudlab.zhaw.ch/user-1-docker_label_consistency/nightly -fi 'Duplicate Keys,Top level property'".format(sys.argv[0]))
15 | sys.exit(1)
16 |
17 | autosearch = None
18 | filebasedlist = None
19 | filebased = None
20 | urlbased = None
21 | eventing = None
22 | filters = []
23 |
24 | i = 1
25 | while i < len(sys.argv):
26 | if sys.argv[i] == "-a":
27 | autosearch = sys.argv[i + 1]
28 | elif sys.argv[i] == "-fl":
29 | filebasedlist = sys.argv[i + 1]
30 | elif sys.argv[i] == "-f":
31 | filebased = sys.argv[i + 1]
32 | elif sys.argv[i] == "-u":
33 | urlbased = sys.argv[i + 1]
34 | elif sys.argv[i] == "-fi":
35 | filters = sys.argv[i + 1]
36 | filters = filters.split(',')
37 | elif sys.argv[i] == "--fih":
38 | print("Whole list of fliters is here!\n \n ====> 'Duplicate Keys','Top level property','Duplicate ports','Container name','Labels','Typing mistakes', 'DNS', 'Duplicate expose'\n \n How to use it? \n\n EZ!\n\n Something like this\n\n python validator-cli.py -a elastest/deploy -fi 'Duplicate Keys,Top level property' \n\n\t *****Warning*****\n\n Makesure that you enter this arg as a string!\n\n\t *****************")
39 | sys.exit(1)
40 | elif sys.argv[i] == "-e":
41 | eventing = sys.argv[i + 1]
42 | if not "kafka" in dir():
43 | print("warning: eventing disabled")
44 | eventing = None
45 |
46 | i += 1
47 |
48 | my_validator = Validator()
49 | my_validator.validator(autosearch, filebasedlist, urlbased, eventing, filebased, filters)
--------------------------------------------------------------------------------
/web-app/src/assets/img/icons/common/yml.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
56 |
--------------------------------------------------------------------------------
/web-app/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import axios from 'axios';
4 |
5 | import Main from './assets/components/Main';
6 | import Analyze from './assets/components/Analyze';
7 | import Resault from './assets/components/Resault';
8 |
9 | import './App.scss'
10 |
11 | class App extends Component {
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | initialFilters: {},
16 | resault: null,
17 | page: 'home',
18 | };
19 | this.data = {}
20 | axios.get('http://localhost:5000/getlabels').then(res => {
21 | var tmpinitialFilters = this.state.initialFilters
22 | res.data.labels.map(label => tmpinitialFilters[label] = true)
23 | this.setState({ initialFilters: tmpinitialFilters })
24 | })
25 |
26 | }
27 | setData = (data) => {
28 | this.data = data
29 | this.setState({ page: 'analyzing' }, () => this.apiCall())
30 | }
31 | apiCall = () => {
32 | axios.post('http://localhost:5000/analyzing', this.data, {
33 | mode: 'cors',
34 | })
35 | .then(res => {
36 | console.log(res.data)
37 | this.setState({ resault: res, page: 'resault' })
38 | // setTimeout(() => setOpen(false), 1500)
39 | });
40 | }
41 | backHome = () => {
42 | this.setState({ page: 'home' })
43 | }
44 | render() {
45 | const { initialFilters, page, resault } = this.state
46 | return (
47 |
48 | {page === 'home' &&
}
49 | {page === 'analyzing' &&
}
50 | {page === 'resault' &&
}
51 |
63 |
64 | );
65 | }
66 | }
67 |
68 | export default App;
--------------------------------------------------------------------------------
/ThingsThatCanGoWrong/ThingsThatCanGoWrong.py:
--------------------------------------------------------------------------------
1 | # This is a kind of check list to know which labes are validate and which one can go wrong and need to validate
2 | # The values of dictionary are 3 types:
3 | # CHECK: That means the script can validate it. Yeay! :)
4 | # TODO: Means the script not validate it completly or at all! :(
5 | # UNKNOWN: It can go wrong but someone who use this lable, knows what's he doing!
6 |
7 |
8 | issues = {
9 | 'Duplicate service name' : 'CHECK',
10 | 'Typing mistake': 'CHECK',
11 | # 'top-level property': 'CHECK', # version, services, networks, volumes It's not important!
12 | 'extensions starting with x-': 'TODO', #Version 3.4 or higher
13 | 'Duplicate container name' : 'CHECK',
14 | 'NETWORK NAME': 'TODO',
15 | 'Duplicate image' : 'CHECK',
16 | 'Duplicate Key in docker compose': 'CHECK',
17 | 'Duplicate port' : 'CHECK',
18 | 'expose' : 'CHECK',
19 | 'Invalid volume directory' : 'CHECK', # Only for github and CLI. mostly working
20 | 'build label and all it needs' : 'TODO', # Including: build, CONTEXT, DOCKERFILE, ARGS, CACHE_FROM, LABELS, SHM_SIZE, TARGET
21 | # Build path is only for github links and CLI
22 | 'image lable' : 'TODO',
23 | 'other versions of compose' : 'TODO', # Lastest version can cover older versions, right? :)
24 | 'local use' : 'TODO',
25 | 'good file-based reporting' : 'TODO',
26 | 'depends_on' : 'CHECK',
27 | 'credential_spec' : 'TODO',
28 | 'configs' : 'TODO',
29 | 'deploy' : 'TODO', # It's version 3 only
30 | 'ENDPOINT_MODE' : 'TODO', # Only in versio 3.3
31 | 'LABELS' : 'TODO',
32 | 'MODE' : 'TODO',
33 | 'PLACEMENT' : 'TODO',
34 | 'REPLICAS' : 'TODO',
35 | 'RESOURCES' : 'TODO',
36 | 'RESTART_POLICY' : 'TODO',
37 | 'UPDATE_CONFIG' : 'TODO',
38 | 'devices' : 'TODO',
39 | 'dns' : 'CHECK',
40 | 'dns_search' : 'TODO',
41 | 'entrypoint' : 'TODO',
42 | 'env_file' : 'TODO',
43 | 'external_links' : 'TODO',
44 | 'extra_hosts' : 'TODO',
45 | 'healthcheck' : 'TODO',
46 | 'init' : 'TODO', # This is very new! :)
47 | 'links' : 'UNKNOWN',
48 | 'isolation' : 'UNKNOWN',
49 | 'logging' : 'UNKNOWN',
50 | 'network_mode' : 'TODO',
51 | 'networks' : 'TODO',
52 | 'secrets' : 'UNKNOWN',
53 | 'security_opt' : 'TODO',
54 | 'sysctls' : 'UNKNOWN',
55 | 'tmpfs' : 'TODO',
56 | 'stop_signal' : 'UNKNOWN',
57 | 'ulimits' : 'UNKNOWN',
58 | 'Some single values' : 'TODO', # domainname, hostname, ipc, mac_address, privileged, read_only, shm_size, stdin_open, tty, user, working_dir
59 | 'ipam' : 'TODO',
60 | 'Variable substitution' : 'TODO',
61 | 'Extension fields' : 'TODO',
62 |
63 | }
--------------------------------------------------------------------------------
/web-app/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `npm run build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Docker Compose Validator
2 | ## Idea
3 | When we are using docker-compose, we may make silly mistakes. This tool helps you avoid making such mistakes!
4 |
For example:
5 | ``` yaml
6 | version: '3'
7 |
8 | services:
9 | app:
10 | build: .
11 | image: takacsmark/flask-redis:1.0
12 | environment:
13 | - FLASK_ENV=development
14 | ports:
15 | - "4999-5005:4999-5005"
16 | redis:
17 | image: redis:4.0.11-alpine
18 | ```
19 | This is a docker-compose that works correctly!
20 | But imagine that you need more services. More services means complicated our docker-compose template! And at this point, human mistakes will probably show up! Say, we add more services, and at this point, we forgot that two of them share the same name, but THEY ARE NOT THE SAME SERVICE!
21 |
Simply I'll lose one of my services!
22 |
23 | ``` yaml
24 | version: '3'
25 |
26 | services:
27 |
28 | service1:
29 | /* Some stuff */
30 |
31 | service2:
32 | /* Some other stuff */
33 |
34 |
35 | ************ OTHER SERVICES ************
36 |
37 | service-n:
38 | /* Some stuff */
39 |
40 | service2: <---------------- DUPLICATE SERVICE! ----------------
41 | /* Some stuff */
42 |
43 | ```
44 |
45 |
46 |
47 |
48 |
49 | Another example:
50 | Imagine you are new to docker-compose, and your template looks like this:
51 | ``` yaml
52 | version: '3'
53 |
54 | services:
55 |
56 | service1:
57 | /* Some stuff */
58 |
59 | service2:
60 | /* Some other stuff */
61 |
62 | volumes: <---------------- NOT IN A GOOD PLACE! ----------------
63 | db-data:
64 |
65 |
66 | networks:
67 | something:
68 | external: true
69 |
70 | ```
71 | Well this might happened to every newcomer with docker-compose!
72 |
73 | ### This tool can analyze your docker-compose file with Github link or uploading the file.
74 |
75 |
76 |
77 |
78 | **********************************************************************************************************
79 |
80 | ## How to use it
81 | Just go [HERE](http://160.85.252.231:3000/) and enjoy!
82 |
83 |
84 |
85 | ## How to make it better
86 | Make sure you have node.JS, npm, yarn, python 3, pip on your machine.
87 |
Then follow these steps:
88 |
89 |
90 | 1. Download it as a zip file or clone it!
91 | 2. Change your directory to the project directory
92 | 3. `yarn`
93 | 4. `yarn install-client`
94 | 5. `yarn install-server`
95 | 6. `yarn dev`
96 |
97 | You are ready to go!
98 |
99 | ## Can I contribute?
100 | YES!
101 | Feel free to add some features!
102 |
103 | ## Note
104 | This tool extends the [label consistency checker](https://github.com/serviceprototypinglab/label-consistency) which targets Docker Compose and Kubernetes/OpenShift YAML files.
105 | It emerged from [research on microservice quality](https://mao-mao-research.github.io/) at Service Prototyping Lab, Zurich University of Applied Sciences.
106 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const bodyParser = require('body-parser');
4 | const webSocketsServerPort = 8000;
5 | const webSocketServer = require('ws');
6 | const http = require('http');
7 | // Spinning the http server and the websocket server.
8 | const server = http.createServer(app);
9 | const port = process.env.PORT || 5000;
10 | // server.listen(webSocketsServerPort);
11 | const wsServer = new webSocketServer.Server({
12 | server
13 | });
14 | server.listen(webSocketsServerPort, () => {
15 | console.log(`Listening on port ${webSocketsServerPort}`)
16 | wsServer.on('request', request => {
17 | var userID = getUniqueID();
18 | console.log((new Date()) + ' Recieved a new connection from origin ' + request.origin + '.');
19 | // You can rewrite this part of the code to accept only the requests from allowed origin
20 | const connection = request.accept(null, request.origin);
21 | clients[userID] = connection
22 | console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(clients))
23 | });
24 | });
25 |
26 |
27 |
28 |
29 | app.use(bodyParser.json());
30 | app.use(bodyParser.urlencoded({ extended: true }));
31 |
32 | app.get('/api/hello', (req, res) => {
33 | res.send({ express: 'Hello From Express' });
34 | // wsServer.on('request', function(req) {
35 | // var userID = getUniqueID();
36 | // console.log((new Date()) + ' Recieved a new connection from origin ' + req.origin + '.');
37 | // // You can rewrite this part of the code to accept only the requests from allowed origin
38 | // const connection = req.accept(null, req.origin);
39 | // clients[userID] = connection;
40 | // console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(clients))
41 | // });
42 | });
43 |
44 | app.post('/api/world', (req, res) => {
45 | console.log(req.body);
46 | res.send(
47 | `I received your POST request. This is what you sent me: ${req.body.post}`,
48 | );
49 | });
50 | app.post('/api/wsServer', (req, res) => {
51 | // var request = req
52 | res.send(
53 | 'got ya!'
54 | );
55 | console.log(req)
56 | // wsServer.on('request', (request) => {
57 | // var userID = getUniqueID();
58 | // console.log('WASAAAAP!')
59 | // console.log((new Date()) + ' Recieved a new connection from origin ' + request.origin + '.');
60 | // // You can rewrite this part of the code to accept only the requests from allowed origin
61 | // const connection = request.accept(null, request.origin);
62 | // clients[userID] = connection;
63 | // console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(clients))
64 | // });
65 | });
66 | // I'm maintaining all active connections in this object
67 | const clients = {};
68 |
69 | // This code generates unique userid for everyuser.
70 | const getUniqueID = () => {
71 | const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
72 | return s4() + s4() + '-' + s4();
73 | };
74 |
75 | wsServer.on('request', function(request) {
76 | var userID = getUniqueID();
77 | console.log((new Date()) + ' Recieved a new connection from origin ' + request.origin + '.');
78 | // You can rewrite this part of the code to accept only the requests from allowed origin
79 | const connection = request.accept(null, request.origin);
80 | clients[userID] = connection;
81 | console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(clients))
82 | });
83 |
84 | // app.listen(port, () => console.log(`Listening on port ${port}`));
--------------------------------------------------------------------------------
/web-app/src/assets/lotties/loader.json:
--------------------------------------------------------------------------------
1 | {"v":"4.6.3","fr":25,"ip":0,"op":15,"w":200,"h":200,"nm":"Graph Load","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":6,"s":[0],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":9,"s":[100],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":12,"s":[100],"e":[0]},{"t":15}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[128,100,0]},"a":{"a":0,"k":[3.481,-1.019,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.963,14.963]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.067,0.278,0.584,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[3.481,-1.019],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":15,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":3,"s":[0],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":6,"s":[100],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":9,"s":[100],"e":[0]},{"t":12}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[99.875,100,0]},"a":{"a":0,"k":[3.481,-1.019,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.963,14.963]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.067,0.278,0.584,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[3.481,-1.019],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":15,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":3,"s":[100],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":6,"s":[100],"e":[0]},{"t":9}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[71.75,100,0]},"a":{"a":0,"k":[3.481,-1.019,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[14.963,14.963]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"fl","c":{"a":0,"k":[0.067,0.278,0.584,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[3.481,-1.019],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":15,"st":0,"bm":0,"sr":1}]}
--------------------------------------------------------------------------------
/web-app/src/assets/components/Resault.scss:
--------------------------------------------------------------------------------
1 | .resault-container {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | flex-direction: column;
6 | }
7 | .resault-header{
8 | margin: 2rem;
9 | font-size: 2rem;
10 | }
11 | .paper {
12 | padding:1rem 0rem 0rem 3rem;
13 |
14 | display: flex;
15 | justify-content: flex-start;
16 | align-items: flex-start;
17 | flex-direction: column;
18 | width: 60rem;
19 | height: 35rem;
20 | // margin-top: 5rem;
21 | overflow-y: scroll;
22 | border-radius: 10px;
23 | box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
24 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
25 | }
26 | .paper::-webkit-scrollbar-track
27 | {
28 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0);
29 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
30 | // padding-right: 2px;
31 | border-radius: 10px;
32 | // background-color: rgba(0, 0, 0, 0.055);
33 | }
34 | .paper::-webkit-scrollbar:hover {
35 | /* Select the down or left scroll button when it's being hovered by the mouse */
36 | background-color: rgba(0, 0, 0, 0.055);
37 | border-radius: 10px;
38 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
39 | }
40 | .paper::-webkit-scrollbar
41 | {
42 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
43 | width: 10px;
44 | border-radius: 10px;
45 | background-color: rgba(245, 245, 245, 0);
46 | }
47 |
48 | .paper::-webkit-scrollbar-thumb
49 | {
50 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
51 | border-radius: 10px;
52 | // -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
53 | background-color: rgba(207, 207, 207, 0.76);
54 | }
55 | .button-bar {
56 | margin: 4vh 0 3vh 0;
57 | display: flex;
58 | justify-content: center;
59 | align-items: center;
60 | }
61 |
62 | .resault-download-button {
63 | width: 20vh;
64 | height: 4vh;
65 | border-radius: 20px;
66 | display: flex;
67 | border-style: solid;
68 | border-color: rgb(13, 32, 58);
69 | background-color: transparent;
70 | border-width: 2px;
71 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
72 | position: relative;
73 | align-items: center;
74 | justify-content: center;
75 | flex-direction: row;
76 | cursor: pointer;
77 | & .text {
78 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
79 | font-size: 1.25rem;
80 | }
81 | }
82 | .resault-download-button:hover {
83 | border-width: 0;
84 | background-color: rgba(64, 120, 192, 0.39);
85 | }
86 | .resault-download-button:active {
87 | border-width: 0;
88 | background-color: rgba(64, 120, 192, 0.39);
89 | }
90 |
91 | .resault-back-button {
92 | margin: 0 1rem 0 -1rem;
93 | width: 6vh;
94 | height: 4vh;
95 | border-radius: 20px;
96 | display: flex;
97 | border-style: solid;
98 | border-color: rgb(13, 32, 58);
99 | background-color: transparent;
100 | border-width: 2px;
101 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
102 | position: relative;
103 | align-items: center;
104 | justify-content: center;
105 | flex-direction: row;
106 | cursor: pointer;
107 | & .left-arrow {
108 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
109 | height: 3vh;
110 | width: 3vh;
111 | }
112 |
113 | }
114 | .resault-back-button:hover {
115 | border-width: 0;
116 | background-color: rgba(64, 120, 192, 0.39);
117 | }
118 | .resault-back-button:active {
119 | border-width: 0;
120 | background-color: rgba(64, 120, 192, 0.39);
121 | }
--------------------------------------------------------------------------------
/server/server.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import sys
4 | from flask import Flask, flash, request, redirect, url_for, session, send_from_directory, abort
5 | # from flask_restful import Resource, Api, reqparse
6 | from werkzeug.utils import secure_filename
7 | from flask_cors import CORS, cross_origin
8 |
9 | sys.path.insert(1, '../')
10 | from validator import Validator
11 | my_validator = Validator()
12 |
13 |
14 |
15 |
16 | BASE_FOLDER = './'
17 | ALLOWED_EXTENSIONS = set(['yml', 'yaml'])
18 |
19 | app = Flask(__name__)
20 | app.config['UPLOAD_FOLDER'] = BASE_FOLDER
21 |
22 | CORS(app)
23 |
24 | @app.route("/upload", methods=['POST'])
25 | def fileUpload():
26 | target=os.path.join(BASE_FOLDER,'docker-compose-file')
27 | if not os.path.isdir(target):
28 | os.mkdir(target)
29 | file_name_list = open(os.path.join(target,"list.txt"), "w+")
30 |
31 | print("dir ", str(target), " created")
32 | try:
33 | Dfiles = request.files.getlist('file[]')
34 | file_name_list = open(os.path.join(target,"list.txt"), "w+")
35 | status = 200
36 | response="Files received!"
37 | except:
38 | status = 400
39 | response="Something wrong!"
40 | return response, status
41 | else:
42 | for f in Dfiles:
43 | filename = secure_filename(f.filename)
44 | destination="/".join([target, filename])
45 | print("for: ", f, "\n", "name: ", filename, "\n", "destination: ", destination)
46 | f.save(destination)
47 | # file_name_list.writelines(filename)
48 | print(filename, file=file_name_list)
49 | print("File Saved!")
50 | session['uploadFilePath']=destination
51 | file_name_list.close()
52 | return response, status
53 |
54 |
55 | @app.route('/getlabels', methods=['GET'])
56 | def send_labels():
57 | labels = ['Duplicate Keys', 'Duplicate ports', 'Container name', 'Labels', 'Typing mistakes', 'DNS', 'Duplicate expose'] # Not now: 'Top level property',
58 | return {'labels' : labels}
59 |
60 | @app.route('/analyzing', methods=['POST'])
61 | def analyzing():
62 | target=os.path.join(BASE_FOLDER,'docker-compose-file')
63 | if not os.path.isdir(target):
64 | os.mkdir(target)
65 | file_name_list = open(os.path.join(target,"list.txt"), "w+")
66 |
67 | print("dir ", str(target), " created")
68 | try:
69 | Dfiles = request.files.getlist('file[]')
70 | file_name_list = open(os.path.join(target,"list.txt"), "w+")
71 | status = 200
72 | response="Files received!"
73 | except:
74 | status = 400
75 | response="Something wrong!"
76 | return response, status
77 | else:
78 | for f in Dfiles:
79 | filename = secure_filename(f.filename)
80 | destination="/".join([target, filename])
81 | print("for: ", f, "\n", "name: ", filename, "\n", "destination: ", destination)
82 | f.save(destination)
83 | # file_name_list.writelines(filename)
84 | print(filename, file=file_name_list)
85 | print("File Saved!")
86 | session['uploadFilePath']=destination
87 | file_name_list.close()
88 | print('label_filter:')
89 | label_filter = request.form.get('labels[]')
90 | label_filter = label_filter.split(',')
91 | print(label_filter)
92 | print('URL:')
93 | reqURL = request.form.get('URL')
94 | print(reqURL)
95 | file_name_list = os.path.join(target, 'list.txt')
96 | with open(file_name_list) as dokcer_compose_files:
97 | dokcer_compose_list = dokcer_compose_files.read()
98 | dokcer_compose_list = dokcer_compose_list.split('\n')
99 | dokcer_compose_list = dokcer_compose_list[:-1]
100 | if len(dokcer_compose_list) >= 1:
101 | for docker_compose in dokcer_compose_list:
102 | my_validator.validator(autosearch=None, filebasedlist=None, urlbased=None, eventing=None, filebased=os.path.join(target, docker_compose), labelArray=label_filter)
103 | my_validator.validator(autosearch=None, filebasedlist=None, urlbased=reqURL, eventing=None, filebased=None, labelArray=label_filter)
104 | logs = []
105 | shutil.rmtree("./docker-compose-file")
106 | shutil.rmtree("./_cache")
107 | with open("logs.txt", "r") as logsFile:
108 | lineNumber = 0
109 | for _, line in enumerate(logsFile):
110 | lineNumber += 1
111 | logs.append([lineNumber, line])
112 | os.remove("logs.txt")
113 | status = 200
114 | return {'logs': logs}
115 |
116 |
117 | if __name__ == "__main__":
118 | app.secret_key = os.urandom(24)
119 | app.run(debug=True,host="localhost",use_reloader=False)
--------------------------------------------------------------------------------
/web-app/src/assets/components/FileUploader.scss:
--------------------------------------------------------------------------------
1 | .uploadDialog {
2 | text-align: center;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | justify-content: space-evenly;
7 | padding: 3vh;
8 | }
9 | .dropzone {
10 | margin: 2vh;
11 | }
12 | .uploadButton {
13 | margin: 4vh 0 3vh 0;
14 | width: 20vh;
15 | height: 4vh;
16 | border-radius: 20px;
17 | display: flex;
18 | border-style: solid;
19 | border-color: rgb(13, 32, 58);
20 | background-color: transparent;
21 | border-width: 2px;
22 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
23 | position: relative;
24 | align-items: center;
25 | justify-content: center;
26 | flex-direction: row;
27 | cursor: pointer;
28 | // & .uploadFileIcon {
29 | // transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
30 | // margin: 0vh 0vh 0vh 2vh;
31 | // }
32 | & .uploadText {
33 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
34 | font-size: 2vh;
35 | }
36 | }
37 | .uploadButton:hover {
38 | border-width: 0;
39 | background-color: rgba(64, 120, 192, 0.39);
40 | }
41 | .uploadButton:active {
42 | border-width: 0;
43 | background-color: rgba(64, 120, 192, 0.39);
44 | }
45 | .ymlIcon {
46 | margin: 0vh 1vh 0vh 0vh;
47 | width: 3vh;
48 | height: 3vh;
49 | }
50 | .DeleteIcon {
51 | margin: 0vh 0vh 0.75vh 1vh;
52 | width: 1.5vh;
53 | height: 1.5vh;
54 | align-self: flex-end;
55 | justify-self: center;
56 | }
57 | .fileName {
58 | margin: 0vh 1vh 0vh 0vh;
59 | }
60 |
61 | .uploadingButton {
62 | margin: 4vh 0 3vh 0;
63 | width: 20vh;
64 | height: 4vh;
65 | border-radius: 20px;
66 | display: flex;
67 | border-style: solid;
68 | border-color: rgb(13, 32, 58);
69 | border-width: 2px;
70 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
71 | position: relative;
72 | align-items: center;
73 | justify-content: center;
74 | flex-direction: row;
75 | & .uploadText {
76 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
77 | font-size: 2vh;
78 | }
79 | }
80 |
81 | .uploadedButton {
82 | margin: 4vh 0 3vh 0;
83 | width: 0vh;
84 | height: 4vh;
85 | border-radius: 20px;
86 | display: flex;
87 | border-style: solid;
88 | // border-color: rgba(0, 230, 119, 0.616);
89 | // background-color: #00e67733;
90 | // border-color: rgba(0, 230, 119, 0);
91 | // background-color: #00e67700;
92 | border-color: transparent;
93 | background-color: transparent;
94 | border-width: 2px;
95 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
96 | position: relative;
97 | align-items: center;
98 | justify-content: center;
99 | flex-direction: row;
100 | & .uploadText {
101 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
102 | font-size: 2vh;
103 | }
104 | }
105 |
106 | .analysButton {
107 | margin: 4vh 0 3vh 0;
108 | width: 20vh;
109 | height: 4vh;
110 | border-radius: 20px;
111 | display: flex;
112 | border-style: solid;
113 | border-color: rgb(13, 32, 58);
114 | background-color: transparent;
115 | border-width: 2px;
116 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
117 | position: relative;
118 | align-items: center;
119 | justify-content: center;
120 | flex-direction: row;
121 | cursor: pointer;
122 | // & .uploadFileIcon {
123 | // transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
124 | // margin: 0vh 0vh 0vh 2vh;
125 | // }
126 | & .uploadText {
127 | width: 100%;
128 | height: 100%;
129 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
130 | font-size: 2vh;
131 | }
132 | }
133 |
134 | .analysButton:hover {
135 | border-width: 0;
136 | background-color: rgba(64, 120, 192, 0.39);
137 | }
138 | .analysButton:active {
139 | border-width: 0;
140 | background-color: rgba(64, 120, 192, 0.39);
141 | }
142 |
143 |
144 | .disabledUploadButton {
145 | margin: 4vh 0 3vh 0;
146 | width: 20vh;
147 | height: 4vh;
148 | border-radius: 20px;
149 | display: flex;
150 | border-style: solid;
151 | border-color: rgb(13, 32, 58);
152 | border-width: 2px;
153 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
154 | position: relative;
155 | align-items: center;
156 | justify-content: center;
157 | flex-direction: row;
158 | & .uploadText {
159 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
160 | font-size: 2vh;
161 | }
162 | }
163 |
164 | .disabledUploadButton, .uploadedButton, .uploadingButton, .uploadButton, .uploadButton:hover, .uploadButton:active, .analysButton, .analysButton:hover, .analysButton:active {
165 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
166 | }
--------------------------------------------------------------------------------
/typoMistake.py:
--------------------------------------------------------------------------------
1 | tags = {
2 | 'general':
3 | [
4 | 'version',
5 | 'services',
6 | 'networks',
7 | 'volumes',
8 | 'configs',
9 | 'secrets',
10 | ],
11 | 'service':
12 | [
13 | 'build',
14 | 'cap_add',
15 | 'cap_drop',
16 | 'cgroup_parent',
17 | 'command',
18 | 'container_name',
19 | 'credential_spec',
20 | 'config',
21 | 'depends_on',
22 | 'deploy',
23 | 'devices',
24 | 'dns',
25 | 'dns_search',
26 | 'entrypoint',
27 | 'env_file',
28 | 'environment',
29 | 'expose',
30 | 'external_links',
31 | 'extra_hosts',
32 | 'healthcheck',
33 | 'image',
34 | 'init',
35 | 'isolation',
36 | 'labels',
37 | 'links',
38 | 'logging',
39 | 'network_mode',
40 | 'networks',
41 | 'pid',
42 | 'ports',
43 | 'restart',
44 | 'secrets',
45 | 'security_opt',
46 | 'stop_grace_period',
47 | 'stop_signal',
48 | 'sysctls',
49 | 'tmpfs',
50 | 'ulimits',
51 | 'userns_mode',
52 | 'volumes',
53 | ],
54 | 'build':
55 | [
56 | 'context',
57 | 'dockerfile',
58 | 'args',
59 | 'buildno',
60 | 'image',
61 | 'gitcommithash',
62 | 'cache_from',
63 | 'labels',
64 | 'shm_size',
65 | 'target',
66 | ],
67 | 'config':
68 | [
69 | 'source',
70 | 'target',
71 | 'uid',
72 | 'gid',
73 | 'mode',
74 | ],
75 | 'deploy':
76 | [
77 | 'mode',
78 | 'endpoint_mode',
79 | 'replicas',
80 | 'labels',
81 | 'placement',
82 | 'resources',
83 | 'restart_policy',
84 | 'update_config',
85 | 'rollback_config',
86 | ],
87 | 'network':
88 | [
89 | 'aliases',
90 | 'ipv4_address',
91 | 'ipv6_address',
92 | ],
93 |
94 | 'ports':
95 | [
96 | 'target',
97 | 'published',
98 | 'protocol',
99 | 'mode',
100 | ],
101 | 'secret':
102 | [
103 | 'source',
104 | 'target',
105 | 'uid',
106 | 'gid',
107 | 'mode',
108 | ],
109 | 'volumes':
110 | [
111 | 'type',
112 | 'source',
113 | 'target',
114 | 'read_only',
115 | 'bind',
116 | 'propagation',
117 | 'volume',
118 | 'nocopy',
119 | 'tmpfs',
120 | 'size',
121 | 'consistency',
122 | ]
123 | }
124 |
125 | '''
126 | general_property_tags = [
127 | 'version',
128 | 'services',
129 | 'networks',
130 | 'volumes',
131 | 'configs',
132 | 'secrets',
133 |
134 | ]
135 |
136 | service_property_tags = [
137 | 'build',
138 | 'cap_add',
139 | 'cap_drop',
140 | 'cgroup_parent',
141 | 'command',
142 | 'container_name',
143 | 'credential_spec',
144 | 'config',
145 | 'depends_on',
146 | 'deploy',
147 | 'devices',
148 | 'dns',
149 | 'dns_search',
150 | 'entrypoint',
151 | 'env_file',
152 | 'environment',
153 | 'expose',
154 | 'external_links',
155 | 'extra_hosts',
156 | 'healthcheck',
157 | 'image',
158 | 'init',
159 | 'isolation',
160 | 'labels',
161 | 'links',
162 | 'logging',
163 | 'network_mode',
164 | 'networks',
165 | 'pid',
166 | 'ports',
167 | 'restart',
168 | 'secrets',
169 | 'security_opt',
170 | 'stop_grace_period',
171 | 'stop_signal',
172 | 'sysctls',
173 | 'tmpfs',
174 | 'ulimits',
175 | 'userns_mode',
176 | 'volumes',
177 |
178 | ]
179 |
180 | build_property_tags = [
181 | 'context',
182 | 'dockerfile',
183 | 'args',
184 | 'buildno',
185 | 'image',
186 | 'gitcommithash',
187 | 'cache_from',
188 | 'labels',
189 | 'shm_size',
190 | 'target',
191 | ]
192 |
193 | config_property_tags = [
194 |
195 | ]
196 |
197 | deploy_property_tags = [
198 | 'mode',
199 | 'endpoint_mode',
200 | 'replicas',
201 | 'labels',
202 | 'placement',
203 | 'resources',
204 | 'restart_policy',
205 | 'update_config',
206 | 'rollback_config',
207 |
208 | ]
209 |
210 |
211 | network_property_tags = [
212 | 'aliases',
213 | 'ipv4_address',
214 | 'ipv6_address',
215 |
216 | ]
217 |
218 | ports_property_tags = [
219 | 'target',
220 | 'published',
221 | 'protocol',
222 | 'mode',
223 | ]
224 |
225 | secret_property_tags = [
226 | 'source',
227 | 'target',
228 | 'uid',
229 | 'gid',
230 | 'mode',
231 | ]
232 |
233 | volumes_property_tags = [
234 | 'type',
235 | 'source',
236 | 'target',
237 | 'read_only',
238 | 'bind',
239 | 'propagation',
240 | 'volume',
241 | 'nocopy',
242 | 'tmpfs',
243 | 'size',
244 | 'consistency',
245 | ]
246 | '''
--------------------------------------------------------------------------------
/web-app/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/web-app/src/assets/components/Main.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import axios from 'axios';
3 |
4 |
5 | // import lottie from "lottie-web";
6 |
7 | import GithubButton from './GithubButton';
8 | import UploadFileButton from './UploadFileButton';
9 | import FilterButton from './FilterButton';
10 | // import SimpleDialog from './SimpleDialog';
11 |
12 | import './Main.scss';
13 |
14 | function Main(props) {
15 | // Initials
16 | // const initialFilters = {'Duplicate service name':true, 'Duplicate container name':true, 'Duplicate image':true, 'Duplicate port':true}
17 | const { initialFilters, setData } = props
18 | const filtersName = Object.keys(initialFilters)
19 | const data = new FormData();
20 |
21 | // States
22 | const [filterIcon, SetFilterIcon] = useState('plus icon')
23 | const [expand, SetExpand] = useState(false)
24 | const [expanded, setExpanded] = useState(false)
25 | const [gitURL, SetGitURL] = useState('');
26 | const [selectedFiles, SetSelectedFiles] = useState([])
27 | const [uploadPercentage, SetUploadPercentage] = useState(0)
28 | const [uploading, SetUploading] = useState(0) // 0: No 1: uploading 2: finished
29 | const [draged, SetDraged] = useState(false)
30 | // const [open, setOpen] = useState(false);
31 | const [filters, setFilters] = useState(initialFilters);
32 | useEffect(() => {
33 | var snedfilters = []
34 | filtersName.map(filter => filters[filter] ? snedfilters.push(filter) : null)
35 | data.append('labels[]', snedfilters.toString())
36 | selectedFiles.map(file => data.append('file[]', file))
37 | // data.append('file[]', selectedFiles)
38 | data.append('URL', gitURL)
39 | })
40 |
41 | const apiCall = () => {
42 | setData(data)
43 | }
44 |
45 | const searchClicked = () => {
46 | console.log("It's working!")
47 | apiCall()
48 | }
49 |
50 | const handleKeyDown = (e) => {
51 | if (e.key === 'Enter') {
52 | searchClicked()
53 | }
54 | }
55 |
56 | const fileUploadHandler = () => {
57 | if (uploading !== 3) {
58 |
59 |
60 | SetUploading(1)
61 | axios.post('http://localhost:5000/upload', data, {
62 | mode: 'cors',
63 | onUploadProgress: ProgressEvent => {
64 | var uploadProgress = Math.round(ProgressEvent.loaded / ProgressEvent.total * 100)
65 | SetUploadPercentage(uploadProgress)
66 | }
67 | })
68 | .then(res => {
69 | SetUploading(2)
70 | console.log(res.data);
71 | setTimeout(() => SetUploading(3), 2000)
72 | });
73 | } else if(uploading === 3 || uploading === 0) {
74 | apiCall()
75 | }
76 | }
77 |
78 |
79 | const urlInputChange = (e) => {
80 | SetGitURL(e.target.value)
81 | }
82 |
83 |
84 | const fileSelectedHandler = event => {
85 | let files = selectedFiles
86 | event.map(file => {
87 | if (!(file in files)) {
88 | files.push(file)
89 | }
90 | return null
91 | })
92 |
93 | SetSelectedFiles(files)
94 | SetDraged(true)
95 | }
96 |
97 | const handlePlusClick = () => {
98 | if (filterIcon === 'plus icon') {
99 | SetFilterIcon('remove icon')
100 | SetExpand(true)
101 | setTimeout(() => setExpanded(true), 200);
102 | } else {
103 | SetFilterIcon('plus icon')
104 | SetExpand(false)
105 | setExpanded(false)
106 | }
107 | }
108 |
109 | // const handleClose = () => {
110 | // setOpen(false);
111 | // };
112 |
113 | const handleChangeFilters = name => event => {
114 | setFilters(prevState => { return { ...prevState, [name]: event.target.checked } })
115 |
116 | }
117 |
118 | return (
119 |
120 |
121 |
122 |
123 | Docker Compose Validator!
124 |
125 |
126 |
133 |
134 |
142 |
151 |
152 |
153 | {/*
*/}
165 | {/*
*/}
170 |
171 | {/* }
172 | /> */}
173 |
174 | );
175 |
176 | }
177 |
178 |
179 | export default Main;
180 |
--------------------------------------------------------------------------------
/web-app/src/assets/components/FileUploder.js:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useEffect, useState } from 'react';
2 | import { useDropzone } from 'react-dropzone';
3 | import LinearProgress from '@material-ui/core/LinearProgress';
4 | import Lottie from 'react-lottie';
5 |
6 | import { ReactComponent as YmlIcon } from '../img/icons/common/yml.svg';
7 | // import { ReactComponent as DeleteIcon } from '../img/icons/common/delete.svg';
8 | import loadingAnimationData from '../lotties/loader.json';
9 | import doneAnimationData from '../lotties/done.json';
10 |
11 |
12 |
13 | import './FileUploader.scss';
14 |
15 | const baseStyle = {
16 | // flex: 1,
17 | width: '64vh',
18 | height: '26vh',
19 | display: 'flex',
20 | flexDirection: 'column',
21 | alignItems: 'center',
22 | justifyContent: 'center',
23 | padding: '20px',
24 | borderWidth: 2,
25 | borderRadius: 5,
26 | borderColor: '#eeeeee',
27 | borderStyle: 'dashed',
28 | backgroundColor: '#fafafa',
29 | color: '#bdbdbd',
30 | outline: 'none',
31 | transition: 'border .24s ease-in-out',
32 | cursor: 'pointer',
33 | };
34 |
35 | const activeStyle = {
36 | borderColor: '#2196f3'
37 | };
38 |
39 | const acceptStyle = {
40 | borderColor: '#00e676',
41 | backgroundColor: 'rgba(64, 119, 192, 0.025)',
42 | };
43 |
44 | const rejectStyle = {
45 | borderColor: '#ff1744',
46 | backgroundColor: 'rgba(192, 64, 64, 0.068)',
47 | cursor: 'not-allowed',
48 | };
49 |
50 | function StyledDropzone(props) {
51 | const {
52 | getRootProps,
53 | getInputProps,
54 | isDragActive,
55 | isDragAccept,
56 | isDragReject,
57 |
58 | } = useDropzone({
59 | onDrop: props.onDrop,
60 | accept: 'application/x-yaml, application/x-yaml',
61 | multiple: true,
62 | });
63 |
64 | const style = useMemo(() => ({
65 | ...baseStyle,
66 | ...(isDragActive ? activeStyle : {}),
67 | ...(isDragAccept ? acceptStyle : {}),
68 | ...(isDragReject ? rejectStyle : {})
69 | }),
70 | [isDragActive, isDragReject, isDragAccept]);
71 |
72 | return (
73 |
74 |
75 |
76 |
Drag 'n' drop some files here, or click to select files
77 |
78 |
79 | );
80 | }
81 |
82 |
83 |
84 | function FileUploder(props) {
85 | const [disabled, setDisabled] = useState(false)
86 | const {
87 | fileSelectedHandler,
88 | draged,
89 | selectedFiles,
90 | uploading,
91 | uploadPercentage,
92 | fileUploadHandler,
93 | } = props;
94 | useEffect(() => {
95 | if (selectedFiles.length <= 0) {
96 | setDisabled(true)
97 | }
98 | else if (uploading !== 1 || uploading !== 0) {
99 | setDisabled(false)
100 | }
101 | return () => {
102 | if (selectedFiles) {
103 | setDisabled(true)
104 | }
105 | else if (uploading !== 1 || uploading !== 0) {
106 | setDisabled(false)
107 | }
108 | }
109 | }, [selectedFiles, uploading, props])
110 |
111 | const defaultOptions = {
112 | loop: uploading === 2 ? false : true,
113 | autoplay: true,
114 | animationData: uploading === 2 ? doneAnimationData : loadingAnimationData,
115 | rendererSettings: {
116 | preserveAspectRatio: 'xMidYMid slice'
117 | }
118 | };
119 |
120 | return (
121 |
122 |
123 |
124 |
125 | {selectedFiles &&
126 |
127 | selectedFiles.map(file =>
128 |
129 |
130 |
131 | {file.path}
132 |
133 | {/* */}
134 |
135 |
)
136 | }
137 |
138 |
139 | {uploading !== 0 && }
140 |
141 |
142 |
183 |
184 | );
185 |
186 | }
187 |
188 | export default FileUploder;
--------------------------------------------------------------------------------
/web-app/src/assets/components/FilterButton.scss:
--------------------------------------------------------------------------------
1 | .expand.filter {
2 | // width: 29rem;
3 | // height: 1.5rem;
4 | // display: flex;
5 | // background-color: aqua;
6 | // border-radius: 1rem;
7 | // z-index: 10;
8 | // padding: 1px;
9 | margin-left: 1.5rem;
10 | width: 6.5rem;
11 | height: 2rem;
12 | display: flex;
13 | // background-color: aqua;
14 | box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
15 | border-radius: 1rem;
16 | // z-index:: 10;
17 | // border-width: thin;
18 | // border-color: black;
19 | // border-style: solid;
20 | justify-content: flex-start;
21 | align-items: flex-start;
22 | align-self: flex-start;
23 |
24 | .filter-button {
25 | // align-self: flex-start;
26 | // transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
27 | font-size: 0.975rem;
28 | cursor: pointer;
29 | // z-index: 10;
30 | display: flex;
31 | margin-top: 0.25rem;
32 | margin-left: 1rem;
33 | justify-content: center;
34 | align-items: center;
35 | flex-direction: row;
36 | }
37 | &:hover {
38 | // box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
39 | background-color: rgba(64, 119, 192, 0.192);
40 | border-width: 0px;
41 | cursor: pointer;
42 | }
43 | }
44 |
45 | .label-filter-text{
46 | margin-left: 0.5rem;
47 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
48 | // margin-top: 0.2rem;
49 | cursor: pointer;
50 | // z-index: 10;
51 | }
52 |
53 |
54 | .expanded.filter {
55 | width: 29rem;
56 | height: 20rem;
57 | display: flex;
58 | background-color: rgba(64, 119, 192, 0.192);
59 | // box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
60 | // box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
61 | // border-width: thin;
62 | // border-color: black;
63 | // border-style: solid;
64 | border-radius: 1rem;
65 | margin-left: 1.5rem;
66 | align-self: flex-start;
67 | flex-direction: column;
68 | // z-index:: 10;
69 | // padding: 10px;
70 | .filter-button {
71 | align-self: flex-start;
72 | // transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
73 | font-size: 0.975rem;
74 | cursor: pointer;
75 | // z-index: 10;
76 | display: flex;
77 | margin-top: 0.25rem;
78 | margin-left: 1rem;
79 | justify-content: center;
80 | align-items: center;
81 | // flex-direction: row;
82 | }
83 | }
84 |
85 |
86 | .filter, .filter i {
87 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
88 | }
89 | .flterIcon {
90 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
91 | align-self: flex-start;
92 | margin-left: -0.25rem;
93 | display: flex;
94 | width: 1.25rem;
95 | height: 1.5rem;
96 | }
97 |
98 |
99 |
100 | .remove.icon:after {
101 | content: '';
102 | position: absolute;
103 | width: 20px;
104 | height: 3px;
105 | background-color: currentColor;
106 | border-radius: 10px;
107 | transform: rotate(-45deg);
108 | z-index: 10;
109 | }
110 |
111 | .remove.icon:before {
112 | content: '';
113 | position: absolute;
114 | width: 20px;
115 | height: 3px;
116 | background-color: currentColor;
117 | border-radius: 10px;
118 | transform: rotate(45deg);
119 | // z-index:: 10;
120 | }
121 |
122 | .remove.icon {
123 | color: rgb(51, 51, 51);
124 | position: absolute;
125 | margin-left: 3px;
126 | margin-top: 10px;
127 | // z-index:: 10;
128 | }
129 |
130 | .plus.icon:after {
131 | content: '';
132 | position: absolute;
133 | width: 20px;
134 | height: 3px;
135 | background-color: currentColor;
136 | border-radius: 10px;
137 | transform: rotate(90deg);
138 | // z-index:: 10;
139 | }
140 |
141 | .plus.icon:before {
142 | content: '';
143 | position: absolute;
144 | width: 20px;
145 | height: 3px;
146 | border-radius: 10px;
147 | background-color: currentColor;
148 | // z-index:: 10;
149 | }
150 |
151 | .plus.icon {
152 | color: rgb(51, 51, 51);
153 | position: absolute;
154 | margin-left: 3px;
155 | margin-top: 10px;
156 | // z-index:: 10;
157 | }
158 |
159 | .icon, .icon:before, .icon:after, .icon i, .icon i:before, .icon i:after {
160 | transition: all 0.4s ease;
161 | }
162 |
163 | .checkboxes {
164 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
165 | display: flex;
166 | flex-direction: row;
167 | flex-wrap: wrap;
168 | // padding: 10px;
169 | padding: 10px;
170 | justify-content: flex-start;
171 | align-items: flex-start;
172 | flex: flex-grow;
173 | overflow: auto;
174 | overflow-x: hidden;
175 | }
176 |
177 | .checkboxes::-webkit-scrollbar-track
178 | {
179 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0);
180 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
181 | // padding-right: 2px;
182 | border-radius: 10px;
183 | // background-color: rgba(0, 0, 0, 0.055);
184 | }
185 | .checkboxes::-webkit-scrollbar:hover {
186 | /* Select the down or left scroll button when it's being hovered by the mouse */
187 | background-color: rgba(0, 0, 0, 0.055);
188 | border-radius: 10px;
189 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
190 | }
191 | .checkboxes::-webkit-scrollbar
192 | {
193 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
194 | width: 10px;
195 | border-radius: 10px;
196 | background-color: rgba(245, 245, 245, 0);
197 | }
198 |
199 | .checkboxes::-webkit-scrollbar-thumb
200 | {
201 | transition: all 0.45s cubic-bezier(0.65, 0, 0.076, 1);
202 | border-radius: 10px;
203 | // -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
204 | background-color: rgba(207, 207, 207, 0.76);
205 | }
--------------------------------------------------------------------------------
/web-app/src/assets/lotties/clock.json:
--------------------------------------------------------------------------------
1 | {"v":"5.4.2","fr":48,"ip":0,"op":192,"w":500,"h":500,"nm":"12 - Clock","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Longhand_warp Outlines","sr":1,"ks":{"o":{"a":0,"k":35,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.35],"y":[1]},"o":{"x":[0.65],"y":[0]},"n":["0p35_1_0p65_0"],"t":0,"s":[0],"e":[720]},{"t":144}],"ix":10},"p":{"a":0,"k":[250,249.958,0],"ix":2},"a":{"a":0,"k":[6.5,95.417,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Warp Time","np":7,"mn":"ADBE Echo","ix":1,"en":1,"ef":[{"ty":0,"nm":"Echo Time (seconds)","mn":"ADBE Echo-0001","ix":1,"v":{"a":0,"k":-0.006,"ix":1}},{"ty":0,"nm":"Number Of Echoes","mn":"ADBE Echo-0002","ix":2,"v":{"a":0,"k":16,"ix":2}},{"ty":0,"nm":"Starting Intensity","mn":"ADBE Echo-0003","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":0,"nm":"Decay","mn":"ADBE Echo-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":7,"nm":"Echo Operator","mn":"ADBE Echo-0005","ix":5,"v":{"a":0,"k":2,"ix":5}}]},{"ty":5,"nm":"Edge Smooth","np":9,"mn":"ADBE Matte Choker","ix":2,"en":1,"ef":[{"ty":0,"nm":"Geometric Softness 1","mn":"ADBE Matte Choker-0001","ix":1,"v":{"a":0,"k":0,"ix":1}},{"ty":0,"nm":"Choke 1","mn":"ADBE Matte Choker-0002","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":0,"nm":"Gray Level Softness 1","mn":"ADBE Matte Choker-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Geometric Softness 2","mn":"ADBE Matte Choker-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Choke 2","mn":"ADBE Matte Choker-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":0,"nm":"Gray Level Softness 2","mn":"ADBE Matte Choker-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":0,"nm":"Iterations","mn":"ADBE Matte Choker-0007","ix":7,"v":{"a":0,"k":1,"ix":7}}]},{"ty":5,"nm":"Object Crush","np":4,"mn":"ADBE Simple Choker","ix":3,"en":1,"ef":[{"ty":7,"nm":"View","mn":"ADBE Simple Choker-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":0,"nm":"Choke Matte","mn":"ADBE Simple Choker-0002","ix":2,"v":{"a":0,"k":12,"ix":2}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[6.5,95.333],[6.5,6.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13,"ix":5},"lc":2,"lj":1,"ml":10,"ml2":{"a":0,"k":10,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":192,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Longhand Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.35],"y":[1]},"o":{"x":[0.65],"y":[0]},"n":["0p35_1_0p65_0"],"t":0,"s":[0],"e":[720]},{"t":144}],"ix":10},"p":{"a":0,"k":[250,249.958,0],"ix":2},"a":{"a":0,"k":[6.5,95.417,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[6.5,95.333],[6.5,6.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13,"ix":5},"lc":2,"lj":1,"ml":10,"ml2":{"a":0,"k":10,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":192,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shorthand_warp Outlines","sr":1,"ks":{"o":{"a":0,"k":35,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.35],"y":[1]},"o":{"x":[0.65],"y":[0]},"n":["0p35_1_0p65_0"],"t":0,"s":[0],"e":[360]},{"t":144}],"ix":10},"p":{"a":0,"k":[250,249.875,0],"ix":2},"a":{"a":0,"k":[6.5,6.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Warp Time","np":7,"mn":"ADBE Echo","ix":1,"en":1,"ef":[{"ty":0,"nm":"Echo Time (seconds)","mn":"ADBE Echo-0001","ix":1,"v":{"a":0,"k":-0.006,"ix":1}},{"ty":0,"nm":"Number Of Echoes","mn":"ADBE Echo-0002","ix":2,"v":{"a":0,"k":16,"ix":2}},{"ty":0,"nm":"Starting Intensity","mn":"ADBE Echo-0003","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":0,"nm":"Decay","mn":"ADBE Echo-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":7,"nm":"Echo Operator","mn":"ADBE Echo-0005","ix":5,"v":{"a":0,"k":2,"ix":5}}]},{"ty":5,"nm":"Edge Smooth","np":9,"mn":"ADBE Matte Choker","ix":2,"en":1,"ef":[{"ty":0,"nm":"Geometric Softness 1","mn":"ADBE Matte Choker-0001","ix":1,"v":{"a":0,"k":0,"ix":1}},{"ty":0,"nm":"Choke 1","mn":"ADBE Matte Choker-0002","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":0,"nm":"Gray Level Softness 1","mn":"ADBE Matte Choker-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Geometric Softness 2","mn":"ADBE Matte Choker-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Choke 2","mn":"ADBE Matte Choker-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":0,"nm":"Gray Level Softness 2","mn":"ADBE Matte Choker-0006","ix":6,"v":{"a":0,"k":1,"ix":6}},{"ty":0,"nm":"Iterations","mn":"ADBE Matte Choker-0007","ix":7,"v":{"a":0,"k":1,"ix":7}}]},{"ty":5,"nm":"Object Crush","np":4,"mn":"ADBE Simple Choker","ix":3,"en":1,"ef":[{"ty":7,"nm":"View","mn":"ADBE Simple Choker-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":0,"nm":"Choke Matte","mn":"ADBE Simple Choker-0002","ix":2,"v":{"a":0,"k":12,"ix":2}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[6.5,6.5],[64.833,6.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13,"ix":5},"lc":2,"lj":1,"ml":10,"ml2":{"a":0,"k":10,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":192,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shorthand Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.35],"y":[1]},"o":{"x":[0.65],"y":[0]},"n":["0p35_1_0p65_0"],"t":0,"s":[0],"e":[360]},{"t":144}],"ix":10},"p":{"a":0,"k":[250,249.875,0],"ix":2},"a":{"a":0,"k":[6.5,6.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[6.5,6.5],[64.833,6.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13,"ix":5},"lc":2,"lj":1,"ml":10,"ml2":{"a":0,"k":10,"ix":8},"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":192,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Midclock Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,249.875,0],"ix":2},"a":{"a":0,"k":[13.75,13.75,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-7.456],[7.456,0],[0,7.456],[-7.456,0]],"o":[[0,7.456],[-7.456,0],[0,-7.456],[7.456,0]],"v":[[13.5,0],[0,13.5],[-13.5,0],[0,-13.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[13.75,13.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":192,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Clock Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,249.875,0],"ix":2},"a":{"a":0,"k":[126.25,126.25,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-69.588],[69.588,0],[0,69.588],[-69.588,0]],"o":[[0,69.588],[-69.588,0],[0,-69.588],[69.588,0]],"v":[[126,0],[0,126],[-126,0],[0,-126]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[126.25,126.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":192,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":1,"nm":"plate_white","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":500,"sh":500,"sc":"#ffffff","ip":0,"op":192,"st":0,"bm":0}],"markers":[]}
--------------------------------------------------------------------------------
/web-app/src/assets/lotties/done.json:
--------------------------------------------------------------------------------
1 | {"v":"5.5.7","fr":29.9700012207031,"ip":0,"op":91.000003706506,"w":250,"h":250,"nm":"01 Main","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[74,58,0],"ix":2},"a":{"a":0,"k":[49.5,67,0],"ix":1},"s":{"a":0,"k":[72.368,72.368,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[11.125,11.125],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.756862804936,0.027450982262,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[49.188,66.188],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[129.5,129.563,0],"ix":2},"a":{"a":0,"k":[-110.5,93,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[6,6],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.756862804936,0.027450982262,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-32.875,80.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[125,125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[6,6],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.756862804936,0.027450982262,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-32.875,80.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[125,125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[11.125,11.125],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.403921598547,0.968627510819,0.533333333333,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[49.188,66.188],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[119.063,131.875,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[13.688,13.688],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.263,0.941,0.647,0.5,0.329,0.953,0.592,1,0.396,0.965,0.537],"ix":9}},"s":{"a":0,"k":[-6.5,0.75],"ix":5},"e":{"a":0,"k":[5.75,-0.25],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-64.969,33.344],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":1,"ix":10},"p":{"a":0,"k":[125,125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[10.109,10.109],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.263,0.941,0.647,0.5,0.329,0.953,0.592,1,0.396,0.965,0.537],"ix":9}},"s":{"a":0,"k":[-5.875,0.04],"ix":5},"e":{"a":0,"k":[4.986,0.033],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[47.371,-70.258],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Circles","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[1],"y":[0]},"t":2,"s":[-50]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[19]},{"t":80.0000032584668,"s":[95]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.58],"y":[1]},"o":{"x":[0.168],"y":[0]},"t":16,"s":[124]},{"i":{"x":[0.835],"y":[1]},"o":{"x":[0.413],"y":[0]},"t":45,"s":[122]},{"t":80.0000032584668,"s":[124]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.58],"y":[2.375]},"o":{"x":[0.168],"y":[0]},"t":16,"s":[128]},{"i":{"x":[0.835],"y":[1]},"o":{"x":[0.413],"y":[-0.64]},"t":45,"s":[126.309]},{"t":80.0000032584668,"s":[122]}],"ix":4}},"a":{"a":0,"k":[125,125,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":2,"s":[0,0,100]},{"t":16.0000006516934,"s":[100,100,100]}],"ix":6}},"ao":0,"w":250,"h":250,"ip":0,"op":91.000003706506,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Done","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[125,125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":3,"s":[0,0,100]},{"t":17.0000006924242,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-25.75,3],[-9.25,19.5],[27.5,-17.25]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.149019607843,0.125490196078,0.125490196078,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-1.25,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Main Circle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[125,125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":2,"s":[0,0,100]},{"t":16.0000006516934,"s":[100,100,100]}],"ix":6}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0.074509806931,0.063114196062,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":127.5,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":10,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":50,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[95,95],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.227,0.933,0.675,0.5,0.339,0.953,0.582,1,0.451,0.973,0.49],"ix":9}},"s":{"a":0,"k":[26.058,40.948],"ix":5},"e":{"a":0,"k":[-20.053,-40.948],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[107.453,107.453],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Circle 2","sr":1,"ks":{"o":{"a":0,"k":20,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[125,125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":1,"s":[0,0,100]},{"t":15.0000006109625,"s":[118,118,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[95,95],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.227,0.933,0.675,0.5,0.339,0.953,0.582,1,0.451,0.973,0.49],"ix":9}},"s":{"a":0,"k":[26.058,40.948],"ix":5},"e":{"a":0,"k":[-20.053,-40.948],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[107.453,107.453],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Circle 3","sr":1,"ks":{"o":{"a":0,"k":20,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[125,125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"t":14.0000005702317,"s":[136,136,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[95,95],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.227,0.933,0.675,0.5,0.339,0.953,0.582,1,0.451,0.973,0.49],"ix":9}},"s":{"a":0,"k":[26.058,40.948],"ix":5},"e":{"a":0,"k":[-20.053,-40.948],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[107.453,107.453],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":91.000003706506,"st":0,"bm":0}],"markers":[]}
--------------------------------------------------------------------------------
/web-app/src/assets/img/brand/logos_ZHAW.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
23 |
24 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/brand/SPLab.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
494 |
--------------------------------------------------------------------------------
/validator.py:
--------------------------------------------------------------------------------
1 | # Checks for inconsistencies in docker-compose labels
2 | # Dependencies: sudo apt-get install python3-kafka
3 |
4 | import urllib.request
5 | import yaml
6 | import time
7 | import re
8 | import os
9 | import os.path
10 | import hashlib
11 | import json
12 | import sys
13 | import yamlreader
14 | import Levenshtein
15 | import typoMistake
16 | try:
17 | import kafka
18 | except:
19 | print("Warning: no kafka support")
20 |
21 | RATIO = 0.6
22 |
23 | class Validator:
24 |
25 | def __autosearch_github__(self, project, path):
26 | print("https://api.github.com/search/code?q={}+in:path+org:{}&type=Code".format(path, project))
27 | f = urllib.request.urlopen("https://api.github.com/search/code?q={}+in:path+org:{}&type=Code".format(path, project))
28 | s = f.read().decode("utf-8")
29 | f.close()
30 |
31 | composefiles = []
32 | allresults = json.loads(s)
33 | results = allresults["items"]
34 | for result in results:
35 | if result["name"].endswith("docker-compose.yml") or (result["name"].endswith(".yml") and "docker-compose" in result["name"]):
36 | path = result["repository"]["html_url"] + "/blob/master/" + result["path"]
37 | composefiles.append(path)
38 | return composefiles
39 |
40 | def __normalise_githuburl__(self, url):
41 | m = re.match(r"^https://github.com/(.+)/blob/(.+)", url)
42 | if m:
43 | if len(m.groups()) == 2:
44 | url = "https://raw.githubusercontent.com/{}/{}".format(m.groups()[0], m.groups()[1])
45 | return url
46 |
47 | def __loadcache__(self):
48 | cachefiles = {}
49 | if os.path.isdir("_cache"):
50 | st = os.stat("_cache/entries")
51 | cacheage = time.time() - st.st_mtime
52 | if cacheage < 0 or cacheage > 3600:
53 | print("discard cache...")
54 | return cachefiles
55 | print("loading cache...")
56 | f = open("_cache/entries")
57 | cachedfiles = f.readlines()
58 | f.close()
59 | for cachedfile in cachedfiles:
60 | cachedfile = cachedfile[:-1]
61 | unique = hashlib.md5(cachedfile.encode("utf-8")).hexdigest()
62 | cachefiles[cachedfile] = "_cache/{}.cache".format(unique)
63 | return cachefiles
64 |
65 | def __loading__(self, cachefiles, composefiles, localSearch=False):
66 | print("loading compose files...", end="", flush=True)
67 | contents = {}
68 | for composefile in composefiles:
69 |
70 | composefile_load = self.__normalise_githuburl__(composefile)
71 | if composefile in cachefiles:
72 | composefile_load = cachefiles[composefile]
73 | if composefile_load in cachefiles:
74 | composefile_load = cachefiles[composefile_load]
75 | if "http" in composefile_load:
76 | f = urllib.request.urlopen(composefile_load)
77 | s = f.read().decode("utf-8")
78 | else:
79 | f = open(composefile_load)
80 | s = f.read()
81 | contents[composefile] = s
82 | if not composefile in cachefiles:
83 | os.makedirs("_cache", exist_ok=True)
84 | f = open("_cache/entries", "a")
85 | print(composefile, file=f)
86 | f.close()
87 | unique = hashlib.md5(composefile.encode("utf-8")).hexdigest()
88 | f = open("_cache/{}.cache".format(unique), "w")
89 | f.write(s)
90 | f.close()
91 | print(".", end="", flush=True)
92 | print()
93 | return contents
94 |
95 | def __duplicate_tag_founder__(self, itterable, counter):
96 | # tag_set = set()
97 | # for i in itterable:
98 | # if type(i) == type(tuple()):
99 | # if i[0] in tag_set:
100 | # err_message = '* Duplicate label {} in Docker-compose file.'.format(i[0])
101 | # print("="*(len(err_message)//2 - 3) + " ERROR " + "="*(len(err_message)//2 - 3))
102 | # print(err_message)
103 | # print("="*len(err_message))
104 | # else:
105 | # tag_set.add(i[0])
106 | # if type(i[1]) == type(list()):
107 | # self.__duplicate_tag_founder__(i[1])
108 | visited = []
109 | for key in itterable:
110 | counter += 1
111 | if type(key)==type(tuple()) or type(key)==type(list()):
112 | if key[0] in visited:
113 | self.__log_writer__("="*(27) + " ERROR " + "="*(27))
114 | self.__log_writer__('Line {}, this {} key is duplicate'.format(counter, key[0]))
115 | self.__log_writer__("="*(61) + '\n')
116 | break
117 | else:
118 | visited.append(key[0])
119 | return counter
120 |
121 | def __itterator__(self, itterable, counter=0):
122 | if type(itterable) == type(dict()):
123 | for key in itterable:
124 | counter += 1
125 | if type(itterable[key])==type(list()):
126 | # __duplicate_tag_founder__(itterable[key])
127 | counter = self.__duplicate_tag_founder__(itterable[key], counter)
128 | # itterator(itterable[key])
129 | else:
130 | self.__itterator__(itterable[key], counter=counter)
131 | elif (type(itterable)==type(tuple()) or type(itterable)==type(list())):
132 | # __duplicate_tag_founder__(itterable)
133 | counter = self.__duplicate_tag_founder__(itterable, counter)
134 | for item in itterable:
135 | counter += 1
136 | self.__itterator__(item, counter=counter)
137 |
138 | def __top_level_property_checker__(self, itterable):
139 | priority = {
140 | 'version': 4,
141 | 'services': 3,
142 | 'networks': 2,
143 | 'volumes': 2
144 | }
145 |
146 | if type(itterable) == type(dict()):
147 | temp_itterable = list(itterable)
148 | for index in range(1,len(temp_itterable)-1):
149 | if priority[temp_itterable[index]] < priority[temp_itterable[index + 1]]:
150 | self.__log_writer__('Top level property warning:'+ str(temp_itterable[index])+ 'better not be before'+ str(temp_itterable[index + 1]))
151 | if priority[temp_itterable[index - 1]] < priority[temp_itterable[index]]:
152 | self.__log_writer__('Top level property warning:'+ str(temp_itterable[index])+ 'better not be after'+ str(temp_itterable[index - 1]))
153 | if type(itterable) == type(list()):
154 | for index in range(1,len(itterable)-1):
155 | if priority[itterable[index][0]] < priority[itterable[index + 1][0]]:
156 | self.__log_writer__('Top level property warning:'+ str(itterable[index][0])+ 'better not be before'+ str(itterable[index + 1][0]))
157 | if priority[itterable[index - 1][0]] < priority[itterable[index][0]]:
158 | self.__log_writer__('Top level property warning:'+ str(itterable[index][0])+ 'better not be after'+ str(itterable[index - 1][0]))
159 |
160 | def __log_writer__(self, text):
161 | print(text)
162 | f = open("logs.txt", "a+")
163 | f.writelines(text + '\n')
164 | f.close()
165 |
166 | def __typomistake__(self, parsed, targetTag):
167 | tag_list_similarity = {}
168 | # err_message = ""
169 | for generalTag in parsed:
170 | tmp = []
171 | for tag in typoMistake.tags[targetTag]:
172 | ratio = Levenshtein.ratio(generalTag, tag)
173 | if ratio == 1:
174 | break
175 | elif 1 > ratio >= RATIO:
176 | if [ratio,tag] not in tmp:
177 | tmp.append([ratio,tag])
178 | if len(tmp) > 0:
179 | tmp.sort(key=lambda tmp: tmp[0], reverse=True)
180 | tag_list_similarity[generalTag] = tmp
181 | for eachTag in tag_list_similarity:
182 | pair = 0
183 | while pair < len(tag_list_similarity[eachTag]):
184 | if tag_list_similarity[eachTag][pair][0] < 0.8:
185 | tag_list_similarity[eachTag].remove(tag_list_similarity[eachTag][pair])
186 | pair += 1
187 | return tag_list_similarity
188 | # if len(tag_list_similarity) > 0:
189 | # for tag in tag_list_similarity:
190 | # err_message += "I can not find "+str(tag)+", but there is my suggestion: \n"
191 | # for item in tag_list_similarity[tag]:
192 | # for match in item:
193 | # err_message += str(match[1])
194 | # if len(err_message) > 0:
195 | # self.__log_writer__(err_message)
196 |
197 | def __consistencycheck__(self, contents, labelArray):
198 | print("checking consistency...")
199 |
200 | numservices = 0
201 | alltags = {}
202 | faulty = {}
203 |
204 | for content in contents:
205 | parsed = yamlreader.reader(contents[content])
206 | # if 'Top level property' in labelArray:
207 | # self.__top_level_property_checker__(parsed)
208 | self.__itterator__(parsed)
209 |
210 |
211 | if "https://github.com/" in content:
212 | contentname = "faulty:" + content.split("/")[4]
213 | else:
214 | contentname = "faulty:None"
215 | faulty[contentname] = 0.0
216 |
217 |
218 | c = yaml.load(contents[content])
219 |
220 | if 'Typing mistakes' in labelArray:
221 | err_message = ""
222 | tag_list_similarity = self.__typomistake__(c, 'general')
223 | if len(tag_list_similarity) > 0:
224 | for tag in tag_list_similarity:
225 | if len(tag_list_similarity[tag]) > 0:
226 | err_message += "I can not find '"+str(tag)+"' tag. Maybe you can use: \n"
227 | for item in tag_list_similarity[tag]:
228 | err_message += str(item[1]) + '\n'
229 | if len(err_message) > 0:
230 | self.__log_writer__("=================== ERROR ===================")
231 | self.__log_writer__(err_message)
232 | self.__log_writer__("=============================================")
233 |
234 |
235 | if "volumes" in c:
236 | cachevolumes = []
237 | for volume in c["volumes"]:
238 | cachevolumes.append(volume)
239 | if 'configs' in c:
240 | cacheconfigs = []
241 | for configname in c['configs']:
242 | cacheconfigs.append(configname)
243 | if 'file' in c['configs'][configname]:
244 | if not os.path.exists(c['configs'][configname]['file']):
245 | self.__log_writer__("=================== ERROR ===================")
246 | self.__log_writer__("Under config {}".format(c['configs'][configname]))
247 | self.__log_writer__("Check the file {}".format(c['configs'][configname]['file']))
248 | self.__log_writer__("=============================================")
249 |
250 | if "services" in c:
251 | cacheService = []
252 | cacheports = []
253 | cachecontainername = []
254 | self.__log_writer__("= type: docker-compose")
255 | for service in c["services"]:
256 | cacheService.append(service)
257 |
258 | for service in c["services"]:
259 | cachedns = []
260 | cacheexpose = []
261 | self.__log_writer__("- service:"+ service)
262 | numservices += 1
263 | if 'Container name' in labelArray:
264 | if not "container_name" in c["services"][service]:
265 | self.__log_writer__("**Warning** no container name found")
266 | elif c["services"][service]["container_name"] in cachecontainername:
267 | self.__log_writer__("Duplicate container name: "+ c["services"][service]["container_name"])
268 | # raise Exception ('Duplicate container name')
269 | else:
270 | cachecontainername.append(c["services"][service]["container_name"])
271 | if "volumes" in c["services"][service]:
272 | if type(c["services"][service]["volumes"]) == type(list()):
273 | for volume in c["services"][service]["volumes"]:
274 | if type(volume) == type(""):
275 | if ':' in volume:
276 | temp_dir = volume.split(':')
277 | onhostvolume = temp_dir[0]
278 | if not os.path.exists(onhostvolume):
279 | if onhostvolume not in cachevolumes:
280 | self.__log_writer__("=================== ERROR ===================")
281 | self.__log_writer__("Under service: {}".format(service))
282 | self.__log_writer__("Check volume "+ str(volume))
283 | self.__log_writer__("=============================================")
284 | elif type(volume) == type(dict()):
285 | if 'Typing mistakes' in labelArray:
286 | err_message = ""
287 | tag_list_similarity = self.__typomistake__(volume, 'volumes')
288 | if len(tag_list_similarity) > 0:
289 | for tag in tag_list_similarity:
290 | if len(tag_list_similarity[tag]) > 0:
291 | err_message += "I can not find '"+str(tag)+"' tag under '"+service+"' service. Maybe you can use: \n"
292 | for item in tag_list_similarity[tag]:
293 | err_message += str(item[1]) + '\n'
294 | if len(err_message) > 0:
295 | self.__log_writer__("=================== ERROR ===================")
296 | self.__log_writer__(err_message)
297 | self.__log_writer__("=============================================")
298 | if 'type' in volume:
299 | if volume['type'] not in 'volume bind tmpfs npipe':
300 | self.__log_writer__("=================== ERROR ===================")
301 | self.__log_writer__("Under service: {}".format(service))
302 | self.__log_writer__("Wrong type {} for volume \nVolume types are: volume, bind, tmpfs, npipe".format(volume['type']))
303 | self.__log_writer__("=============================================")
304 | if 'source' in volume:
305 | if not os.path.exists(volume['source']):
306 | if volume['source'] not in cachevolumes:
307 | self.__log_writer__("=================== ERROR ===================")
308 | self.__log_writer__("Under service: {}".format(service))
309 | self.__log_writer__("Wrong type {} for volume \nVolume types are: volume, bind, tmpfs, npipe".format(volume['type']))
310 | self.__log_writer__("=============================================")
311 |
312 |
313 | if "ports" in c["services"][service] and 'Duplicate ports' in labelArray:
314 | for port in c["services"][service]["ports"]:
315 | if type(port) == type(""):
316 | try:
317 | port_temp = port.split(':')
318 | except:
319 | self.__log_writer__("Tip: It's better to use the HOST:CONTAINER structure")
320 | else:
321 | port_host = port_temp[0]
322 | if port_host in cacheports:
323 | self.__log_writer__("Duplicate ports in service "+service+ " port "+ str(port_host))
324 | # raise Exception ('Duplicate ports')
325 | else:
326 | cacheports.append(port_host)
327 | if type(port) == type(int()):
328 | cacheports.append(port)
329 | if 'Labels' in labelArray:
330 | if not "labels" in c["services"][service]:
331 | self.__log_writer__(" ! no labels found")
332 | faulty[contentname] = faulty.get(contentname, 0) + 1
333 | continue
334 | else:
335 | for labelpair in c["services"][service]["labels"]:
336 | self.__log_writer__(" - label:"+ str(labelpair))
337 | label, value = labelpair.split("=")
338 | alltags[label] = alltags.get(label, 0) + 1
339 |
340 | if 'DNS' in labelArray:
341 | if "dns" in c["services"][service]:
342 | dns = c["services"][service]["dns"]
343 | if type(dns) == type(list()):
344 | for ip in dns:
345 | try:
346 | splitedIp = ip.split('.')
347 | for section in splitedIp:
348 | if len(section) > 3 or len(section) < 1:
349 | self.__log_writer__("=================== ERROR ===================")
350 | self.__log_writer__("Under service: {}".format(service))
351 | self.__log_writer__("The DNS is not appropriate!")
352 | self.__log_writer__("=============================================")
353 | else:
354 | if ip not in cachedns:
355 | cachedns.append(ip)
356 | else:
357 | self.__log_writer__("=================== ERROR ===================")
358 | self.__log_writer__("Under service: {}".format(service))
359 | self.__log_writer__("Duplicate DNS!")
360 | self.__log_writer__("=============================================")
361 | except:
362 | self.__log_writer__("=================== ERROR ===================")
363 | self.__log_writer__("Under service: {}".format(service))
364 | self.__log_writer__("The DNS is not appropriate!")
365 | self.__log_writer__("=============================================")
366 | continue
367 |
368 | if type(dns) == type(str()):
369 | try:
370 | splitedIp = ip.split('.')
371 | for section in splitedIp:
372 | if len(section) > 3 or len(section) < 1:
373 | self.__log_writer__("=================== ERROR ===================")
374 | self.__log_writer__("Under service: {}".format(service))
375 | self.__log_writer__("The DNS is not appropriate!")
376 | self.__log_writer__("=============================================")
377 | except:
378 | self.__log_writer__("=================== ERROR ===================")
379 | self.__log_writer__("Under service: {}".format(service))
380 | self.__log_writer__("The DNS is not appropriate!")
381 | self.__log_writer__("=============================================")
382 |
383 | else:
384 | self.__log_writer__("=================== ERROR ===================")
385 | self.__log_writer__("Under service: {}".format(service))
386 | self.__log_writer__("The DNS can be a single value or a list!")
387 | self.__log_writer__("=============================================")
388 |
389 | if 'Typing mistakes' in labelArray:
390 | err_message = ""
391 | tag_list_similarity = self.__typomistake__(c["services"][service], 'service')
392 | if len(tag_list_similarity) > 0:
393 | for tag in tag_list_similarity:
394 | if len(tag_list_similarity[tag]) > 0:
395 | err_message += "I can not find '"+str(tag)+"' tag under '"+service+"' service. Maybe you can use: \n"
396 | for item in tag_list_similarity[tag]:
397 | err_message += str(item[1]) + '\n'
398 | if len(err_message) > 0:
399 | self.__log_writer__("=================== ERROR ===================")
400 | self.__log_writer__(err_message)
401 | self.__log_writer__("=============================================")
402 |
403 | if 'Duplicate expose' in labelArray:
404 | if 'expose' in c["services"][service]:
405 | expose = c["services"][service]['expose']
406 | if type(expose) == type(list()):
407 | for port in expose:
408 | if 1 < port < 65536:
409 | if port not in cacheexpose:
410 | cacheexpose.append(port)
411 | else:
412 | self.__log_writer__("=================== ERROR ===================")
413 | self.__log_writer__("Under service: {}".format(service))
414 | self.__log_writer__("Duplicate port {} exposed!".format(port))
415 | self.__log_writer__("=============================================")
416 | else:
417 | self.__log_writer__("=================== ERROR ===================")
418 | self.__log_writer__("Under service: {}".format(service))
419 | self.__log_writer__("The port {} that exposed is not appropriate!".format(port))
420 | self.__log_writer__("=============================================")
421 | else:
422 | self.__log_writer__("=================== ERROR ===================")
423 | self.__log_writer__("Under service: {}".format(service))
424 | self.__log_writer__("Value of expose can be a list!")
425 | self.__log_writer__("=============================================")
426 | if 'depends_on' in c["services"][service]:
427 | for denpendecy in c["services"][service]['depends_on']:
428 | if denpendecy not in cacheService:
429 | self.__log_writer__("=================== ERROR ===================")
430 | self.__log_writer__("Under service: {}".format(service))
431 | self.__log_writer__("Wrong dependency! There is no such service with name of {}".format(denpendecy))
432 | self.__log_writer__("=============================================")
433 | if 'build' in c["services"][service]:
434 | build = c["services"][service]['build']
435 | if type(build) == type(""):
436 | if not os.path.exists(build):
437 | self.__log_writer__("=================== ERROR ===================")
438 | self.__log_writer__("Under service: {}".format(service))
439 | self.__log_writer__("Check build directory "+ str(build))
440 | self.__log_writer__("=============================================")
441 | elif type(build) == type(dict()):
442 | build_path = ""
443 | if 'Typing mistakes' in labelArray:
444 | err_message = ""
445 | tag_list_similarity = self.__typomistake__(build, 'build')
446 | if len(tag_list_similarity) > 0:
447 | for tag in tag_list_similarity:
448 | if len(tag_list_similarity[tag]) > 0:
449 | err_message += "I can not find '"+str(tag)+"' tag under '"+service+"' service. Maybe you can use: \n"
450 | for item in tag_list_similarity[tag]:
451 | err_message += str(item[1]) + '\n'
452 | if len(err_message) > 0:
453 | self.__log_writer__("=================== ERROR ===================")
454 | self.__log_writer__(err_message)
455 | self.__log_writer__("=============================================")
456 | if 'context' in build:
457 | if os.path.exists(build['context']):
458 | build_path = os.path.join(build_path,build['context'])
459 | else:
460 | self.__log_writer__("=================== ERROR ===================")
461 | self.__log_writer__("Under service: {}".format(service))
462 | self.__log_writer__("Check build context directory "+ str(build['context']))
463 | self.__log_writer__("=============================================")
464 | if 'dockerfile' in build:
465 | dockerfilepath = os.path.join(build_path,build['dockerfile'])
466 | if not os.path.exists(dockerfilepath):
467 | self.__log_writer__("=================== ERROR ===================")
468 | self.__log_writer__("Under service: {}".format(service))
469 | self.__log_writer__("Check build dockerfile "+ str(dockerfilepath))
470 | self.__log_writer__("=============================================")
471 | if 'configs' in c["services"][service]:
472 | configs = c["services"][service]['configs']
473 | if type(configs) == type(list()):
474 | if type(configs[0]) == type(""):
475 | for config in configs:
476 | if config not in cacheconfigs:
477 | self.__log_writer__("=================== ERROR ===================")
478 | self.__log_writer__("Under service: {}".format(service))
479 | self.__log_writer__("I can not find config "+ str(config))
480 | self.__log_writer__("=============================================")
481 | elif type(configs[0]) == type(dict()):
482 | for config in configs:
483 | if 'Typing mistakes' in labelArray:
484 | err_message = ""
485 | tag_list_similarity = self.__typomistake__(configs, 'configs')
486 | if len(tag_list_similarity) > 0:
487 | for tag in tag_list_similarity:
488 | if len(tag_list_similarity[tag]) > 0:
489 | err_message += "I can not find '"+str(tag)+"' tag under '"+service+"' service. Maybe you can use: \n"
490 | for item in tag_list_similarity[tag]:
491 | err_message += str(item[1]) + '\n'
492 | if len(err_message) > 0:
493 | self.__log_writer__("=================== ERROR ===================")
494 | self.__log_writer__(err_message)
495 | self.__log_writer__("=============================================")
496 | if 'source' in config:
497 | if config['source'] not in cacheconfigs:
498 | self.__log_writer__("=================== ERROR ===================")
499 | self.__log_writer__("Under service: {}".format(service))
500 | self.__log_writer__("I can not find 'source' {} in config ".format(str(config)))
501 | self.__log_writer__("=============================================")
502 | elif "apiVersion" in c and "items" in c:
503 | self.__log_writer__("= type: kubernetes")
504 | for service in c["items"]:
505 | name = service["metadata"]["name"]
506 | self.__log_writer__("- service:"+ str(service["kind"])+ str(name))
507 | numservices += 1
508 | if not "labels" in service["metadata"]:
509 | self.__log_writer__(" ! no labels found")
510 | faulty[contentname] = faulty.get(contentname, 0) + 1
511 | continue
512 | for label in service["metadata"]["labels"]:
513 | value = service["metadata"]["labels"][label]
514 | self.__log_writer__(" - label:"+ str(label)+ "="+ str(value))
515 | alltags[label] = alltags.get(label, 0) + 1
516 | else:
517 | self.__log_writer__("! no docker-compose or kubernetes service entries found")
518 | faulty[contentname] = faulty.get(contentname, 0) + 1
519 | continue
520 |
521 | return numservices, alltags, faulty
522 |
523 | def __sendmessage__(self, host, label, series, message):
524 | if kafka.__version__.startswith("0"):
525 | c = kafka.client.KafkaClient(hosts=[host])
526 | if series:
527 | p = kafka.producer.keyed.KeyedProducer(c)
528 | else:
529 | p = kafka.producer.simple.SimpleProducer(c)
530 | else:
531 | p = kafka.KafkaProducer(bootstrap_servers=host)
532 | success = False
533 | t = 0.2
534 | while not success:
535 | try:
536 | if kafka.__version__.startswith("0"):
537 | if series:
538 | p.send_messages(label, series.encode("utf-8"), message.encode("utf-8"))
539 | else:
540 | p.send_messages(label, message.encode("utf-8"))
541 | else:
542 | p.send(label, key=series.encode("utf-8"), value=message.encode("utf-8"))
543 | p.close()
544 | self.__log_writer__("success")
545 | success = True
546 | except Exception as e:
547 | self.__log_writer__("error (sleep {})".format(t)+ str(e))
548 | time.sleep(t)
549 | t *= 2
550 |
551 | def validator(self, autosearch, filebasedlist, urlbased, eventing, filebased=None, labelArray=[]):
552 | composefiles = []
553 |
554 | d_start = time.time()
555 | cachefiles = self.__loadcache__()
556 |
557 | if filebasedlist:
558 | f = open(filebasedlist)
559 | composefiles += [line.strip() for line in f.readlines()]
560 |
561 | if urlbased:
562 | composefiles += [urlbased]
563 | if filebased:
564 | composefiles += [filebased]
565 | if autosearch:
566 | if not cachefiles:
567 | org, basepath = autosearch.split("/")
568 | composefiles += self.__autosearch_github__(org, basepath)
569 | else:
570 | composefiles += cachefiles
571 |
572 | contents = self.__loading__(cachefiles, composefiles)
573 |
574 | numservices, alltags, faulty = self.__consistencycheck__(contents, labelArray)
575 | d_end = time.time()
576 |
577 | self.__log_writer__("services: {}".format(numservices))
578 | self.__log_writer__("labels:")
579 | for label in alltags:
580 | self.__log_writer__("- {}: {} ({:.1f}% coverage)".format(label, alltags[label], 100 * alltags[label] / numservices))
581 | self.__log_writer__("time: {:.1f}s".format(d_end - d_start))
582 |
583 | d = {}
584 | d["agent"] = "sentinel-generic-agent"
585 | d["services"] = float(numservices)
586 | for label in alltags:
587 | d[label] = float(alltags[label])
588 | d.update(faulty)
589 | if eventing:
590 | kafka, space, series = eventing.split("/")
591 | print("sending message... {}".format(d))
592 | self.__sendmessage__(kafka, space, series, json.dumps(d))
593 | else:
594 | print("not sending message... {}".format(d))
595 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | accepts@~1.3.7:
6 | version "1.3.7"
7 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
8 | integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
9 | dependencies:
10 | mime-types "~2.1.24"
11 | negotiator "0.6.2"
12 |
13 | ansi-regex@^2.0.0:
14 | version "2.1.1"
15 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
16 | integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
17 |
18 | ansi-regex@^3.0.0:
19 | version "3.0.0"
20 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
21 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
22 |
23 | ansi-styles@^3.2.1:
24 | version "3.2.1"
25 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
26 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
27 | dependencies:
28 | color-convert "^1.9.0"
29 |
30 | array-flatten@1.1.1:
31 | version "1.1.1"
32 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
33 | integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
34 |
35 | body-parser@1.19.0, body-parser@^1.18.3:
36 | version "1.19.0"
37 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
38 | integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
39 | dependencies:
40 | bytes "3.1.0"
41 | content-type "~1.0.4"
42 | debug "2.6.9"
43 | depd "~1.1.2"
44 | http-errors "1.7.2"
45 | iconv-lite "0.4.24"
46 | on-finished "~2.3.0"
47 | qs "6.7.0"
48 | raw-body "2.4.0"
49 | type-is "~1.6.17"
50 |
51 | bytes@3.1.0:
52 | version "3.1.0"
53 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
54 | integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
55 |
56 | camelcase@^5.0.0:
57 | version "5.3.1"
58 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
59 | integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
60 |
61 | chalk@^2.4.1:
62 | version "2.4.2"
63 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
64 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
65 | dependencies:
66 | ansi-styles "^3.2.1"
67 | escape-string-regexp "^1.0.5"
68 | supports-color "^5.3.0"
69 |
70 | cliui@^4.0.0:
71 | version "4.1.0"
72 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
73 | integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==
74 | dependencies:
75 | string-width "^2.1.1"
76 | strip-ansi "^4.0.0"
77 | wrap-ansi "^2.0.0"
78 |
79 | code-point-at@^1.0.0:
80 | version "1.1.0"
81 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
82 | integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
83 |
84 | color-convert@^1.9.0:
85 | version "1.9.3"
86 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
87 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
88 | dependencies:
89 | color-name "1.1.3"
90 |
91 | color-name@1.1.3:
92 | version "1.1.3"
93 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
94 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
95 |
96 | concurrently@^4.0.1:
97 | version "4.1.1"
98 | resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-4.1.1.tgz#42cf84d625163f3f5b2e2262568211ad76e1dbe8"
99 | integrity sha512-48+FE5RJ0qc8azwKv4keVQWlni1hZeSjcWr8shBelOBtBHcKj1aJFM9lHRiSc1x7lq416pkvsqfBMhSRja+Lhw==
100 | dependencies:
101 | chalk "^2.4.1"
102 | date-fns "^1.23.0"
103 | lodash "^4.17.10"
104 | read-pkg "^4.0.1"
105 | rxjs "^6.3.3"
106 | spawn-command "^0.0.2-1"
107 | supports-color "^4.5.0"
108 | tree-kill "^1.1.0"
109 | yargs "^12.0.1"
110 |
111 | content-disposition@0.5.3:
112 | version "0.5.3"
113 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
114 | integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
115 | dependencies:
116 | safe-buffer "5.1.2"
117 |
118 | content-type@~1.0.4:
119 | version "1.0.4"
120 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
121 | integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
122 |
123 | cookie-signature@1.0.6:
124 | version "1.0.6"
125 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
126 | integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
127 |
128 | cookie@0.4.0:
129 | version "0.4.0"
130 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
131 | integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
132 |
133 | cross-spawn@^6.0.0:
134 | version "6.0.5"
135 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
136 | integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
137 | dependencies:
138 | nice-try "^1.0.4"
139 | path-key "^2.0.1"
140 | semver "^5.5.0"
141 | shebang-command "^1.2.0"
142 | which "^1.2.9"
143 |
144 | date-fns@^1.23.0:
145 | version "1.30.1"
146 | resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
147 | integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
148 |
149 | debug@2.6.9:
150 | version "2.6.9"
151 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
152 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
153 | dependencies:
154 | ms "2.0.0"
155 |
156 | decamelize@^1.2.0:
157 | version "1.2.0"
158 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
159 | integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
160 |
161 | depd@~1.1.2:
162 | version "1.1.2"
163 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
164 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
165 |
166 | destroy@~1.0.4:
167 | version "1.0.4"
168 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
169 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
170 |
171 | ee-first@1.1.1:
172 | version "1.1.1"
173 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
174 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
175 |
176 | encodeurl@~1.0.2:
177 | version "1.0.2"
178 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
179 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
180 |
181 | end-of-stream@^1.1.0:
182 | version "1.4.1"
183 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
184 | integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
185 | dependencies:
186 | once "^1.4.0"
187 |
188 | error-ex@^1.3.1:
189 | version "1.3.2"
190 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
191 | integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
192 | dependencies:
193 | is-arrayish "^0.2.1"
194 |
195 | escape-html@~1.0.3:
196 | version "1.0.3"
197 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
198 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
199 |
200 | escape-string-regexp@^1.0.5:
201 | version "1.0.5"
202 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
203 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
204 |
205 | etag@~1.8.1:
206 | version "1.8.1"
207 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
208 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
209 |
210 | execa@^1.0.0:
211 | version "1.0.0"
212 | resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
213 | integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
214 | dependencies:
215 | cross-spawn "^6.0.0"
216 | get-stream "^4.0.0"
217 | is-stream "^1.1.0"
218 | npm-run-path "^2.0.0"
219 | p-finally "^1.0.0"
220 | signal-exit "^3.0.0"
221 | strip-eof "^1.0.0"
222 |
223 | express@^4.16.4:
224 | version "4.17.1"
225 | resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
226 | integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
227 | dependencies:
228 | accepts "~1.3.7"
229 | array-flatten "1.1.1"
230 | body-parser "1.19.0"
231 | content-disposition "0.5.3"
232 | content-type "~1.0.4"
233 | cookie "0.4.0"
234 | cookie-signature "1.0.6"
235 | debug "2.6.9"
236 | depd "~1.1.2"
237 | encodeurl "~1.0.2"
238 | escape-html "~1.0.3"
239 | etag "~1.8.1"
240 | finalhandler "~1.1.2"
241 | fresh "0.5.2"
242 | merge-descriptors "1.0.1"
243 | methods "~1.1.2"
244 | on-finished "~2.3.0"
245 | parseurl "~1.3.3"
246 | path-to-regexp "0.1.7"
247 | proxy-addr "~2.0.5"
248 | qs "6.7.0"
249 | range-parser "~1.2.1"
250 | safe-buffer "5.1.2"
251 | send "0.17.1"
252 | serve-static "1.14.1"
253 | setprototypeof "1.1.1"
254 | statuses "~1.5.0"
255 | type-is "~1.6.18"
256 | utils-merge "1.0.1"
257 | vary "~1.1.2"
258 |
259 | finalhandler@~1.1.2:
260 | version "1.1.2"
261 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
262 | integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
263 | dependencies:
264 | debug "2.6.9"
265 | encodeurl "~1.0.2"
266 | escape-html "~1.0.3"
267 | on-finished "~2.3.0"
268 | parseurl "~1.3.3"
269 | statuses "~1.5.0"
270 | unpipe "~1.0.0"
271 |
272 | find-up@^3.0.0:
273 | version "3.0.0"
274 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
275 | integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
276 | dependencies:
277 | locate-path "^3.0.0"
278 |
279 | forwarded@~0.1.2:
280 | version "0.1.2"
281 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
282 | integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
283 |
284 | fresh@0.5.2:
285 | version "0.5.2"
286 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
287 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
288 |
289 | get-caller-file@^1.0.1:
290 | version "1.0.3"
291 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
292 | integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
293 |
294 | get-stream@^4.0.0:
295 | version "4.1.0"
296 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
297 | integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
298 | dependencies:
299 | pump "^3.0.0"
300 |
301 | has-flag@^2.0.0:
302 | version "2.0.0"
303 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
304 | integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=
305 |
306 | has-flag@^3.0.0:
307 | version "3.0.0"
308 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
309 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
310 |
311 | hosted-git-info@^2.1.4:
312 | version "2.8.4"
313 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546"
314 | integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==
315 |
316 | http-errors@1.7.2:
317 | version "1.7.2"
318 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
319 | integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
320 | dependencies:
321 | depd "~1.1.2"
322 | inherits "2.0.3"
323 | setprototypeof "1.1.1"
324 | statuses ">= 1.5.0 < 2"
325 | toidentifier "1.0.0"
326 |
327 | http-errors@~1.7.2:
328 | version "1.7.3"
329 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
330 | integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
331 | dependencies:
332 | depd "~1.1.2"
333 | inherits "2.0.4"
334 | setprototypeof "1.1.1"
335 | statuses ">= 1.5.0 < 2"
336 | toidentifier "1.0.0"
337 |
338 | iconv-lite@0.4.24:
339 | version "0.4.24"
340 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
341 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
342 | dependencies:
343 | safer-buffer ">= 2.1.2 < 3"
344 |
345 | inherits@2.0.3:
346 | version "2.0.3"
347 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
348 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
349 |
350 | inherits@2.0.4:
351 | version "2.0.4"
352 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
353 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
354 |
355 | invert-kv@^2.0.0:
356 | version "2.0.0"
357 | resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
358 | integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
359 |
360 | ipaddr.js@1.9.0:
361 | version "1.9.0"
362 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65"
363 | integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==
364 |
365 | is-arrayish@^0.2.1:
366 | version "0.2.1"
367 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
368 | integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
369 |
370 | is-fullwidth-code-point@^1.0.0:
371 | version "1.0.0"
372 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
373 | integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
374 | dependencies:
375 | number-is-nan "^1.0.0"
376 |
377 | is-fullwidth-code-point@^2.0.0:
378 | version "2.0.0"
379 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
380 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
381 |
382 | is-stream@^1.1.0:
383 | version "1.1.0"
384 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
385 | integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
386 |
387 | isexe@^2.0.0:
388 | version "2.0.0"
389 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
390 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
391 |
392 | json-parse-better-errors@^1.0.1:
393 | version "1.0.2"
394 | resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
395 | integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
396 |
397 | lcid@^2.0.0:
398 | version "2.0.0"
399 | resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf"
400 | integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==
401 | dependencies:
402 | invert-kv "^2.0.0"
403 |
404 | locate-path@^3.0.0:
405 | version "3.0.0"
406 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
407 | integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
408 | dependencies:
409 | p-locate "^3.0.0"
410 | path-exists "^3.0.0"
411 |
412 | lodash@^4.17.10:
413 | version "4.17.15"
414 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
415 | integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
416 |
417 | map-age-cleaner@^0.1.1:
418 | version "0.1.3"
419 | resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a"
420 | integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==
421 | dependencies:
422 | p-defer "^1.0.0"
423 |
424 | media-typer@0.3.0:
425 | version "0.3.0"
426 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
427 | integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
428 |
429 | mem@^4.0.0:
430 | version "4.3.0"
431 | resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178"
432 | integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==
433 | dependencies:
434 | map-age-cleaner "^0.1.1"
435 | mimic-fn "^2.0.0"
436 | p-is-promise "^2.0.0"
437 |
438 | merge-descriptors@1.0.1:
439 | version "1.0.1"
440 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
441 | integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
442 |
443 | methods@~1.1.2:
444 | version "1.1.2"
445 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
446 | integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
447 |
448 | mime-db@1.40.0:
449 | version "1.40.0"
450 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
451 | integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
452 |
453 | mime-types@~2.1.24:
454 | version "2.1.24"
455 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
456 | integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
457 | dependencies:
458 | mime-db "1.40.0"
459 |
460 | mime@1.6.0:
461 | version "1.6.0"
462 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
463 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
464 |
465 | mimic-fn@^2.0.0:
466 | version "2.1.0"
467 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
468 | integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
469 |
470 | ms@2.0.0:
471 | version "2.0.0"
472 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
473 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
474 |
475 | ms@2.1.1:
476 | version "2.1.1"
477 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
478 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
479 |
480 | negotiator@0.6.2:
481 | version "0.6.2"
482 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
483 | integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
484 |
485 | nice-try@^1.0.4:
486 | version "1.0.5"
487 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
488 | integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
489 |
490 | normalize-package-data@^2.3.2:
491 | version "2.5.0"
492 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
493 | integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
494 | dependencies:
495 | hosted-git-info "^2.1.4"
496 | resolve "^1.10.0"
497 | semver "2 || 3 || 4 || 5"
498 | validate-npm-package-license "^3.0.1"
499 |
500 | npm-run-path@^2.0.0:
501 | version "2.0.2"
502 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
503 | integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
504 | dependencies:
505 | path-key "^2.0.0"
506 |
507 | number-is-nan@^1.0.0:
508 | version "1.0.1"
509 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
510 | integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
511 |
512 | on-finished@~2.3.0:
513 | version "2.3.0"
514 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
515 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
516 | dependencies:
517 | ee-first "1.1.1"
518 |
519 | once@^1.3.1, once@^1.4.0:
520 | version "1.4.0"
521 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
522 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
523 | dependencies:
524 | wrappy "1"
525 |
526 | os-locale@^3.0.0:
527 | version "3.1.0"
528 | resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
529 | integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
530 | dependencies:
531 | execa "^1.0.0"
532 | lcid "^2.0.0"
533 | mem "^4.0.0"
534 |
535 | p-defer@^1.0.0:
536 | version "1.0.0"
537 | resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
538 | integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
539 |
540 | p-finally@^1.0.0:
541 | version "1.0.0"
542 | resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
543 | integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
544 |
545 | p-is-promise@^2.0.0:
546 | version "2.1.0"
547 | resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
548 | integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==
549 |
550 | p-limit@^2.0.0:
551 | version "2.2.0"
552 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2"
553 | integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==
554 | dependencies:
555 | p-try "^2.0.0"
556 |
557 | p-locate@^3.0.0:
558 | version "3.0.0"
559 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
560 | integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
561 | dependencies:
562 | p-limit "^2.0.0"
563 |
564 | p-try@^2.0.0:
565 | version "2.2.0"
566 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
567 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
568 |
569 | parse-json@^4.0.0:
570 | version "4.0.0"
571 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
572 | integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
573 | dependencies:
574 | error-ex "^1.3.1"
575 | json-parse-better-errors "^1.0.1"
576 |
577 | parseurl@~1.3.3:
578 | version "1.3.3"
579 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
580 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
581 |
582 | path-exists@^3.0.0:
583 | version "3.0.0"
584 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
585 | integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
586 |
587 | path-key@^2.0.0, path-key@^2.0.1:
588 | version "2.0.1"
589 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
590 | integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
591 |
592 | path-parse@^1.0.6:
593 | version "1.0.6"
594 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
595 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
596 |
597 | path-to-regexp@0.1.7:
598 | version "0.1.7"
599 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
600 | integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
601 |
602 | pify@^3.0.0:
603 | version "3.0.0"
604 | resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
605 | integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
606 |
607 | proxy-addr@~2.0.5:
608 | version "2.0.5"
609 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34"
610 | integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==
611 | dependencies:
612 | forwarded "~0.1.2"
613 | ipaddr.js "1.9.0"
614 |
615 | pump@^3.0.0:
616 | version "3.0.0"
617 | resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
618 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
619 | dependencies:
620 | end-of-stream "^1.1.0"
621 | once "^1.3.1"
622 |
623 | qs@6.7.0:
624 | version "6.7.0"
625 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
626 | integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
627 |
628 | range-parser@~1.2.1:
629 | version "1.2.1"
630 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
631 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
632 |
633 | raw-body@2.4.0:
634 | version "2.4.0"
635 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
636 | integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
637 | dependencies:
638 | bytes "3.1.0"
639 | http-errors "1.7.2"
640 | iconv-lite "0.4.24"
641 | unpipe "1.0.0"
642 |
643 | read-pkg@^4.0.1:
644 | version "4.0.1"
645 | resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237"
646 | integrity sha1-ljYlN48+HE1IyFhytabsfV0JMjc=
647 | dependencies:
648 | normalize-package-data "^2.3.2"
649 | parse-json "^4.0.0"
650 | pify "^3.0.0"
651 |
652 | require-directory@^2.1.1:
653 | version "2.1.1"
654 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
655 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
656 |
657 | require-main-filename@^1.0.1:
658 | version "1.0.1"
659 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
660 | integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
661 |
662 | resolve@^1.10.0:
663 | version "1.12.0"
664 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6"
665 | integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==
666 | dependencies:
667 | path-parse "^1.0.6"
668 |
669 | rxjs@^6.3.3:
670 | version "6.5.2"
671 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.2.tgz#2e35ce815cd46d84d02a209fb4e5921e051dbec7"
672 | integrity sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==
673 | dependencies:
674 | tslib "^1.9.0"
675 |
676 | safe-buffer@5.1.2:
677 | version "5.1.2"
678 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
679 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
680 |
681 | "safer-buffer@>= 2.1.2 < 3":
682 | version "2.1.2"
683 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
684 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
685 |
686 | "semver@2 || 3 || 4 || 5", semver@^5.5.0:
687 | version "5.7.1"
688 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
689 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
690 |
691 | send@0.17.1:
692 | version "0.17.1"
693 | resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
694 | integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
695 | dependencies:
696 | debug "2.6.9"
697 | depd "~1.1.2"
698 | destroy "~1.0.4"
699 | encodeurl "~1.0.2"
700 | escape-html "~1.0.3"
701 | etag "~1.8.1"
702 | fresh "0.5.2"
703 | http-errors "~1.7.2"
704 | mime "1.6.0"
705 | ms "2.1.1"
706 | on-finished "~2.3.0"
707 | range-parser "~1.2.1"
708 | statuses "~1.5.0"
709 |
710 | serve-static@1.14.1:
711 | version "1.14.1"
712 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
713 | integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
714 | dependencies:
715 | encodeurl "~1.0.2"
716 | escape-html "~1.0.3"
717 | parseurl "~1.3.3"
718 | send "0.17.1"
719 |
720 | set-blocking@^2.0.0:
721 | version "2.0.0"
722 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
723 | integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
724 |
725 | setprototypeof@1.1.1:
726 | version "1.1.1"
727 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
728 | integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
729 |
730 | shebang-command@^1.2.0:
731 | version "1.2.0"
732 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
733 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
734 | dependencies:
735 | shebang-regex "^1.0.0"
736 |
737 | shebang-regex@^1.0.0:
738 | version "1.0.0"
739 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
740 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
741 |
742 | signal-exit@^3.0.0:
743 | version "3.0.2"
744 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
745 | integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
746 |
747 | spawn-command@^0.0.2-1:
748 | version "0.0.2-1"
749 | resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
750 | integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=
751 |
752 | spdx-correct@^3.0.0:
753 | version "3.1.0"
754 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
755 | integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
756 | dependencies:
757 | spdx-expression-parse "^3.0.0"
758 | spdx-license-ids "^3.0.0"
759 |
760 | spdx-exceptions@^2.1.0:
761 | version "2.2.0"
762 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977"
763 | integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==
764 |
765 | spdx-expression-parse@^3.0.0:
766 | version "3.0.0"
767 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0"
768 | integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==
769 | dependencies:
770 | spdx-exceptions "^2.1.0"
771 | spdx-license-ids "^3.0.0"
772 |
773 | spdx-license-ids@^3.0.0:
774 | version "3.0.5"
775 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
776 | integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
777 |
778 | "statuses@>= 1.5.0 < 2", statuses@~1.5.0:
779 | version "1.5.0"
780 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
781 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
782 |
783 | string-width@^1.0.1:
784 | version "1.0.2"
785 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
786 | integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
787 | dependencies:
788 | code-point-at "^1.0.0"
789 | is-fullwidth-code-point "^1.0.0"
790 | strip-ansi "^3.0.0"
791 |
792 | string-width@^2.0.0, string-width@^2.1.1:
793 | version "2.1.1"
794 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
795 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
796 | dependencies:
797 | is-fullwidth-code-point "^2.0.0"
798 | strip-ansi "^4.0.0"
799 |
800 | strip-ansi@^3.0.0, strip-ansi@^3.0.1:
801 | version "3.0.1"
802 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
803 | integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
804 | dependencies:
805 | ansi-regex "^2.0.0"
806 |
807 | strip-ansi@^4.0.0:
808 | version "4.0.0"
809 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
810 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
811 | dependencies:
812 | ansi-regex "^3.0.0"
813 |
814 | strip-eof@^1.0.0:
815 | version "1.0.0"
816 | resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
817 | integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
818 |
819 | supports-color@^4.5.0:
820 | version "4.5.0"
821 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"
822 | integrity sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=
823 | dependencies:
824 | has-flag "^2.0.0"
825 |
826 | supports-color@^5.3.0:
827 | version "5.5.0"
828 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
829 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
830 | dependencies:
831 | has-flag "^3.0.0"
832 |
833 | toidentifier@1.0.0:
834 | version "1.0.0"
835 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
836 | integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
837 |
838 | tree-kill@^1.1.0:
839 | version "1.2.1"
840 | resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.1.tgz#5398f374e2f292b9dcc7b2e71e30a5c3bb6c743a"
841 | integrity sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==
842 |
843 | tslib@^1.9.0:
844 | version "1.10.0"
845 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
846 | integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
847 |
848 | type-is@~1.6.17, type-is@~1.6.18:
849 | version "1.6.18"
850 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
851 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
852 | dependencies:
853 | media-typer "0.3.0"
854 | mime-types "~2.1.24"
855 |
856 | unpipe@1.0.0, unpipe@~1.0.0:
857 | version "1.0.0"
858 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
859 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
860 |
861 | utils-merge@1.0.1:
862 | version "1.0.1"
863 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
864 | integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
865 |
866 | validate-npm-package-license@^3.0.1:
867 | version "3.0.4"
868 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
869 | integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
870 | dependencies:
871 | spdx-correct "^3.0.0"
872 | spdx-expression-parse "^3.0.0"
873 |
874 | vary@~1.1.2:
875 | version "1.1.2"
876 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
877 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
878 |
879 | which-module@^2.0.0:
880 | version "2.0.0"
881 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
882 | integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
883 |
884 | which@^1.2.9:
885 | version "1.3.1"
886 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
887 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
888 | dependencies:
889 | isexe "^2.0.0"
890 |
891 | wrap-ansi@^2.0.0:
892 | version "2.1.0"
893 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
894 | integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=
895 | dependencies:
896 | string-width "^1.0.1"
897 | strip-ansi "^3.0.1"
898 |
899 | wrappy@1:
900 | version "1.0.2"
901 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
902 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
903 |
904 | "y18n@^3.2.1 || ^4.0.0":
905 | version "4.0.0"
906 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
907 | integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
908 |
909 | yargs-parser@^11.1.1:
910 | version "11.1.1"
911 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
912 | integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==
913 | dependencies:
914 | camelcase "^5.0.0"
915 | decamelize "^1.2.0"
916 |
917 | yargs@^12.0.1:
918 | version "12.0.5"
919 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
920 | integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
921 | dependencies:
922 | cliui "^4.0.0"
923 | decamelize "^1.2.0"
924 | find-up "^3.0.0"
925 | get-caller-file "^1.0.1"
926 | os-locale "^3.0.0"
927 | require-directory "^2.1.1"
928 | require-main-filename "^1.0.1"
929 | set-blocking "^2.0.0"
930 | string-width "^2.0.0"
931 | which-module "^2.0.0"
932 | y18n "^3.2.1 || ^4.0.0"
933 | yargs-parser "^11.1.1"
934 |
--------------------------------------------------------------------------------