Merge branch 'restructure_project' of xicon/xtr-improved-frontend into master

This commit is contained in:
xiconluan 2019-11-20 23:20:55 +01:00 committed by Gitea
commit cc8eb07afa
12 changed files with 8480 additions and 1 deletions

5
.babelrc.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@babel/preset-env'
],
}

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -1,3 +1,17 @@
# xtr-improved-frontend
This is the improved frontend/webgui for xtr.
This is the improved frontend/webgui for xtr.
## Install / Build guide
Install all needed node packages - `node_modules` folder will be created
```
$ npm install
```
To build final sources for production environment in `dist` folder
```
$ npm run build
```
Running app in development mode - accessible under http://localhost:8080/
````
$ npm run serve
````

View File

View File

7974
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "xtr",
"version": "0.0.2",
"description": "xtr",
"main": "main.js",
"scripts": {
"clean": "rimraf dist",
"build": "npm run clean && webpack --mode production",
"serve": "webpack-dev-server --mode development"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.6.4",
"@babel/preset-env": "^7.6.3",
"babel-loader": "^8.0.4",
"css-loader": "^1.0.1",
"html-webpack-plugin": "^3.2.0",
"rimraf": "^2.7.1",
"vue": "^2.5.17",
"vue-loader": "^15.7.2",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.5.17",
"axios": "^0.19.0",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
}
}

205
src/StatusBox.vue Normal file
View File

@ -0,0 +1,205 @@
<template>
<div id="statusBox">
<div class="row">
<div class="col-4">
<select v-model="selectedIp" id="serverSelectList">
<option v-for="(serverItem, index) in serverlist"
v-bind:value="serverItem"
>
{{ serverItem[0] }}:{{ serverItem[1] }} (AS{{ serverItem[2] }})
</option>
</select>
<input v-model="target" placeholder="Enter Ip or Domain">
<a href="#" class="traceroutes" v-on:click="traceRoute()"></a>
<span class="small">{{executionTime}}</span>
<div>
<span>History: {{ tableDataHistory.length }}</span>
<table :id="'tableId_'+boxNumber" class="table table-striped">
<thead>
<tr>
<td>IP</td>
<td>HOSTNAME</td>
<td>PING</td>
<td>AS</td>
</tr>
<tr v-for="tData in tableData">
<td>{{ tData[0] }}</td>
<td>{{ tData[1] }}</td>
<td>{{ tData[2] }}</td>
<td :title="tData[4]">{{ tData[3] }}</td>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
props: {
serverlist: Array,
boxNumber: Number
},
watch: {
tableDataHistory: {
handler() {
sessionStorage.setItem('tableDataHistory' + this.boxNumber, JSON.stringify(this.tableDataHistory));
}
},
executionTime: {
handler() {
sessionStorage.setItem('executionTime' + this.boxNumber, JSON.stringify(this.executionTime));
},
deep: true
},
selectedIp: {
handler() {
sessionStorage.setItem('selectedIp' + this.boxNumber, JSON.stringify(this.selectedIp));
},
deep: true
},
target : {
handler() {
sessionStorage.setItem('target' + this.boxNumber, JSON.stringify(this.target));
},
deep: true
},
tableData : {
handler() {
sessionStorage.setItem('tableData' + this.boxNumber, JSON.stringify(this.tableData));
},
deep: true
}
},
methods: {
setLastExecutionTime: function setLastExecutionTime() {
var day = new Date();
var hours = day.getHours();
var minutes = day.getMinutes() ;
var seconds = day.getSeconds();
if (hours < 10) {
hours = '0' + hours;
}
if (minutes < 10) {
minutes = '0' + minutes;
}
if (seconds < 10) {
seconds = '0' + seconds;
}
this.executionTime = hours + ':' + minutes + ':' + seconds;
},
traceRoute: function traceRoute() {
// e.g. http://199.247.22.146:12111/v3/client/request/8.8.8.8
axios.get("http://"+ this.selectedIp[0] + ":" +this.selectedIp[1] + "/v3/client/request/" + this.target)
.then(response => {
// JSON responses are automatically parsed.
this.tableData = response.data;
this.setLastExecutionTime();
this.tableDataHistory.push(this.tableData);
})
.catch(e => {
this.executionTime = 'Check request'
})
},
resetData: function resetData() {
this.executionTime = 'Not execute yet',
this.selectedIp = ["199.247.22.146",12111,20473,1],
this.target = '8.8.8.8',
this.tableData = [ ],
this.tableDataHistory = [ ]
}
},
mounted() {
if (sessionStorage.getItem('executionTime'+ this.boxNumber)) this.executionTime = JSON.parse(sessionStorage.getItem('executionTime'+ this.boxNumber));
if (sessionStorage.getItem('selectedIp'+ this.boxNumber)) {
this.selectedIp = JSON.parse(sessionStorage.getItem('selectedIp'+ this.boxNumber));
} else {
this.selectedIp = ["199.247.22.146",12111,20473,1]; // Change from static to dynamic
}
if (sessionStorage.getItem('tableDataHistory'+ this.boxNumber)) this.tableDataHistory = JSON.parse(sessionStorage.getItem('tableDataHistory'+ this.boxNumber));
if (sessionStorage.getItem('target'+ this.boxNumber)) this.target = JSON.parse(sessionStorage.getItem('target'+ this.boxNumber));
if (sessionStorage.getItem('tableData'+ this.boxNumber)) this.tableData = JSON.parse(sessionStorage.getItem('tableData'+ this.boxNumber));
},
data: function () {
return {
executionTime: 'Not execute yet',
selectedIp: ["199.247.22.146",12111,20473,1],
target: '8.8.8.8',
tableData : [ ],
tableDataHistory: []
}
}
}
</script>
<style scoped>
span.small {
font-size: 75%;
}
table.active {
border: 3px solid #9ac5e3;
}
table {
border-collapse: collapse;
width: 90%;
}
td
{
border:1px solid grey;
font-size: 9pt;
}
.traceroutes
{
text-decoration: none;
display: inline-block;
}
.traceroutes:visited
{
color: #000000;
}
.traceroutes:hover
{
color: #880000;
transform: rotate(-30deg);
}
.traceroutes:active
{
color: #bb0000;
}
.traceroutes_rotating
{
display: inline-block;
text-decoration: none;
color: #bb0000;
animation: spin 2s linear infinite;
}
@keyframes spin
{
0% { transform: rotate(-30deg); }
25% { transform: rotate(0deg); }
50% { transform: rotate(30deg); }
75% { transform: rotate(0deg); }
100% { transform: rotate(-30deg); }
}
</style>

212
src/StatusBoxContainer.vue Normal file
View File

@ -0,0 +1,212 @@
<template>
<div id="statusBoxContainer">
<div>
<button v-on:click="resetSession()">Reset whole session</button>
</div>
<!-- Hover icon section -->
<div id="info" class="info" title="test">
<div id="info_box" class="info_box">
This service is free for use by accepting the <a href="aup.txt">Acceptable Use Policy</a>.<br>
<br>
This is just an interface to to give a more GUI-ish
access to the self-registered XTR clients. No traceroute requests will be saved on this server. All request to the XTR clients
are handled by your browser.<br>
<br>
Want to add your server? <a href="xtr.pl">Download XTR client</a> now. Readme included in source code!<br>
Want to run your own master server? <a href="xtrd.pl">Download XTRd</a>. Readme included in source code!<br>
<br>
Questions?/Impress? -> <a href="mailto:rest@xicon.de">Friedrich Schrader</a>
</div>
</div>
<div>
<button v-on:click="executeSpecificBoxes(checkedBoxes)">Run selected</button>
<div style="display: inline-block;">
<input
:id="index"
type="checkbox"
v-for="(traceBox, index) in traceBoxes"
v-model="checkedBoxes"
:value="index"
@click="setActive(index)"
>
</div>
</div>
<br>
<button v-on:click="executeAllBoxes()">Request for all boxes</button>
<button v-on:click="addTraceBox()">Add new TraceBox</button>
<div class="grid-container">
<template v-for="(traceBox, index) in traceBoxes">
<component :is="traceBox" :ref="'boxref'" :key="index" v-bind:serverlist="listOfServers" v-bind:boxNumber="index"></component>
</template>
</div>
</div>
</template>
<script>
import axios from 'axios';
import StatusBoxVue from './StatusBox.vue';
export default {
components: {
StatusBoxVue
},
data: function () {
return {
listOfServers: [],
checkedBoxes: [],
traceBoxes: [
StatusBoxVue,
StatusBoxVue,
StatusBoxVue,
StatusBoxVue,
StatusBoxVue,
StatusBoxVue,
]
}
},
watch: {
checkedBoxes : {
handler() {
sessionStorage.setItem('checkedBoxes', JSON.stringify(this.checkedBoxes));
},
deep: true
} ,
listOfServers: {
handler() {
sessionStorage.setItem('listOfServers', JSON.stringify(this.listOfServers));
},
deep: true
},
traceBoxes: {
handler() {
sessionStorage.setItem('traceBoxes', JSON.stringify(this.traceBoxes));
},
}
},
mounted() {
if (sessionStorage.getItem('listOfServers')) {
this.listOfServers = JSON.parse(sessionStorage.getItem('listOfServers'));
} else {
this.getServer();
}
if (sessionStorage.getItem('traceBoxes')) {
this.traceBoxes = [];
JSON.parse(sessionStorage.getItem('traceBoxes')).forEach(element => {
this.traceBoxes.push(StatusBoxVue);
});
}
if (sessionStorage.getItem('checkedBoxes')) this.checkedBoxes = JSON.parse(sessionStorage.getItem('checkedBoxes'));
this.setActiveFromSession(this.checkedBoxes);
},
methods: {
resetData: function () {
var boxesRefs = this.$refs.boxref;
boxesRefs.forEach(function(boxRef) {
boxRef.resetData();
});
this.checkedBoxes = [];
this.traceBoxes = [
StatusBoxVue,
StatusBoxVue,
StatusBoxVue,
StatusBoxVue,
StatusBoxVue,
StatusBoxVue,
];
},
resetSession: function resetSession() {
this.resetData();
sessionStorage.clear();
document.querySelectorAll('[id^=tableId_]').forEach(element => {
element.className = 'table table-striped';
});
},
setActive: function setActive(tableId) {
var settedClasses = document.getElementById("tableId_"+tableId).className;
if (settedClasses == 'table table-striped') {
document.getElementById("tableId_"+tableId).className = 'table table-striped active';
} else {
document.getElementById("tableId_"+tableId).className = 'table table-striped';
}
},
setActiveFromSession: function setActiveFromSession(tableIds) {
tableIds.forEach(tableId => {
this.setActive(tableId);
});
},
executeAllBoxes: function () {
var boxesRefs = this.$refs.boxref;
boxesRefs.forEach(function(boxRef) {
boxRef.traceRoute();
});
},
executeSpecificBoxes: function (choosedBoxes) {
var boxesRefs = this.$refs.boxref;
choosedBoxes.forEach(function(choosedBox) {
boxesRefs.forEach(function(boxRef) {
if(choosedBox == boxRef.boxNumber) {
boxRef.traceRoute();
}
});
});
},
addTraceBox () {
this.traceBoxes.push(StatusBoxVue);
},
getServer: function getServerList() {
axios.get("http://xtr-master.xicon.eu/v3/server/list/online")
.then(response => {
// JSON responses are automatically parsed.
this.listOfServers = response.data;
})
.catch(e => {
this.errors.push(e)
})
},
}
}
</script>
<style scoped>
h4 {
text-align: center;
}
.grid-container {
display: grid;
grid-template-columns: auto auto auto;
grid-gap: 5px;
padding: 5px;
}
.info
{
display: block; position: absolute; right: 10px; top: 10px;
}
.info:hover .info_box
{
visibility: visible;
z-index: 4;
}
.info .info_box
{
display: block;
position: absolute;
right: 0px;
top: 0px;
visibility: hidden;
width: 502px;
border: solid 1px;
background-color: #999999;
font-size: 10pt;
}
</style>

13
src/index.html Normal file
View File

@ -0,0 +1,13 @@
<html>
<head>
<meta charset="UTF-8">
<title>XTR :: xicon trace route</title>
</head>
<body>
<section>
<div id="statusBoxContainer"></div>
</section>
</body>
</html>

7
src/main.js Normal file
View File

@ -0,0 +1,7 @@
import Vue from 'vue';
import StatusBoxContainer from './StatusBoxContainer.vue';
new Vue({
el: '#statusBoxContainer',
render: b => b(StatusBoxContainer),
});

19
webpack.config.js Normal file
View File

@ -0,0 +1,19 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
entry: './src/main.js',
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' },
{ test: /\.vue$/, use: 'vue-loader' },
{ test: /\.css$/, use: ['vue-style-loader', 'css-loader']},
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new VueLoaderPlugin(),
]
};