mirror of https://github.com/statping/statping
updates
parent
30e64f688c
commit
978bc17768
3
Makefile
3
Makefile
|
@ -20,6 +20,9 @@ release: dev-deps
|
|||
frontend:
|
||||
cd frontend && yarn serve
|
||||
|
||||
frontend-build:
|
||||
cd frontend && rm -rf dist && yarn build
|
||||
|
||||
# build and push the images to docker hub
|
||||
docker: docker-build-all docker-publish-all
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"apexcharts": "^3.15.0",
|
||||
"axios": "^0.19.1",
|
||||
"core-js": "^3.4.4",
|
||||
"querystring": "^0.2.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-apexcharts": "^1.5.2",
|
||||
"vue-router": "^3.1.3"
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<router-view/>
|
||||
<Footer/>
|
||||
<Footer version="DEV" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Footer from "./components/Footer";
|
||||
export default {
|
||||
import Footer from "./components/Footer";
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
Footer
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 96 KiB |
|
@ -1,5 +1,8 @@
|
|||
import axios from 'axios'
|
||||
|
||||
const qs = require('querystring')
|
||||
const tokenKey = "statping_user";
|
||||
|
||||
class Api {
|
||||
constructor() {
|
||||
|
||||
|
@ -21,10 +24,52 @@ class Api {
|
|||
return axios.get('/api/groups').then(response => (response.data))
|
||||
}
|
||||
|
||||
async users () {
|
||||
return axios.get('/api/users').then(response => (response.data))
|
||||
}
|
||||
|
||||
async messages () {
|
||||
return axios.get('/api/messages').then(response => (response.data))
|
||||
}
|
||||
|
||||
async group (id) {
|
||||
return axios.get('/api/groups/'+id).then(response => (response.data))
|
||||
}
|
||||
|
||||
async notifiers () {
|
||||
return axios.get('/api/notifiers').then(response => (response.data))
|
||||
}
|
||||
|
||||
async login (username, password) {
|
||||
const f = {username: username, password: password}
|
||||
return axios.post('/api/login', qs.stringify(f))
|
||||
.then(response => (response.data))
|
||||
}
|
||||
|
||||
async logout () {
|
||||
await axios.get('/api/logout').then(response => (response.data))
|
||||
return localStorage.removeItem(tokenKey)
|
||||
}
|
||||
|
||||
saveToken (username, token) {
|
||||
const user = {username: username, token: token}
|
||||
localStorage.setItem(tokenKey, JSON.stringify(user));
|
||||
return user
|
||||
}
|
||||
|
||||
token () {
|
||||
return JSON.parse(localStorage.getItem(tokenKey));
|
||||
}
|
||||
|
||||
authToken () {
|
||||
let user = JSON.parse(localStorage.getItem(tokenKey));
|
||||
if (user && user.token) {
|
||||
return { 'Authorization': 'Bearer ' + user.token };
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
const api = new Api()
|
||||
export default api
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<div class="col-12 mt-3">
|
||||
|
||||
<div class="row stats_area mb-5">
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{services.length}}</span>
|
||||
Total Services
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="lg_number">48</span>
|
||||
Failures last 24 Hours
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="lg_number">7</span>
|
||||
Online Services
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="(service, index) in services" v-bind:key="index">
|
||||
<ServiceInfo :service=service />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ServiceInfo from "./ServiceInfo";
|
||||
|
||||
export default {
|
||||
name: 'DashboardIndex',
|
||||
components: {
|
||||
ServiceInfo
|
||||
},
|
||||
props: {
|
||||
services: Array
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -0,0 +1,181 @@
|
|||
<!--
|
||||
- Statup
|
||||
- Copyright (C) 2020. Hunter Long and the project contributors
|
||||
- Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
-
|
||||
- https://github.com/hunterlong/statup
|
||||
-
|
||||
- The licenses for most software and other practical works are designed
|
||||
- to take away your freedom to share and change the works. By contrast,
|
||||
- the GNU General Public License is intended to guarantee your freedom to
|
||||
- share and change all versions of a program--to make sure it remains free
|
||||
- software for all its users.
|
||||
-
|
||||
- You should have received a copy of the GNU General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="col-12">
|
||||
<h1 class="text-black-50">Messages</h1>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Title</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">Service</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">Begins</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr v-for="(message, index) in messages" v-bind:key="index">
|
||||
<td>{{message.title}}</td>
|
||||
<td class="d-none d-md-table-cell"><a href="service/1">{{message.service}}</a></td>
|
||||
<td class="d-none d-md-table-cell">{{message.start_on}}</td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<a href="message/1" class="btn btn-outline-secondary"><i class="fas fa-exclamation-triangle"></i> Edit</a>
|
||||
<a href="api/messages/1" class="ajax_delete btn btn-danger"><i class="fas fa-times"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12">
|
||||
<h1 class="text-black-50 mt-5">Create Message</h1>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<form class="ajax_form" action="api/messages" data-redirect="messages" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-4 col-form-label">Title</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="title" class="form-control" value="" id="title" placeholder="Message Title" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-4 col-form-label">Description</label>
|
||||
<div class="col-sm-8">
|
||||
<textarea rows="5" name="description" class="form-control" id="description" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Message Date Range</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" name="start_on" class="form-control form-control-plaintext" id="start_on" value="0001-01-01T00:00:00Z" required>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" name="end_on" class="form-control form-control-plaintext" id="end_on" value="0001-01-01T00:00:00Z" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="service_id" class="col-sm-4 col-form-label">Service</label>
|
||||
<div class="col-sm-8">
|
||||
<select class="form-control" name="service" id="service_id">
|
||||
<option value="0" selected>Global Message</option>
|
||||
|
||||
|
||||
<option value="7" >Statping API</option>
|
||||
|
||||
|
||||
<option value="6" >Push Notification Server</option>
|
||||
|
||||
|
||||
<option value="1" >Google</option>
|
||||
|
||||
|
||||
<option value="2" >Statping Github</option>
|
||||
|
||||
|
||||
<option value="3" >JSON Users Test</option>
|
||||
|
||||
|
||||
<option value="4" >JSON API Tester</option>
|
||||
|
||||
|
||||
<option value="5" >Google DNS</option>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="notify_method" class="col-sm-4 col-form-label">Notification Method</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="notify_method" class="form-control" id="notify_method" value="" placeholder="email">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="notify_method" class="col-sm-4 col-form-label">Notify Users</label>
|
||||
<div class="col-sm-8">
|
||||
<span class="switch">
|
||||
<input type="checkbox" name="notify_users-value" class="switch" id="switch-normal">
|
||||
<label for="switch-normal">Notify Users Before Scheduled Time</label>
|
||||
<input type="hidden" name="notify_users" id="switch-normal-value" value="false">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="notify_before" class="col-sm-4 col-form-label">Notify Before</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="form-inline">
|
||||
<input type="number" name="notify_before" class="col-4 form-control" id="notify_before" value="0">
|
||||
<select class="ml-2 col-7 form-control" name="notify_before_scale" id="notify_before_scale">
|
||||
<option value="minute">Minutes</option>
|
||||
<option value="hour">Hours</option>
|
||||
<option value="day">Days</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-primary btn-block">Create Message</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API"
|
||||
|
||||
export default {
|
||||
name: 'DashboardMessages',
|
||||
data () {
|
||||
return {
|
||||
messages: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getMessages()
|
||||
},
|
||||
methods: {
|
||||
async getMessages () {
|
||||
this.messages = await Api.messages()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -0,0 +1,146 @@
|
|||
<!--
|
||||
- Statup
|
||||
- Copyright (C) 2020. Hunter Long and the project contributors
|
||||
- Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
-
|
||||
- https://github.com/hunterlong/statup
|
||||
-
|
||||
- The licenses for most software and other practical works are designed
|
||||
- to take away your freedom to share and change the works. By contrast,
|
||||
- the GNU General Public License is intended to guarantee your freedom to
|
||||
- share and change all versions of a program--to make sure it remains free
|
||||
- software for all its users.
|
||||
-
|
||||
- You should have received a copy of the GNU General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="col-12">
|
||||
<h1 class="text-black-50">Services <a href="service/create" class="btn btn-outline-success mt-1 float-right">
|
||||
<i class="fas fa-plus"></i> Create</a>
|
||||
</h1>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">Status</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">Visibility</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">Group</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="sortable" id="services_table">
|
||||
|
||||
<tr v-for="(service, index) in services" v-bind:key="index">
|
||||
<td><span class="drag_icon d-none d-md-inline"><i class="fas fa-bars"></i></span> {{service.name}}</td>
|
||||
<td class="d-none d-md-table-cell"><span class="badge badge-success">ONLINE</span>
|
||||
<i class="toggle-service fas fa-toggle-on text-success" data-online="true" data-id="1"></i>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell"><span class="badge badge-primary">PUBLIC</span></td>
|
||||
<td class="d-none d-md-table-cell"><span class="badge badge-secondary">Main Services</span></td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<router-link :to="{path: `/service/${service.id}`, params: {service: service} }" class="btn btn-outline-secondary"><i class="fas fa-chart-area"></i> View</router-link>
|
||||
<a href="api/services/1" class="ajax_delete btn btn-danger" data-method="DELETE" data-obj="service_1" data-id="1"><i class="fas fa-times"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-5">
|
||||
|
||||
<h1 class="text-muted">Groups</h1>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Services</th>
|
||||
<th scope="col">Visibility</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="sortable_groups" id="groups_table">
|
||||
|
||||
<tr v-for="(group, index) in groups" v-bind:key="index">
|
||||
<td><span class="drag_icon d-none d-md-inline"><i class="fas fa-bars"></i></span>{{group.name}}</td>
|
||||
<td></td>
|
||||
<td><span class="badge badge-secondary">PRIVATE</span></td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<a href="group/2" class="btn btn-outline-secondary"><i class="fas fa-chart-area"></i> Edit</a>
|
||||
<a href="api/groups/2" class="ajax_delete btn btn-danger" data-method="DELETE" data-obj="group_2" data-id="2"><i class="fas fa-times"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<h1 class="text-muted mt-5">Create Group</h1>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<form class="ajax_form" action="api/groups" data-redirect="services" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="title" class="col-sm-4 col-form-label">Group Name</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="name" class="form-control" value="" id="title" placeholder="Group Name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-group-public" class="col-sm-4 col-form-label">Public Group</label>
|
||||
<div class="col-8 mt-1">
|
||||
<span class="switch float-left">
|
||||
<input type="checkbox" name="public" class="switch" id="switch-group-public" >
|
||||
<label for="switch-group-public">Show group services to the public</label>
|
||||
<input type="hidden" name="public" id="switch-group-public-value" value="false">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-primary btn-block">Create Group</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'DashboardServices',
|
||||
props: {
|
||||
services: Array,
|
||||
groups: Array,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -0,0 +1,114 @@
|
|||
<!--
|
||||
- Statup
|
||||
- Copyright (C) 2020. Hunter Long and the project contributors
|
||||
- Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
-
|
||||
- https://github.com/hunterlong/statup
|
||||
-
|
||||
- The licenses for most software and other practical works are designed
|
||||
- to take away your freedom to share and change the works. By contrast,
|
||||
- the GNU General Public License is intended to guarantee your freedom to
|
||||
- share and change all versions of a program--to make sure it remains free
|
||||
- software for all its users.
|
||||
-
|
||||
- You should have received a copy of the GNU General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="col-12">
|
||||
<h1 class="text-black-50">Users</h1>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="users_table">
|
||||
|
||||
<tr v-for="(user, index) in users" v-bind:key="index" >
|
||||
<td>{{user.username}}</td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<a href="user/1" class="btn btn-outline-secondary"><i class="fas fa-user-edit"></i> Edit</a>
|
||||
<a href="api/users/1" class="ajax_delete btn btn-danger"><i class="fas fa-times"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h1 class="text-black-50 mt-5">Create User</h1>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="ajax_form" action="api/users" data-redirect="users" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-4 col-form-label">Username</label>
|
||||
<div class="col-6 col-md-4">
|
||||
<input type="text" name="username" class="form-control" value="" id="username" placeholder="Username" required autocorrect="off" autocapitalize="none">
|
||||
</div>
|
||||
<div class="col-6 col-md-4">
|
||||
<span class="switch">
|
||||
<input type="checkbox" name="admin" class="switch" id="switch-normal">
|
||||
<label for="switch-normal">Administrator</label>
|
||||
<input type="hidden" name="admin" id="switch-normal-value" value="false">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="email" class="col-sm-4 col-form-label">Email Address</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="email" name="email" class="form-control" id="email" value="" placeholder="user@domain.com" required autocapitalize="none" spellcheck="false">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="password" class="col-sm-4 col-form-label">Password</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="password" name="password" class="form-control" id="password" placeholder="Password" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="password_confirm" class="col-sm-4 col-form-label">Confirm Password</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="password" name="password_confirm" class="form-control" id="password_confirm" placeholder="Confirm Password" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-primary btn-block">Create User</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API"
|
||||
|
||||
export default {
|
||||
name: 'DashboardUsers',
|
||||
data () {
|
||||
return {
|
||||
users: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getUsers()
|
||||
},
|
||||
methods: {
|
||||
async getUsers () {
|
||||
this.users = await Api.users()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<div class="col-12 card mb-3" style="min-height: 260px">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><a href="service/7">{{service.name}}</a>
|
||||
<span class="badge float-right badge-success">{{service.online ? "ONLINE" : "OFFLINE"}}</span>
|
||||
</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div id="spark_service_7_1"></div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div id="spark_service_7_2"></div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div id="spark_service_7_3"></div>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-6">
|
||||
<div id="spark_service_7_4"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ServiceInfo',
|
||||
props: {
|
||||
service: Object
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<router-link to="/" class="navbar-brand">Statping</router-link>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarText">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a v-on:click="changeView('DashboardIndex', '/dashboard')" class="nav-link" href="#">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a v-on:click="changeView('DashboardServices', '/dashboard/services')" class="nav-link" href="#">Services</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a v-on:click="changeView('DashboardUsers', '/dashboard/users')" class="nav-link" href="#">Users</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a v-on:click="changeView('DashboardMessages', '/dashboard/messages')" class="nav-link" href="#">Messages</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a v-on:click="changeView('Settings', '/dashboard/settings')" class="nav-link" href="#">Settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a v-on:click="changeView('Logs', '/dashboard/logs')" class="nav-link" href="#">Logs</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a v-on:click="changeView('DashboardIndex', '/dashboard/help')" class="nav-link" href="#">Help</a>
|
||||
</li>
|
||||
</ul>
|
||||
<span class="navbar-text">
|
||||
<a href="#" class="nav-link" v-on:click="logout">Logout</a>
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API"
|
||||
|
||||
export default {
|
||||
name: 'TopNav',
|
||||
props: {
|
||||
changeView: Function
|
||||
},
|
||||
methods: {
|
||||
async logout () {
|
||||
await Api.logout()
|
||||
await this.$router.push('/')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,14 +1,18 @@
|
|||
<template>
|
||||
<footer>
|
||||
<div class="footer text-center mb-4 p-2">
|
||||
<a href="https://github.com/hunterlong/statping" target="_blank">Statping 0.80.70 made with <i class="text-danger fas fa-heart"></i></a> | <a href="dashboard">Dashboard</a>
|
||||
<a href="https://github.com/hunterlong/statping" target="_blank">Statping {{version}} made with <i class="text-danger fas fa-heart"></i></a> |
|
||||
<router-link to="/dashboard">Dashboard</router-link>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Footer'
|
||||
name: 'Footer',
|
||||
props: {
|
||||
version: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="col-12">
|
||||
<h4 class="mt-3"><a href="service/1">{{service.name}}</a>
|
||||
<h4 class="mt-3">
|
||||
<router-link :to="`/service/${service.id}`">{{service.name}}</router-link>
|
||||
<span class="badge bg-success float-right">{{service.online ? "ONLINE" : "OFFLINE"}}</span>
|
||||
</h4>
|
||||
|
||||
|
@ -34,7 +35,7 @@
|
|||
<span class="d-none d-md-inline">Online, last Failure was Wednesday 1:16:49PM, Dec 18 2019</span>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<a href="service/1" class="btn btn-success btn-sm float-right dyn-dark btn-block">View Service</a>
|
||||
<router-link :to="`/service/${service.id}`" class="btn btn-success btn-sm float-right dyn-dark btn-block">View Service</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -3,29 +3,43 @@ import App from './App.vue'
|
|||
import VueRouter from 'vue-router'
|
||||
import Index from "./pages/Index";
|
||||
import Dashboard from "./pages/Dashboard";
|
||||
import Login from "./pages/Login";
|
||||
import Settings from "./pages/Settings";
|
||||
import Service from "./pages/Service";
|
||||
import Services from "./pages/Services";
|
||||
import Service from "./pages/Service";
|
||||
|
||||
require("./assets/css/bootstrap.min.css")
|
||||
require("./assets/css/base.css")
|
||||
|
||||
// require("./assets/js/bootstrap.min")
|
||||
// require("./assets/js/flatpickr")
|
||||
// require("./assets/js/inputTags.min")
|
||||
// require("./assets/js/rangePlugin")
|
||||
// require("./assets/js/sortable.min")
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Index',
|
||||
component: Index
|
||||
},
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: Dashboard
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Settings',
|
||||
component: Settings
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'Index',
|
||||
component: Index
|
||||
},
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: Dashboard,
|
||||
alias: ['/dashboard/settings', '/dashboard/services', '/dashboard/messages', '/dashboard/groups', '/dashboard/users', '/dashboard/logs', '/dashboard/help']
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: Login
|
||||
},
|
||||
{ path: '/logout', redirect: '/' },
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Settings',
|
||||
component: Settings
|
||||
},
|
||||
{
|
||||
path: '/services',
|
||||
name: 'Services',
|
||||
|
@ -34,12 +48,12 @@ const routes = [
|
|||
{
|
||||
path: '/service/:id',
|
||||
name: 'Service',
|
||||
component: Service
|
||||
component: Service,
|
||||
props: true
|
||||
}
|
||||
];
|
||||
|
||||
const router = new VueRouter
|
||||
({
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
routes
|
||||
})
|
||||
|
|
|
@ -1,53 +1,85 @@
|
|||
<template>
|
||||
<div v-show="core" class="container col-md-7 col-sm-12 mt-2 sm-container">
|
||||
<div>
|
||||
<Login v-show="token === null"/>
|
||||
|
||||
<Header :core="core"/>
|
||||
<div v-show="token !== null" class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
|
||||
<div v-for="(group, index) in groups" v-bind:key="index">
|
||||
<Group :group=group />
|
||||
</div>
|
||||
<TopNav :changeView="changeView"/>
|
||||
|
||||
<div class="col-12">
|
||||
<MessageBlock/>
|
||||
</div>
|
||||
<DashboardIndex v-show="view === 'DashboardIndex'" :services="services"/>
|
||||
|
||||
<div class="col-12 full-col-12">
|
||||
<DashboardServices v-show="view === 'DashboardServices'" :services="services"/>
|
||||
|
||||
<div v-for="(service, index) in services" v-bind:key="index">
|
||||
<ServiceBlock :service=service />
|
||||
</div>
|
||||
<DashboardUsers v-show="view === 'DashboardUsers'" :services="services"/>
|
||||
|
||||
</div>
|
||||
<DashboardMessages v-show="view === 'DashboardMessages'" :services="services"/>
|
||||
|
||||
<Settings v-show="view === 'Settings'" :services="services"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ServiceBlock from '../components/Service/ServiceBlock.vue'
|
||||
import MessageBlock from "../components/Index/MessageBlock";
|
||||
import Group from "../components/Index/Group";
|
||||
import Header from "../components/Index/Header";
|
||||
import Api from "../components/API"
|
||||
import Api from "../components/API"
|
||||
import Login from "./Login";
|
||||
import TopNav from "../components/Dashboard/TopNav";
|
||||
import DashboardIndex from "../components/Dashboard/DashboardIndex";
|
||||
import DashboardServices from "../components/Dashboard/DashboardServices";
|
||||
import DashboardUsers from "../components/Dashboard/DashboardUsers";
|
||||
import DashboardMessages from "../components/Dashboard/DashboardMessages";
|
||||
import Settings from "./Settings";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
components: {
|
||||
Header,
|
||||
Group,
|
||||
MessageBlock,
|
||||
ServiceBlock,
|
||||
Settings,
|
||||
DashboardMessages,
|
||||
DashboardUsers,
|
||||
DashboardServices,
|
||||
DashboardIndex,
|
||||
TopNav,
|
||||
Login,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
services: null,
|
||||
groups: null,
|
||||
core: null,
|
||||
token: null,
|
||||
view: "DashboardIndex",
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
created() {
|
||||
this.pathView(this.$route.path)
|
||||
this.token = Api.token()
|
||||
this.loadAll()
|
||||
},
|
||||
methods: {
|
||||
pathView (path) {
|
||||
switch (path) {
|
||||
case "/dashboard/settings":
|
||||
this.view = "Settings"
|
||||
break
|
||||
case "/dashboard/users":
|
||||
this.view = "DashboardUsers"
|
||||
break
|
||||
case "/dashboard/messages":
|
||||
this.view = "DashboardMessages"
|
||||
break
|
||||
case "/dashboard/services":
|
||||
this.view = "DashboardServices"
|
||||
break
|
||||
default:
|
||||
this.view = "DashboardIndex"
|
||||
}
|
||||
},
|
||||
changeView (v, name) {
|
||||
this.view = v
|
||||
this.$router.push('/'+name)
|
||||
},
|
||||
async loadAll () {
|
||||
this.token = await Api.token()
|
||||
this.core = await Api.root()
|
||||
this.groups = await Api.groups()
|
||||
this.services = await Api.services()
|
||||
|
|
|
@ -22,13 +22,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ServiceBlock from '../components/Service/ServiceBlock.vue'
|
||||
import MessageBlock from "../components/Index/MessageBlock";
|
||||
import Group from "../components/Index/Group";
|
||||
import Header from "../components/Index/Header";
|
||||
import Api from "../components/API"
|
||||
import ServiceBlock from '../components/Service/ServiceBlock.vue'
|
||||
import MessageBlock from "../components/Index/MessageBlock";
|
||||
import Group from "../components/Index/Group";
|
||||
import Header from "../components/Index/Header";
|
||||
import Api from "../components/API"
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
Header,
|
||||
|
@ -41,13 +41,19 @@ export default {
|
|||
services: null,
|
||||
groups: null,
|
||||
core: null,
|
||||
auth: null
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
created() {
|
||||
this.auth = Api.authToken()
|
||||
this.core = Api.root()
|
||||
},
|
||||
mounted() {
|
||||
this.loadAll()
|
||||
},
|
||||
methods: {
|
||||
async loadAll () {
|
||||
this.auth = Api.authToken()
|
||||
this.core = await Api.root()
|
||||
this.groups = await Api.groups()
|
||||
this.services = await Api.services()
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
<div class="col-10 offset-1 col-md-8 offset-md-2 mt-md-2">
|
||||
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
||||
<img class="col-12 mt-5 mt-md-0" :src="require(`@/assets/banner.png`)">
|
||||
</div>
|
||||
{{auth}}
|
||||
<form id="login_form" @submit="login" method="post">
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-2 col-form-label">Username</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" v-model="username" name="username" class="form-control" id="username" placeholder="Username" autocorrect="off" autocapitalize="none">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="password" class="col-sm-2 col-form-label">Password</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" v-model="password" name="password" class="form-control" id="password" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-primary btn-block mb-3">Sign in</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API"
|
||||
|
||||
export default {
|
||||
name: 'Login',
|
||||
components: {
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
username: "",
|
||||
password: "",
|
||||
auth: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async login (e) {
|
||||
e.preventDefault();
|
||||
const auth = await Api.login(this.username, this.password)
|
||||
if (auth.token !== null) {
|
||||
this.auth = Api.saveToken(this.username, auth.token)
|
||||
await this.$router.push('/dashboard')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,16 +1,443 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-2 sm-container">
|
||||
{{service}}
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
|
||||
<TopNav/>
|
||||
|
||||
<div class="col-12 mb-4">
|
||||
|
||||
<span class="mt-3 mb-3 text-white d-md-none btn bg-success d-block d-md-none">ONLINE</span>
|
||||
|
||||
|
||||
<h4 class="mt-2"><a href="">{{service.name}}</a> - {{service.name}}
|
||||
|
||||
<span class="badge bg-success float-right d-none d-md-block">ONLINE</span>
|
||||
</h4>
|
||||
|
||||
<div class="row stats_area mt-5 mb-5">
|
||||
<div class="col-4">
|
||||
<span class="lg_number">100%</span>
|
||||
Online last 24 Hours
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="lg_number">31ms</span>
|
||||
Average Response
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="lg_number">85.70%</span>
|
||||
Total Uptime
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="service-chart-container">
|
||||
<div id="service"></div>
|
||||
<div id="service-bar"></div>
|
||||
</div>
|
||||
|
||||
<div class="service-chart-heatmap">
|
||||
<div id="service_heatmap"></div>
|
||||
</div>
|
||||
|
||||
<form id="service_date_form" class="col-12 mt-2 mb-3">
|
||||
<input type="text" class="d-none" name="start" id="service_start" data-input>
|
||||
<span data-toggle title="toggle" id="start_date" class="text-muted small float-left pointer mt-2">Thu, 09 Jan 2020 to Thu, 16 Jan 2020</span>
|
||||
<button type="submit" class="btn btn-light btn-sm mt-2">Set Timeframe</button>
|
||||
<input type="text" class="d-none" name="end" id="service_end" data-input>
|
||||
|
||||
<div id="start_container"></div>
|
||||
<div id="end_container"></div>
|
||||
</form>
|
||||
|
||||
|
||||
<nav class="nav nav-pills flex-column flex-sm-row mt-3" id="service_tabs" role="serviceLists">
|
||||
<a class="flex-sm-fill text-sm-center nav-link active" id="edit-tab" data-toggle="tab" href="#edit" role="tab" aria-controls="edit" aria-selected="false">Edit Service</a>
|
||||
<a class="flex-sm-fill text-sm-center nav-link" id="failures-tab" data-toggle="tab" href="#failures" role="tab" aria-controls="failures" aria-selected="true">Failures</a>
|
||||
<a class="flex-sm-fill text-sm-center nav-link disabled" id="incidents-tab" data-toggle="tab" href="#incidents" role="tab" aria-controls="incidents" aria-selected="true">Incidents</a>
|
||||
<a class="flex-sm-fill text-sm-center nav-link" id="checkins-tab" data-toggle="tab" href="#checkins" role="tab" aria-controls="checkins" aria-selected="false">Checkins</a>
|
||||
<a class="flex-sm-fill text-sm-center nav-link" id="response-tab" data-toggle="tab" href="#response" role="tab" aria-controls="response" aria-selected="false">Response</a>
|
||||
</nav>
|
||||
<div class="tab-content" id="myTabContent">
|
||||
|
||||
<div class="tab-pane fade" id="failures" role="serviceLists" aria-labelledby="failures-tab">
|
||||
|
||||
<div class="list-group mt-3 mb-4">
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Incorrect HTTP Status Code</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Status Code 502 did not match 200</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Failed</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Error Get https://api.statping.com: dial tcp 162.248.92.36:443: connect: connection refused</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Failed</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Error Get https://api.statping.com: dial tcp 162.248.92.36:443: connect: connection refused</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Failed</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Error Get https://api.statping.com: dial tcp 162.248.92.36:443: connect: connection refused</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Failed</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Error Get https://api.statping.com: dial tcp 162.248.92.36:443: connect: connection refused</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Failed</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Error Get https://api.statping.com: dial tcp 162.248.92.36:443: connect: connection refused</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Reset</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Error Get https://api.statping.com: read tcp 172.27.0.8:46586->162.248.92.36:443: read: connection reset by peer</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Failed</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">Could not get IP address for domain https://api.statping.com, lookup api.statping.com on 127.0.0.11:53: read udp 127.0.0.1:50890->127.0.0.11:53: read: connection refused</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Failed</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">Could not get IP address for domain https://api.statping.com, lookup api.statping.com on 127.0.0.11:53: read udp 127.0.0.1:35222->127.0.0.11:53: read: connection refused</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Failed</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">Could not get IP address for domain https://api.statping.com, lookup api.statping.com on 127.0.0.11:53: read udp 127.0.0.1:49817->127.0.0.11:53: read: connection refused</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Failed</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">Could not get IP address for domain https://api.statping.com, lookup api.statping.com on 127.0.0.11:53: read udp 127.0.0.1:57247->127.0.0.11:53: read: connection refused</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Reset</h5>
|
||||
<small>4 weeks ago</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Error Get https://api.statping.com: read tcp 172.27.0.10:52594->162.248.92.36:443: read: connection reset by peer</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Timed Out</h5>
|
||||
<small>Last month</small>
|
||||
</div>
|
||||
<p class="mb-1">Could not get IP address for domain https://api.statping.com, lookup api.statping.com on 127.0.0.11:53: read udp 127.0.0.1:51238->127.0.0.11:53: i/o timeout</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Connection Timed Out</h5>
|
||||
<small>Last month</small>
|
||||
</div>
|
||||
<p class="mb-1">Could not get IP address for domain https://api.statping.com, lookup api.statping.com on 127.0.0.11:53: read udp 127.0.0.1:47323->127.0.0.11:53: i/o timeout</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Incorrect HTTP Status Code</h5>
|
||||
<small>Last month</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Status Code 503 did not match 200</p>
|
||||
</a>
|
||||
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Incorrect HTTP Status Code</h5>
|
||||
<small>Last month</small>
|
||||
</div>
|
||||
<p class="mb-1">HTTP Status Code 503 did not match 200</p>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="incidents" role="serviceLists" aria-labelledby="incidents-tab">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="checkins" role="serviceLists" aria-labelledby="checkins-tab">
|
||||
|
||||
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="ajax_form" action="api/checkin" data-redirect="/service/7" method="POST">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
||||
<input type="text" name="name" class="form-control" id="checkin_name" placeholder="New Checkin">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label for="checkin_interval" class="col-form-label">Interval (seconds)</label>
|
||||
<input type="number" name="interval" class="form-control" id="checkin_interval" placeholder="60">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label for="grace_period" class="col-form-label">Grace Period</label>
|
||||
<input type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label for="submit" class="col-form-label"></label>
|
||||
<input type="hidden" name="service_id" class="form-control" id="service_id" value="7">
|
||||
<button type="submit" id="submit" class="btn btn-success d-block" style="margin-top: 14px;">Save Checkin</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="response" role="serviceLists" aria-labelledby="response-tab">
|
||||
<div class="col-12 mt-4">
|
||||
<h3>Last Response</h3>
|
||||
<textarea rows="8" class="form-control" readonly>invalid route</textarea>
|
||||
<div class="form-group row mt-2">
|
||||
<label for="last_status_code" class="col-sm-3 col-form-label">HTTP Status Code</label>
|
||||
<div class="col-sm-2">
|
||||
<input type="text" id="last_status_code" class="form-control" value="200" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade show active" id="edit" role="serviceLists" aria-labelledby="edit-tab">
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<form class="ajax_form" action="api/services/7" data-redirect="services" method="POST">
|
||||
<h4 class="mb-5 text-muted">Basic Information</h4>
|
||||
<div class="form-group row">
|
||||
<label for="service_name" class="col-sm-4 col-form-label">Service Name</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="name" class="form-control" id="service_name" value="Statping API" placeholder="Name" required spellcheck="false" autocorrect="off">
|
||||
<small class="form-text text-muted">Give your service a name you can recognize</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_type" class="col-sm-4 col-form-label">Service Type</label>
|
||||
<div class="col-sm-8">
|
||||
<select name="type" class="form-control" id="service_type" value="http" readonly>
|
||||
<option value="http" selected>HTTP Service</option>
|
||||
<option value="tcp" >TCP Service</option>
|
||||
<option value="udp" >UDP Service</option>
|
||||
<option value="icmp" >ICMP Ping</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Use HTTP if you are checking a website or use TCP if you are checking a server</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_url" class="col-sm-4 col-form-label">Application Endpoint (URL)</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="domain" class="form-control" id="service_url" value="https://api.statping.com" placeholder="https://google.com" required autocapitalize="none" spellcheck="false">
|
||||
<small class="form-text text-muted">Statping will attempt to connect to this URL</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_type" class="col-sm-4 col-form-label">Group</label>
|
||||
<div class="col-sm-8">
|
||||
<select name="group_id" class="form-control" id="group_id">
|
||||
<option value="0" >None</option>
|
||||
|
||||
<option value="1" >JSON Test Servers</option>
|
||||
|
||||
<option value="2" >Google Servers</option>
|
||||
|
||||
<option value="3" selected>Statping Servers</option>
|
||||
|
||||
</select>
|
||||
<small class="form-text text-muted">Attach this service to a group</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-5 text-muted">Request Details</h4>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="service_check_type" class="col-sm-4 col-form-label">Service Check Type</label>
|
||||
<div class="col-sm-8">
|
||||
<select name="method" class="form-control" id="service_check_type" value="GET">
|
||||
<option value="GET" selected>GET</option>
|
||||
<option value="POST" >POST</option>
|
||||
<option value="DELETE" >DELETE</option>
|
||||
<option value="PATCH" >PATCH</option>
|
||||
<option value="PUT" >PUT</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">A GET request will simply request the endpoint, you can also send data with POST.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row d-none">
|
||||
<label for="post_data" class="col-sm-4 col-form-label">Optional Post Data (JSON)</label>
|
||||
<div class="col-sm-8">
|
||||
<textarea name="post_data" class="form-control" id="post_data" rows="3" autocapitalize="none" spellcheck="false" placeholder='{"data": { "method": "success", "id": 148923 } }'></textarea>
|
||||
<small class="form-text text-muted">Insert a JSON string to send data to the endpoint.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="headers" class="col-sm-4 col-form-label">HTTP Headers</label>
|
||||
<div class="col-sm-8">
|
||||
<input name="headers" class="form-control" id="headers" autocapitalize="none" spellcheck="false" placeholder='Authorization=1010101,Content-Type=application/json' value="">
|
||||
<small class="form-text text-muted">Comma delimited list of HTTP Headers (KEY=VALUE,KEY=VALUE)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_response" class="col-sm-4 col-form-label">Expected Response (Regex)</label>
|
||||
<div class="col-sm-8">
|
||||
<textarea name="expected" class="form-control" id="service_response" rows="3" autocapitalize="none" spellcheck="false" placeholder='(method)": "((\\"|[success])*)"'></textarea>
|
||||
<small class="form-text text-muted">You can use plain text or insert <a target="_blank" href="https://regex101.com/r/I5bbj9/1">Regex</a> to validate the response</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_response_code" class="col-sm-4 col-form-label">Expected Status Code</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="expected_status" class="form-control" value="200" placeholder="200" id="service_response_code">
|
||||
<small class="form-text text-muted">A status code of 200 is success, or view all the <a target="_blank" href="https://www.restapitutorial.com/httpstatuscodes.html">HTTP Status Codes</a></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row d-none">
|
||||
<label for="port" class="col-sm-4 col-form-label">TCP Port</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="port" class="form-control" value="" id="service_port" placeholder="8080">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-5 text-muted">Additional Options</h4>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="service_interval" class="col-sm-4 col-form-label">Check Interval (Seconds)</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="check_interval" class="form-control" value="60" min="1" id="service_interval" required>
|
||||
<small id="interval" class="form-text text-muted">10,000+ will be checked in Microseconds (1 millisecond = 1000 microseconds).</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_timeout" class="col-sm-4 col-form-label">Timeout in Seconds</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="timeout" class="form-control" value="15" placeholder="15" id="service_timeout" min="1">
|
||||
<small class="form-text text-muted">If the endpoint does not respond within this time it will be considered to be offline</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="post_data" class="col-sm-4 col-form-label">Permalink URL</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="permalink" class="form-control" value="" id="permalink" autocapitalize="none" spellcheck="true" placeholder='awesome_service'>
|
||||
<small class="form-text text-muted">Use text for the service URL rather than the service number.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row d-none">
|
||||
<label for="order" class="col-sm-4 col-form-label">List Order</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="order" class="form-control" min="0" value="0" id="order">
|
||||
<small class="form-text text-muted">You can also drag and drop services to reorder on the Services tab.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="order" class="col-sm-4 col-form-label">Verify SSL</label>
|
||||
<div class="col-8 mt-1">
|
||||
<span class="switch float-left">
|
||||
<input type="checkbox" name="verify_ssl-option" class="switch" id="switch-verify-ssl" >
|
||||
<label for="switch-verify-ssl">Verify SSL Certificate for this service</label>
|
||||
<input type="hidden" name="verify_ssl" id="switch-verify-ssl-value" value="false">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="order" class="col-sm-4 col-form-label">Notifications</label>
|
||||
<div class="col-8 mt-1">
|
||||
<span class="switch float-left">
|
||||
<input type="checkbox" name="allow_notifications-option" class="switch" id="switch-notifications" checked>
|
||||
<label for="switch-notifications">Allow notifications to be sent for this service</label>
|
||||
<input type="hidden" name="allow_notifications" id="switch-notifications-value" value="true">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="order" class="col-sm-4 col-form-label">Visible</label>
|
||||
<div class="col-8 mt-1">
|
||||
<span class="switch float-left">
|
||||
<input type="checkbox" name="public-option" class="switch" id="switch-public" checked>
|
||||
<label for="switch-public">Show service details to the public</label>
|
||||
<input type="hidden" name="public" id="switch-public-value" value="true">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-6">
|
||||
<button type="submit" class="btn btn-success btn-block">Update Service</button>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<a href="service/7/delete_failures" data-method="GET" data-redirect="/service/7" class="btn btn-danger btn-block confirm-btn">Delete All Failures</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API"
|
||||
import Api from "../components/API"
|
||||
import TopNav from "../components/Dashboard/TopNav";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'Service',
|
||||
components: {
|
||||
|
||||
TopNav
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
@ -18,13 +445,19 @@ export default {
|
|||
service: null,
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
mounted() {
|
||||
this.service = this.$route.params.service
|
||||
this.id = this.$route.params.id
|
||||
this.getService()
|
||||
if (!this.service) {
|
||||
this.getService(this.id)
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async getService() {
|
||||
this.service = await Api.services()
|
||||
async getService(id) {
|
||||
this.service = await Api.service(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-2 sm-container">
|
||||
<div v-show="core" class="container col-md-7 col-sm-12 mt-2 sm-container">
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API"
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'Services',
|
||||
components: {
|
||||
|
||||
|
@ -14,13 +15,21 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
services: null,
|
||||
groups: null,
|
||||
core: null,
|
||||
auth: null
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
||||
this.loadAll()
|
||||
},
|
||||
methods: {
|
||||
|
||||
async loadAll () {
|
||||
this.auth = Api.authToken()
|
||||
this.core = await Api.root()
|
||||
this.groups = await Api.groups()
|
||||
this.services = await Api.services()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6341,7 +6341,7 @@ querystring-es3@^0.2.0:
|
|||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||
integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
|
||||
|
||||
querystring@0.2.0:
|
||||
querystring@0.2.0, querystring@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
||||
|
|
1
go.mod
1
go.mod
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/agnivade/levenshtein v1.0.2 // indirect
|
||||
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d
|
||||
github.com/daaku/go.zipexe v1.0.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v1.13.1
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -36,6 +36,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
|
||||
|
|
|
@ -18,12 +18,14 @@ package handlers
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/core/notifier"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func dashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -36,19 +38,12 @@ func dashboardHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if sessionStore == nil {
|
||||
resetCookies()
|
||||
}
|
||||
session, _ := sessionStore.Get(r, cookieKey)
|
||||
form := parseForm(r)
|
||||
username := form.Get("username")
|
||||
password := form.Get("password")
|
||||
user, auth := core.AuthUser(username, password)
|
||||
if auth {
|
||||
session.Values["authenticated"] = true
|
||||
session.Values["user_id"] = user.Id
|
||||
session.Values["admin"] = user.Admin.Bool
|
||||
session.Save(r, w)
|
||||
setJwtToken(user, w)
|
||||
utils.Log.Infoln(fmt.Sprintf("User %v logged in from IP %v", user.Username, r.RemoteAddr))
|
||||
http.Redirect(w, r, basePath+"dashboard", http.StatusSeeOther)
|
||||
} else {
|
||||
|
@ -58,11 +53,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func logoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := sessionStore.Get(r, cookieKey)
|
||||
session.Values["authenticated"] = false
|
||||
session.Values["admin"] = false
|
||||
session.Values["user_id"] = 0
|
||||
session.Save(r, w)
|
||||
removeJwtToken(w)
|
||||
http.Redirect(w, r, basePath, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
|
@ -117,3 +108,63 @@ func exportHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.ServeContent(w, r, "export.json", utils.Now(), bytes.NewReader(export))
|
||||
|
||||
}
|
||||
|
||||
type JwtClaim struct {
|
||||
Username string `json:"username"`
|
||||
Admin bool `json:"admin"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
func removeJwtToken(w http.ResponseWriter) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieKey,
|
||||
Value: "",
|
||||
Expires: time.Now().UTC(),
|
||||
})
|
||||
}
|
||||
|
||||
func setJwtToken(user *core.User, w http.ResponseWriter) (*JwtClaim, string) {
|
||||
expirationTime := time.Now().Add(24 * time.Hour)
|
||||
jwtClaim := &JwtClaim{
|
||||
user.Username,
|
||||
user.Admin.Bool,
|
||||
jwt.StandardClaims{
|
||||
ExpiresAt: expirationTime.Unix(),
|
||||
}}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwtClaim)
|
||||
tokenString, err := token.SignedString([]byte(jwtKey))
|
||||
if err != nil {
|
||||
utils.Log.Errorln("error setting token: ", err)
|
||||
}
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieKey,
|
||||
Value: tokenString,
|
||||
Expires: expirationTime,
|
||||
})
|
||||
return jwtClaim, tokenString
|
||||
}
|
||||
|
||||
func apiLoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
form := parseForm(r)
|
||||
username := form.Get("username")
|
||||
password := form.Get("password")
|
||||
user, auth := core.AuthUser(username, password)
|
||||
if auth {
|
||||
utils.Log.Infoln(fmt.Sprintf("User %v logged in from IP %v", user.Username, r.RemoteAddr))
|
||||
_, token := setJwtToken(user, w)
|
||||
|
||||
resp := struct {
|
||||
Token string `json:"token"`
|
||||
}{
|
||||
token,
|
||||
}
|
||||
returnJson(resp, w, r)
|
||||
} else {
|
||||
resp := struct {
|
||||
Error string `json:"error"`
|
||||
}{
|
||||
"incorrect authentication",
|
||||
}
|
||||
returnJson(resp, w, r)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -28,7 +29,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/types"
|
||||
|
@ -41,12 +41,12 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
sessionStore *sessions.CookieStore
|
||||
httpServer *http.Server
|
||||
usingSSL bool
|
||||
mainTmpl = `{{define "main" }} {{ template "base" . }} {{ end }}`
|
||||
templates = []string{"base.gohtml", "head.gohtml", "nav.gohtml", "footer.gohtml", "scripts.gohtml", "form_service.gohtml", "form_notifier.gohtml", "form_integration.gohtml", "form_group.gohtml", "form_user.gohtml", "form_checkin.gohtml", "form_message.gohtml"}
|
||||
javascripts = []string{"charts.js", "chart_index.js"}
|
||||
jwtKey string
|
||||
httpServer *http.Server
|
||||
usingSSL bool
|
||||
mainTmpl = `{{define "main" }} {{ template "base" . }} {{ end }}`
|
||||
templates = []string{"base.gohtml", "head.gohtml", "nav.gohtml", "footer.gohtml", "scripts.gohtml", "form_service.gohtml", "form_notifier.gohtml", "form_integration.gohtml", "form_group.gohtml", "form_user.gohtml", "form_checkin.gohtml", "form_message.gohtml"}
|
||||
javascripts = []string{"charts.js", "chart_index.js"}
|
||||
)
|
||||
|
||||
// RunHTTPServer will start a HTTP server on a specific IP and port
|
||||
|
@ -137,9 +137,6 @@ func IsFullAuthenticated(r *http.Request) bool {
|
|||
if core.SetupMode {
|
||||
return false
|
||||
}
|
||||
if sessionStore == nil {
|
||||
return true
|
||||
}
|
||||
var token string
|
||||
tokens, ok := r.Header["Authorization"]
|
||||
if ok && len(tokens) >= 1 {
|
||||
|
@ -152,19 +149,44 @@ func IsFullAuthenticated(r *http.Request) bool {
|
|||
return IsAdmin(r)
|
||||
}
|
||||
|
||||
func getJwtAuth(r *http.Request) (bool, string) {
|
||||
c, err := r.Cookie(cookieKey)
|
||||
if err != nil {
|
||||
utils.Log.Errorln(err)
|
||||
if err == http.ErrNoCookie {
|
||||
return false, ""
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
tknStr := c.Value
|
||||
var claims JwtClaim
|
||||
tkn, err := jwt.ParseWithClaims(tknStr, &claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(jwtKey), nil
|
||||
})
|
||||
if err != nil {
|
||||
utils.Log.Errorln("error getting jwt token: ", err)
|
||||
if err == jwt.ErrSignatureInvalid {
|
||||
return false, ""
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
if !tkn.Valid {
|
||||
utils.Log.Errorln("token is not valid")
|
||||
return false, ""
|
||||
}
|
||||
return claims.Admin, claims.Username
|
||||
}
|
||||
|
||||
// IsAdmin returns true if the user session is an administrator
|
||||
func IsAdmin(r *http.Request) bool {
|
||||
if core.SetupMode {
|
||||
return false
|
||||
}
|
||||
session, err := sessionStore.Get(r, cookieKey)
|
||||
if err != nil {
|
||||
admin, username := getJwtAuth(r)
|
||||
if username == "" {
|
||||
return false
|
||||
}
|
||||
if session.Values["admin"] == nil {
|
||||
return false
|
||||
}
|
||||
return session.Values["admin"].(bool)
|
||||
return admin
|
||||
}
|
||||
|
||||
// IsUser returns true if the user is registered
|
||||
|
@ -175,14 +197,9 @@ func IsUser(r *http.Request) bool {
|
|||
if os.Getenv("GO_ENV") == "test" {
|
||||
return true
|
||||
}
|
||||
session, err := sessionStore.Get(r, cookieKey)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if session.Values["authenticated"] == nil {
|
||||
return false
|
||||
}
|
||||
return session.Values["authenticated"].(bool)
|
||||
ff, username := getJwtAuth(r)
|
||||
fmt.Println(ff, username)
|
||||
return username != ""
|
||||
}
|
||||
|
||||
func loadTemplate(w http.ResponseWriter, r *http.Request) (*template.Template, error) {
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"fmt"
|
||||
"github.com/99designs/gqlgen/handler"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/handlers/graphql"
|
||||
"github.com/hunterlong/statping/source"
|
||||
|
@ -115,6 +114,8 @@ func Router() *mux.Router {
|
|||
|
||||
// API Routes
|
||||
r.Handle("/api", scopedRoute(apiIndexHandler))
|
||||
r.Handle("/api/login", http.HandlerFunc(apiLoginHandler)).Methods("POST")
|
||||
r.Handle("/api/logout", http.HandlerFunc(logoutHandler))
|
||||
r.Handle("/api/renew", authenticated(apiRenewHandler, false))
|
||||
r.Handle("/api/clear_cache", authenticated(apiClearCacheHandler, false))
|
||||
|
||||
|
@ -195,10 +196,5 @@ func resetRouter() {
|
|||
}
|
||||
|
||||
func resetCookies() {
|
||||
if core.CoreApp != nil {
|
||||
cookie := fmt.Sprintf("%v_%v", core.CoreApp.ApiSecret, utils.Now().Nanosecond())
|
||||
sessionStore = sessions.NewCookieStore([]byte(cookie))
|
||||
} else {
|
||||
sessionStore = sessions.NewCookieStore([]byte("secretinfo"))
|
||||
}
|
||||
jwtKey = fmt.Sprintf("%v_%v", core.CoreApp.ApiSecret, utils.Now().Nanosecond())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue