Merge branch 'restructure_project' of xicon/xtr-improved-frontend into master
This commit is contained in:
commit
cc8eb07afa
5
.babelrc.js
Normal file
5
.babelrc.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@babel/preset-env'
|
||||
],
|
||||
}
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
node_modules
|
14
README.md
14
README.md
@ -1,3 +1,17 @@
|
||||
# xtr-improved-frontend
|
||||
|
||||
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
|
||||
````
|
0
index.html → dist/index.html
vendored
0
index.html → dist/index.html
vendored
0
main.js → dist/main.js
vendored
0
main.js → dist/main.js
vendored
7974
package-lock.json
generated
Normal file
7974
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
package.json
Normal file
29
package.json
Normal 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
205
src/StatusBox.vue
Normal 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
212
src/StatusBoxContainer.vue
Normal 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
13
src/index.html
Normal 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
7
src/main.js
Normal 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
19
webpack.config.js
Normal 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(),
|
||||
]
|
||||
};
|
Loading…
Reference in New Issue
Block a user