mirror of https://github.com/statping/statping
custom notifier JSON/text, codemirror custom designs for variables, JWT updates
parent
722ce47977
commit
01fea69edc
|
@ -28,6 +28,7 @@
|
||||||
"codemirror-colorpicker": "^1.9.66",
|
"codemirror-colorpicker": "^1.9.66",
|
||||||
"core-js": "^3.4.4",
|
"core-js": "^3.4.4",
|
||||||
"date-fns": "^2.9.0",
|
"date-fns": "^2.9.0",
|
||||||
|
"js-beautify": "^1.11.0",
|
||||||
"querystring": "^0.2.0",
|
"querystring": "^0.2.0",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-apexcharts": "^1.5.2",
|
"vue-apexcharts": "^1.5.2",
|
||||||
|
|
|
@ -395,6 +395,30 @@ HTML,BODY {
|
||||||
color: #a0a0a0;
|
color: #a0a0a0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.service_block {
|
||||||
|
min-height: 340px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-field {
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-bracketer {
|
||||||
|
color: #a011c9;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-var-highlight {
|
||||||
|
color: #0fa50f;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-var-sub-highlight {
|
||||||
|
color: #b40727;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background-color: $service-background;
|
background-color: $service-background;
|
||||||
border: $service-border;
|
border: $service-border;
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import CodeMirror from 'codemirror'
|
||||||
|
|
||||||
|
CodeMirror.defineMode('mymode', () => {
|
||||||
|
return {
|
||||||
|
token(stream, state) {
|
||||||
|
if (stream.match(".Service") || (stream.match(".Core")) || (stream.match(".Failure"))) {
|
||||||
|
return "var-highlight"
|
||||||
|
} else if (stream.match(".Id") || stream.match(".Domain") || stream.match(".CreatedAt") ||
|
||||||
|
stream.match(".Name") || stream.match(".DowntimeAgo") || stream.match(".Issue") || stream.match(".LastStatusCode") ||
|
||||||
|
stream.match(".Port") || stream.match(".FailuresLast24Hours") || stream.match(".PingTime")) {
|
||||||
|
return "var-sub-highlight"
|
||||||
|
} else if (stream.match("{{") || stream.match("}}")) {
|
||||||
|
return "bracketer"
|
||||||
|
} else {
|
||||||
|
stream.next()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -4,15 +4,15 @@
|
||||||
<div class="row stats_area mb-5">
|
<div class="row stats_area mb-5">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="font-6 font-weight-bold d-block">{{$store.getters.services.length}}</span>
|
<span class="font-6 font-weight-bold d-block">{{$store.getters.services.length}}</span>
|
||||||
<span class="font-2">Total Services</span>
|
<span class="font-2">{{ $t('dashboard.total_services') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="font-6 font-weight-bold d-block">{{failuresLast24Hours()}}</span>
|
<span class="font-6 font-weight-bold d-block">{{failuresLast24Hours()}}</span>
|
||||||
<span class="font-2">Failures last 24 Hours</span>
|
<span class="font-2">{{ $t('dashboard.failures_24_hours') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="font-6 font-weight-bold d-block">{{$store.getters.onlineServices(true).length}}</span>
|
<span class="font-6 font-weight-bold d-block">{{$store.getters.onlineServices(true).length}}</span>
|
||||||
<span class="font-2">Online Services</span>
|
<span class="font-2">{{ $t('dashboard.online_services') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -121,20 +121,18 @@
|
||||||
},
|
},
|
||||||
changeTab (v) {
|
changeTab (v) {
|
||||||
this.tab = v
|
this.tab = v
|
||||||
if (v === 'base') {
|
// if (v === 'base') {
|
||||||
this.$refs.base.codemirror.refresh();
|
// this.$refs.base.codemirror.refresh();
|
||||||
} else if (v === 'vars') {
|
// } else if (v === 'vars') {
|
||||||
this.$refs.vars.codemirror.refresh();
|
// this.$refs.vars.codemirror.refresh();
|
||||||
} else if (v === 'mobile') {
|
// } else if (v === 'mobile') {
|
||||||
this.$refs.mobile.codemirror.refresh();
|
// this.$refs.mobile.codemirror.refresh();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style scoped>
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style>
|
|
||||||
.CodeMirror {
|
.CodeMirror {
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
height: 550px;
|
height: 550px;
|
||||||
|
|
|
@ -9,22 +9,22 @@
|
||||||
<div class="navbar-collapse" :class="{collapse: !navopen}" id="navbarText">
|
<div class="navbar-collapse" :class="{collapse: !navopen}" id="navbarText">
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav mr-auto">
|
||||||
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
||||||
<router-link to="/dashboard" class="nav-link">Dashboard</router-link>
|
<router-link to="/dashboard" class="nav-link">{{ $t('top_nav.dashboard') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
||||||
<router-link to="/dashboard/services" class="nav-link">Services</router-link>
|
<router-link to="/dashboard/services" class="nav-link">{{ $t('top_nav.services') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||||
<router-link to="/dashboard/users" class="nav-link">Users</router-link>
|
<router-link to="/dashboard/users" class="nav-link">{{ $t('top_nav.users') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
||||||
<router-link to="/dashboard/messages" class="nav-link">Announcements</router-link>
|
<router-link to="/dashboard/messages" class="nav-link">{{ $t('top_nav.announcements') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||||
<router-link to="/dashboard/settings" class="nav-link">Settings</router-link>
|
<router-link to="/dashboard/settings" class="nav-link">{{ $t('top_nav.settings') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||||
<router-link to="/dashboard/logs" class="nav-link">Logs</router-link>
|
<router-link to="/dashboard/logs" class="nav-link">{{ $t('top_nav.logs') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<span class="navbar-text">
|
<span class="navbar-text">
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="saveSettings">
|
<form @submit.prevent="saveSettings">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Project Name</label>
|
<label>{{ $t('settings.name') }}</label>
|
||||||
<input v-model="core.name" type="text" class="form-control" placeholder="Great Uptime" id="project">
|
<input v-model="core.name" type="text" class="form-control" placeholder="Great Uptime" id="project">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Project Description</label>
|
<label>{{ $t('settings.description') }}</label>
|
||||||
<input v-model="core.description" type="text" class="form-control" placeholder="Great Uptime" id="description">
|
<input v-model="core.description" type="text" class="form-control" placeholder="Great Uptime" id="description">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-8 col-sm-9">
|
<div class="col-8 col-sm-9">
|
||||||
<label>Domain</label>
|
<label>{{ $t('domain') }}</label>
|
||||||
<input v-model="core.domain" type="url" class="form-control" id="domain">
|
<input v-model="core.domain" type="url" class="form-control" id="domain">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4 col-sm-3 mt-sm-1 mt-0">
|
<div class="col-4 col-sm-3 mt-sm-1 mt-0">
|
||||||
|
@ -26,13 +26,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Custom Footer</label>
|
<label>{{ $t('settings.footer') }}</label>
|
||||||
<textarea v-model="core.footer" rows="4" class="form-control" id="footer">{{core.footer}}</textarea>
|
<textarea v-model="core.footer" rows="4" class="form-control" id="footer">{{core.footer}}</textarea>
|
||||||
<small class="form-text text-muted">HTML is allowed inside the footer</small>
|
<small class="form-text text-muted">{{ $t('settings.footer_notes') }}</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Language</label>
|
<label>{{ $t('setup.language') }}</label>
|
||||||
<select v-model="core.language" class="form-control">
|
<select v-model="core.language" class="form-control">
|
||||||
<option value="en">English</option>
|
<option value="en">English</option>
|
||||||
<option value="es">Spanish</option>
|
<option value="es">Spanish</option>
|
||||||
|
@ -40,11 +40,10 @@
|
||||||
<option value="ru">Russian</option>
|
<option value="ru">Russian</option>
|
||||||
<option value="de">German</option>
|
<option value="de">German</option>
|
||||||
</select>
|
</select>
|
||||||
<small class="form-text text-muted">HTML is allowed inside the footer</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group row mt-3">
|
<div class="form-group row mt-3">
|
||||||
<label class="col-sm-10 col-form-label">Enable Error Reporting</label>
|
<label class="col-sm-10 col-form-label">{{ $t('settings.error_reporting') }}</label>
|
||||||
<div class="col-sm-2 float-right">
|
<div class="col-sm-2 float-right">
|
||||||
<span @click="core.allow_reports = !!core.allow_reports" class="switch" id="allow_report">
|
<span @click="core.allow_reports = !!core.allow_reports" class="switch" id="allow_report">
|
||||||
<input v-model="core.allow_reports" type="checkbox" name="allow_report" class="switch" id="switch_allow_report" :checked="core.allow_reports">
|
<input v-model="core.allow_reports" type="checkbox" name="allow_report" class="switch" id="switch_allow_report" :checked="core.allow_reports">
|
||||||
|
@ -52,12 +51,12 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<small>Help the Statping project out by sending anonymous error logs back to our server.</small>
|
<small>{{ $t('settings.error_reporting_notes') }}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button @click.prevent="saveSettings" id="save_core" type="submit" class="btn btn-primary btn-block mt-3" v-bind:disabled="loading">
|
<button @click.prevent="saveSettings" id="save_core" type="submit" class="btn btn-primary btn-block mt-3" v-bind:disabled="loading">
|
||||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>Save Settings
|
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{ $t('settings.save') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<form @submit.prevent="saveNotifier">
|
||||||
<div class="card contain-card text-black-50 bg-white mb-3">
|
<div class="card contain-card text-black-50 bg-white mb-3">
|
||||||
<div class="card-header text-capitalize">
|
<div class="card-header text-capitalize">
|
||||||
{{notifier.title}}
|
{{notifier.title}}
|
||||||
|
@ -10,8 +11,6 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<form @submit.prevent="saveNotifier">
|
|
||||||
|
|
||||||
<p class="small text-muted" v-html="notifier.description"/>
|
<p class="small text-muted" v-html="notifier.description"/>
|
||||||
|
|
||||||
<div v-for="(form, index) in notifier.form" v-bind:key="index" class="form-group">
|
<div v-for="(form, index) in notifier.form" v-bind:key="index" class="form-group">
|
||||||
|
@ -30,10 +29,44 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="notifier.data_type" class="card text-black-50 bg-white mb-3">
|
||||||
|
<div class="card-header text-capitalize">
|
||||||
|
{{notifier.title}} Outgoing Request
|
||||||
|
<span class="badge badge-dark float-right text-uppercase mt-1">{{notifier.data_type}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<span class="text-muted d-block mb-3" v-if="notifier.request_info" v-html="notifier.request_info"></span>
|
||||||
|
|
||||||
|
<div class="row" v-observe-visibility="visible">
|
||||||
|
<div class="col-12">
|
||||||
|
<h5 class="text-capitalize">Success Data</h5>
|
||||||
|
<codemirror v-model="success_data"
|
||||||
|
ref="cmsuccess"
|
||||||
|
:options="cmOptions"
|
||||||
|
@ready="onCmSuccessReady"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<h5 class="text-capitalize">Failure Data</h5>
|
||||||
|
<codemirror v-model="failure_data"
|
||||||
|
ref="cmfailure"
|
||||||
|
:options="cmOptions"
|
||||||
|
@ready="onCmFailureReady"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
</div>
|
<div v-if="error && !success" class="card text-black-50 bg-white mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
<div v-if="error && !success" class="alert alert-danger col-12" role="alert">
|
<div v-if="error && !success" class="alert alert-danger col-12" role="alert">
|
||||||
{{error}}<p v-if="response">Response:<br>{{response}}</p>
|
{{error}}<p v-if="response">Response:<br>{{response}}</p>
|
||||||
|
@ -43,18 +76,25 @@
|
||||||
<p v-if="response">Response:<br>{{response}}</p>
|
<p v-if="response">Response:<br>{{response}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card text-black-50 bg-white mb-3">
|
<div class="card text-black-50 bg-white mb-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6 col-sm-6 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
<div class="col-4 col-sm-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||||
<button @click.prevent="saveNotifier" type="submit" class="btn btn-block text-capitalize btn-primary save-notifier">
|
<button @click.prevent="saveNotifier" type="submit" class="btn btn-block text-capitalize btn-primary save-notifier">
|
||||||
<i class="fa fa-check-circle"></i> {{loading ? "Loading..." : saved ? "Saved" : "Save Settings"}}
|
<i class="fa fa-check-circle"></i> {{loading ? "Loading..." : saved ? "Saved" : "Save Settings"}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-6 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
<div class="col-4 col-md-4">
|
||||||
<button @click.prevent="testNotifier" class="btn btn-outline-dark btn-block text-capitalize test-notifier"><i class="fa fa-vial"></i>
|
<button @click.prevent="testNotifier" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||||
{{loadingTest ? "Loading..." : "Test Notifier"}}</button>
|
<i class="fa fa-vial"></i>{{loadingTest ? "Loading..." : "Test Success"}}</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 col-md-4">
|
||||||
|
<button @click.prevent="testNotifier" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||||
|
<i class="fa fa-vial"></i>{{loadingTest ? "Loading..." : "Test Failure"}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -69,15 +109,30 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../API";
|
import Api from "../API";
|
||||||
|
|
||||||
|
const beautify = require('js-beautify').js
|
||||||
|
|
||||||
|
// require component
|
||||||
|
import { codemirror } from 'vue-codemirror'
|
||||||
|
import 'codemirror/mode/javascript/javascript.js'
|
||||||
|
import 'codemirror/lib/codemirror.css'
|
||||||
|
import 'codemirror/theme/neat.css'
|
||||||
|
import '../codemirror_json'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Notifier',
|
name: 'Notifier',
|
||||||
|
components: {
|
||||||
|
codemirror
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
notifier: {
|
notifier: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -87,10 +142,55 @@ export default {
|
||||||
response: null,
|
response: null,
|
||||||
success: false,
|
success: false,
|
||||||
saved: false,
|
saved: false,
|
||||||
|
success_data: null,
|
||||||
|
failure_data: null,
|
||||||
form: {},
|
form: {},
|
||||||
|
cmOptions: {
|
||||||
|
height: 700,
|
||||||
|
tabSize: 2,
|
||||||
|
lineNumbers: true,
|
||||||
|
line: true,
|
||||||
|
class: "json-field",
|
||||||
|
theme: 'neat',
|
||||||
|
mode: "mymode",
|
||||||
|
lineWrapping: true,
|
||||||
|
json: true,
|
||||||
|
autoRefresh: true,
|
||||||
|
mime: this.notifier.data_type === "json" ? "application/json" : "text/plain"
|
||||||
|
},
|
||||||
|
beautifySettings: { indent_size: 2, space_in_empty_paren: true },
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
visible(isVisible, entry) {
|
||||||
|
if (isVisible) {
|
||||||
|
this.$refs.cmfailure.codemirror.refresh()
|
||||||
|
this.$refs.cmsuccess.codemirror.refresh()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCmSuccessReady(cm) {
|
||||||
|
this.success_data = beautify(this.notifier.success_data, this.beautifySettings)
|
||||||
|
console.log('the editor is ready!', cm)
|
||||||
|
setTimeout(function() {
|
||||||
|
cm.refresh();
|
||||||
|
},1);
|
||||||
|
},
|
||||||
|
onCmFailureReady(cm) {
|
||||||
|
this.failure_data = beautify(this.notifier.failure_data, this.beautifySettings)
|
||||||
|
setTimeout(function() {
|
||||||
|
cm.refresh();
|
||||||
|
},1);
|
||||||
|
},
|
||||||
|
onCmFocus(cm) {
|
||||||
|
console.log('the editor is focused!', cm)
|
||||||
|
},
|
||||||
|
onCmCodeChange(newCode) {
|
||||||
|
console.log('this is new code', newCode)
|
||||||
|
this.success_data = newCode
|
||||||
|
},
|
||||||
async enableToggle() {
|
async enableToggle() {
|
||||||
this.notifier.enabled = !!this.notifier.enabled
|
this.notifier.enabled = !!this.notifier.enabled
|
||||||
const form = {
|
const form = {
|
||||||
|
@ -112,6 +212,9 @@ export default {
|
||||||
}
|
}
|
||||||
this.form[field] = val
|
this.form[field] = val
|
||||||
});
|
});
|
||||||
|
this.form.success_data = this.success_data
|
||||||
|
this.form.failure_data = this.failure_data
|
||||||
|
window.console.log(this.form)
|
||||||
await Api.notifier_save(this.form)
|
await Api.notifier_save(this.form)
|
||||||
const notifiers = await Api.notifiers()
|
const notifiers = await Api.notifiers()
|
||||||
await this.$store.commit('setNotifiers', notifiers)
|
await this.$store.commit('setNotifiers', notifiers)
|
||||||
|
@ -142,7 +245,10 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.CodeMirror {
|
||||||
|
border: 1px solid #eee;
|
||||||
|
height: 550px;
|
||||||
|
font-size: 9pt;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<form v-if="service.type" @submit.prevent="saveService">
|
<form v-if="service.type" @submit.prevent="saveService">
|
||||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||||
<div class="card-header">Service Information</div>
|
<div class="card-header">{{ $t('service.info') }}</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-4 col-form-label">Service Name</label>
|
<label class="col-sm-4 col-form-label">{{ $t('service.name') }}</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<input v-model="service.name" @input="updatePermalink" id="name" type="text" name="name" class="form-control" placeholder="Server Name" required spellcheck="false" autocorrect="off">
|
<input v-model="service.name" @input="updatePermalink" id="name" type="text" name="name" class="form-control" placeholder="Server Name" required spellcheck="false" autocorrect="off">
|
||||||
<small class="form-text text-muted">Give your service a name you can recognize</small>
|
<small class="form-text text-muted">Give your service a name you can recognize</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="service_type" class="col-sm-4 col-form-label">Service Type</label>
|
<label for="service_type" class="col-sm-4 col-form-label">{{ $t('service.type') }}</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<select v-model="service.type" class="form-control" id="service_type">
|
<select v-model="service.type" class="form-control" id="service_type">
|
||||||
<option value="http">HTTP Service</option>
|
<option value="http">HTTP Service</option>
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="service_type" class="col-sm-4 col-form-label">Group</label>
|
<label for="service_type" class="col-sm-4 col-form-label">{{ $t('group') }}</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<select v-model="service.group_id" class="form-control">
|
<select v-model="service.group_id" class="form-control">
|
||||||
<option value="0" >No Group</option>
|
<option value="0" >No Group</option>
|
||||||
|
|
|
@ -26,13 +26,19 @@
|
||||||
<option value="mysql">MySQL</option>
|
<option value="mysql">MySQL</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||||
<label class="text-capitalize">{{ $t('setup.host') }}</label>
|
<label class="text-capitalize">{{ $t('setup.host') }}</label>
|
||||||
<input @keyup="canSubmit" v-model="setup.db_host" id="db_host" type="text" class="form-control" placeholder="localhost">
|
<input @keyup="canSubmit" v-model="setup.db_host" id="db_host" type="text" class="form-control" placeholder="localhost">
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||||
<label class="text-capitalize">{{ $t('port') }}</label>
|
<label class="text-capitalize">{{ $t('port') }}</label>
|
||||||
<input @keyup="canSubmit" v-model="setup.db_port" id="db_port" type="text" class="form-control" placeholder="localhost">
|
<input @keyup="canSubmit" v-model="setup.db_port" id="db_port" type="number" class="form-control" placeholder="5432">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||||
<label class="text-capitalize">{{ $t('username') }}</label>
|
<label class="text-capitalize">{{ $t('username') }}</label>
|
||||||
|
@ -47,6 +53,21 @@
|
||||||
<input @keyup="canSubmit" v-model="setup.db_database" id="db_database" type="text" class="form-control" placeholder="Database name">
|
<input @keyup="canSubmit" v-model="setup.db_database" id="db_database" type="text" class="form-control" placeholder="Database name">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group mt-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-9">
|
||||||
|
<span class="text-left text-capitalize">{{ $t('setup.send_reports') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-3 text-right">
|
||||||
|
<span @click="setup.send_reports = !!setup.send_reports" class="switch">
|
||||||
|
<input v-model="setup.send_reports" type="checkbox" name="send_reports" class="switch" id="send_reports" :checked="setup.send_reports">
|
||||||
|
<label for="send_reports"></label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
|
@ -72,7 +93,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="text-capitalize">{{ $t('setup.username') }}</label>
|
<label class="text-capitalize">{{ $t('setup.password') }}</label>
|
||||||
<input @keyup="canSubmit" v-model="setup.password" id="password" type="password" class="form-control" placeholder="password" required>
|
<input @keyup="canSubmit" v-model="setup.password" id="password" type="password" class="form-control" placeholder="password" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -90,7 +111,7 @@
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="d-none d-sm-block text-capitalize text-capitalize">{{ $t('setup.newsletter') }}</label>
|
<label class="d-none d-sm-block text-capitalize text-capitalize">{{ $t('setup.newsletter') }}</label>
|
||||||
<span @click="setup.newsletter = !!setup.newsletter" class="switch">
|
<span @click="setup.newsletter = !!setup.newsletter" class="switch">
|
||||||
<input v-model="setup.newsletter" type="checkbox" name="using_cdn" class="switch" id="send_newsletter" :checked="setup.newsletter">
|
<input v-model="setup.newsletter" type="checkbox" name="send_newsletter" class="switch" id="send_newsletter" :checked="setup.newsletter">
|
||||||
<label for="send_newsletter"></label>
|
<label for="send_newsletter"></label>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -140,6 +161,7 @@
|
||||||
confirm_password: "",
|
confirm_password: "",
|
||||||
sample_data: true,
|
sample_data: true,
|
||||||
newsletter: true,
|
newsletter: true,
|
||||||
|
send_reports: true,
|
||||||
email: "",
|
email: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
const english = {
|
const english = {
|
||||||
|
top_nav: {
|
||||||
|
dashboard: "Dashboard",
|
||||||
|
services: "Services",
|
||||||
|
users: "Users",
|
||||||
|
announcements: "Announcements",
|
||||||
|
settings: "Settings",
|
||||||
|
logs: "Logs",
|
||||||
|
},
|
||||||
setup: {
|
setup: {
|
||||||
language: "Language",
|
language: "Language",
|
||||||
connection: "Database Connection",
|
connection: "Database Connection",
|
||||||
|
@ -9,19 +17,41 @@ const english = {
|
||||||
domain: "Domain URL",
|
domain: "Domain URL",
|
||||||
username: "Admin Username",
|
username: "Admin Username",
|
||||||
password: "Admin Password",
|
password: "Admin Password",
|
||||||
password_confirm: "Confirm Admin Username",
|
password_confirm: "Confirm Admin Password",
|
||||||
newsletter: "Newsletter",
|
newsletter: "Newsletter",
|
||||||
newsletter_note: "We will not share your email, emails are only for major updates.",
|
newsletter_note: "We will not share your email, emails are only for major updates.",
|
||||||
|
send_reports: "Send Error Reports to Statping"
|
||||||
|
},
|
||||||
|
dashboard: {
|
||||||
|
total_services: "Total Services",
|
||||||
|
failures_24_hours: "Failures last 24 Hours",
|
||||||
|
online_services: "Online Services",
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
name: "Project Name",
|
||||||
|
description: "Project Name",
|
||||||
|
footer: "Custom Footer",
|
||||||
|
footer_notes: "HTML is allowed inside the footer",
|
||||||
|
error_reporting: "Enable Error Reporting",
|
||||||
|
error_reporting_notes: "Help the Statping project out by sending anonymous error logs back to our server.",
|
||||||
|
save: "Save Settings"
|
||||||
|
},
|
||||||
|
service: {
|
||||||
|
name: "Service Name",
|
||||||
|
type: "Service Type",
|
||||||
|
info: "Service Information"
|
||||||
},
|
},
|
||||||
email: "Email Address",
|
email: "Email Address",
|
||||||
port: "Database Port",
|
port: "Database Port",
|
||||||
|
setting: "Settings",
|
||||||
username: "Username",
|
username: "Username",
|
||||||
password: 'password',
|
password: 'Password',
|
||||||
service: 'service',
|
services: 'Services',
|
||||||
|
domain: 'Domain',
|
||||||
online: 'online',
|
online: 'online',
|
||||||
offline: 'offline',
|
offline: 'offline',
|
||||||
incident: 'incident',
|
incident: 'incident',
|
||||||
group: 'group',
|
group: 'Group',
|
||||||
message: 'message',
|
message: 'message',
|
||||||
logout: 'logout',
|
logout: 'logout',
|
||||||
sample_data: 'Sample Data',
|
sample_data: 'Sample Data',
|
||||||
|
|
|
@ -6679,7 +6679,7 @@ js-base64@^2.1.8:
|
||||||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209"
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209"
|
||||||
integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==
|
integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==
|
||||||
|
|
||||||
js-beautify@^1.6.12:
|
js-beautify@^1.11.0, js-beautify@^1.6.12:
|
||||||
version "1.11.0"
|
version "1.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.11.0.tgz#afb873dc47d58986360093dcb69951e8bcd5ded2"
|
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.11.0.tgz#afb873dc47d58986360093dcb69951e8bcd5ded2"
|
||||||
integrity sha512-a26B+Cx7USQGSWnz9YxgJNMmML/QG2nqIaL7VVYPCXbqiKz8PN0waSNvroMtvAK6tY7g/wPdNWGEP+JTNIBr6A==
|
integrity sha512-a26B+Cx7USQGSWnz9YxgJNMmML/QG2nqIaL7VVYPCXbqiKz8PN0waSNvroMtvAK6tY7g/wPdNWGEP+JTNIBr6A==
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -14,6 +14,7 @@ require (
|
||||||
github.com/go-mail/mail v2.3.1+incompatible
|
github.com/go-mail/mail v2.3.1+incompatible
|
||||||
github.com/golang/protobuf v1.3.5 // indirect
|
github.com/golang/protobuf v1.3.5 // indirect
|
||||||
github.com/gorilla/mux v1.7.4
|
github.com/gorilla/mux v1.7.4
|
||||||
|
github.com/gorilla/securecookie v1.1.1
|
||||||
github.com/jinzhu/gorm v1.9.12
|
github.com/jinzhu/gorm v1.9.12
|
||||||
github.com/magiconair/properties v1.8.1
|
github.com/magiconair/properties v1.8.1
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -123,6 +123,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||||
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
|
|
@ -3,14 +3,12 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dgrijalva/jwt-go"
|
|
||||||
"github.com/statping/statping/source"
|
"github.com/statping/statping/source"
|
||||||
"github.com/statping/statping/types/errors"
|
"github.com/statping/statping/types/errors"
|
||||||
"github.com/statping/statping/types/users"
|
"github.com/statping/statping/types/users"
|
||||||
"github.com/statping/statping/utils"
|
"github.com/statping/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func logoutHandler(w http.ResponseWriter, r *http.Request) {
|
func logoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -132,44 +130,6 @@ func logsLineHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(),
|
|
||||||
MaxAge: -1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func setJwtToken(user *users.User, w http.ResponseWriter) (JwtClaim, string) {
|
|
||||||
expirationTime := time.Now().Add(72 * time.Hour)
|
|
||||||
jwtClaim := JwtClaim{
|
|
||||||
Username: user.Username,
|
|
||||||
Admin: user.Admin.Bool,
|
|
||||||
StandardClaims: jwt.StandardClaims{
|
|
||||||
ExpiresAt: expirationTime.Unix(),
|
|
||||||
}}
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwtClaim)
|
|
||||||
tokenString, err := token.SignedString([]byte(jwtKey))
|
|
||||||
if err != nil {
|
|
||||||
log.Errorln("error setting token: ", err)
|
|
||||||
}
|
|
||||||
user.Token = tokenString
|
|
||||||
// set cookies
|
|
||||||
http.SetCookie(w, &http.Cookie{
|
|
||||||
Name: cookieKey,
|
|
||||||
Value: tokenString,
|
|
||||||
Expires: expirationTime,
|
|
||||||
})
|
|
||||||
return jwtClaim, tokenString
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiLoginHandler(w http.ResponseWriter, r *http.Request) {
|
func apiLoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
form := parseForm(r)
|
form := parseForm(r)
|
||||||
username := form.Get("username")
|
username := form.Get("username")
|
||||||
|
|
|
@ -3,7 +3,6 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dgrijalva/jwt-go"
|
|
||||||
"github.com/statping/statping/types/errors"
|
"github.com/statping/statping/types/errors"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -15,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
cookieKey = "statping_auth"
|
cookieName = "statping_auth"
|
||||||
timeout = time.Second * 30
|
timeout = time.Second * 30
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -100,31 +99,6 @@ func IsFullAuthenticated(r *http.Request) bool {
|
||||||
return IsAdmin(r)
|
return IsAdmin(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getJwtToken(r *http.Request) (JwtClaim, error) {
|
|
||||||
c, err := r.Cookie(cookieKey)
|
|
||||||
if err != nil {
|
|
||||||
if err == http.ErrNoCookie {
|
|
||||||
return JwtClaim{}, err
|
|
||||||
}
|
|
||||||
return JwtClaim{}, err
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
if err == jwt.ErrSignatureInvalid {
|
|
||||||
return JwtClaim{}, err
|
|
||||||
}
|
|
||||||
return JwtClaim{}, err
|
|
||||||
}
|
|
||||||
if !tkn.Valid {
|
|
||||||
return claims, errors.New("token is not valid")
|
|
||||||
}
|
|
||||||
return claims, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScopeName will show private JSON fields in the API.
|
// ScopeName will show private JSON fields in the API.
|
||||||
// It will return "admin" if request has valid admin authentication.
|
// It will return "admin" if request has valid admin authentication.
|
||||||
func ScopeName(r *http.Request) string {
|
func ScopeName(r *http.Request) string {
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/dgrijalva/jwt-go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/statping/statping/types/users"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JwtClaim struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Admin bool `json:"admin"`
|
||||||
|
jwt.StandardClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeJwtToken(w http.ResponseWriter) {
|
||||||
|
c := http.Cookie{
|
||||||
|
Name: cookieName,
|
||||||
|
Value: "",
|
||||||
|
MaxAge: -1,
|
||||||
|
Path: "/",
|
||||||
|
}
|
||||||
|
http.SetCookie(w, &c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setJwtToken(user *users.User, w http.ResponseWriter) (JwtClaim, string) {
|
||||||
|
expirationTime := time.Now().Add(72 * time.Hour)
|
||||||
|
jwtClaim := JwtClaim{
|
||||||
|
Username: user.Username,
|
||||||
|
Admin: user.Admin.Bool,
|
||||||
|
StandardClaims: jwt.StandardClaims{
|
||||||
|
ExpiresAt: expirationTime.Unix(),
|
||||||
|
}}
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwtClaim)
|
||||||
|
tokenString, err := token.SignedString([]byte(jwtKey))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("error setting token: ", err)
|
||||||
|
}
|
||||||
|
user.Token = tokenString
|
||||||
|
// set cookies
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: cookieName,
|
||||||
|
Value: tokenString,
|
||||||
|
Expires: expirationTime,
|
||||||
|
MaxAge: int(time.Duration(72 * time.Hour).Seconds()),
|
||||||
|
})
|
||||||
|
return jwtClaim, tokenString
|
||||||
|
}
|
||||||
|
|
||||||
|
func getJwtToken(r *http.Request) (JwtClaim, error) {
|
||||||
|
c, err := r.Cookie(cookieName)
|
||||||
|
if err != nil {
|
||||||
|
if err == http.ErrNoCookie {
|
||||||
|
return JwtClaim{}, err
|
||||||
|
}
|
||||||
|
return JwtClaim{}, err
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
if err == jwt.ErrSignatureInvalid {
|
||||||
|
return JwtClaim{}, err
|
||||||
|
}
|
||||||
|
return JwtClaim{}, err
|
||||||
|
}
|
||||||
|
if !tkn.Valid {
|
||||||
|
return claims, errors.New("token is not valid")
|
||||||
|
}
|
||||||
|
return claims, err
|
||||||
|
}
|
|
@ -45,6 +45,8 @@ func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("Updating %s Notifier", notifer.Title)
|
||||||
|
|
||||||
err = notifer.Update()
|
err = notifer.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
|
|
|
@ -34,6 +34,8 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
domain := r.PostForm.Get("domain")
|
domain := r.PostForm.Get("domain")
|
||||||
newsletter := r.PostForm.Get("newsletter")
|
newsletter := r.PostForm.Get("newsletter")
|
||||||
sendNews, _ := strconv.ParseBool(newsletter)
|
sendNews, _ := strconv.ParseBool(newsletter)
|
||||||
|
reports := r.PostForm.Get("send_reports")
|
||||||
|
sendReports, _ := strconv.ParseBool(reports)
|
||||||
|
|
||||||
log.WithFields(utils.ToFields(core.App, confgs)).Debugln("new configs posted")
|
log.WithFields(utils.ToFields(core.App, confgs)).Debugln("new configs posted")
|
||||||
|
|
||||||
|
@ -91,6 +93,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
UseCdn: null.NewNullBool(false),
|
UseCdn: null.NewNullBool(false),
|
||||||
Footer: null.NewNullString(""),
|
Footer: null.NewNullString(""),
|
||||||
Language: confgs.Language,
|
Language: confgs.Language,
|
||||||
|
AllowReports: null.NewNullBool(sendReports),
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infoln("Creating new Core")
|
log.Infoln("Creating new Core")
|
||||||
|
|
|
@ -29,6 +29,9 @@ var Command = &commandLine{¬ifications.Notification{
|
||||||
Delay: time.Duration(1 * time.Second),
|
Delay: time.Duration(1 * time.Second),
|
||||||
Icon: "fas fa-terminal",
|
Icon: "fas fa-terminal",
|
||||||
Host: "/bin/bash",
|
Host: "/bin/bash",
|
||||||
|
SuccessData: "curl -L http://localhost:8080",
|
||||||
|
FailureData: "curl -L http://localhost:8080",
|
||||||
|
DataType: "text",
|
||||||
Limits: 60,
|
Limits: 60,
|
||||||
Form: []notifications.NotificationForm{{
|
Form: []notifications.NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
|
@ -36,18 +39,6 @@ var Command = &commandLine{¬ifications.Notification{
|
||||||
Placeholder: "/usr/bin/curl",
|
Placeholder: "/usr/bin/curl",
|
||||||
DbField: "host",
|
DbField: "host",
|
||||||
SmallText: "You can use '/bin/sh', '/bin/bash', '/usr/bin/curl' or an absolute path for an application.",
|
SmallText: "You can use '/bin/sh', '/bin/bash', '/usr/bin/curl' or an absolute path for an application.",
|
||||||
}, {
|
|
||||||
Type: "text",
|
|
||||||
Title: "Command to Run on OnSuccess",
|
|
||||||
Placeholder: "http://localhost:8080/health",
|
|
||||||
DbField: "var1",
|
|
||||||
SmallText: "<b>Accepts Variables</b> This Command will run when a service is receiving a Successful event.",
|
|
||||||
}, {
|
|
||||||
Type: "text",
|
|
||||||
Title: "Command to Run on OnFailure",
|
|
||||||
Placeholder: "http://localhost:8080/health",
|
|
||||||
DbField: "var2",
|
|
||||||
SmallText: "<b>Accepts Variables</b> This Command will run when a service is receiving a Failing event.",
|
|
||||||
}}},
|
}}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,16 +50,14 @@ func runCommand(app string, cmd ...string) (string, string, error) {
|
||||||
|
|
||||||
// OnSuccess for commandLine will trigger successful service
|
// OnSuccess for commandLine will trigger successful service
|
||||||
func (c *commandLine) OnSuccess(s *services.Service) error {
|
func (c *commandLine) OnSuccess(s *services.Service) error {
|
||||||
msg := c.GetValue("var1")
|
tmpl := ReplaceVars(c.SuccessData, s, nil)
|
||||||
tmpl := ReplaceVars(msg, s, nil)
|
|
||||||
_, _, err := runCommand(c.Host, tmpl)
|
_, _, err := runCommand(c.Host, tmpl)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure for commandLine will trigger failing service
|
// OnFailure for commandLine will trigger failing service
|
||||||
func (c *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (c *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := c.GetValue("var2")
|
tmpl := ReplaceVars(c.FailureData, s, f)
|
||||||
tmpl := ReplaceVars(msg, s, f)
|
|
||||||
_, _, err := runCommand(c.Host, tmpl)
|
_, _, err := runCommand(c.Host, tmpl)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,11 @@ type discord struct {
|
||||||
*notifications.Notification
|
*notifications.Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
successData = `{"content": "Your service '{{.Service.Name}}' is currently online!"}`
|
||||||
|
failureData = `{"content": "Your service '{{.Service.Name}}' is currently failing! Reason: {{.Failure.Issue}}"}`
|
||||||
|
)
|
||||||
|
|
||||||
var Discorder = &discord{¬ifications.Notification{
|
var Discorder = &discord{¬ifications.Notification{
|
||||||
Method: "discord",
|
Method: "discord",
|
||||||
Title: "discord",
|
Title: "discord",
|
||||||
|
@ -28,6 +33,9 @@ var Discorder = &discord{¬ifications.Notification{
|
||||||
Delay: time.Duration(5 * time.Second),
|
Delay: time.Duration(5 * time.Second),
|
||||||
Host: "https://discordapp.com/api/webhooks/****/*****",
|
Host: "https://discordapp.com/api/webhooks/****/*****",
|
||||||
Icon: "fab fa-discord",
|
Icon: "fab fa-discord",
|
||||||
|
SuccessData: successData,
|
||||||
|
FailureData: failureData,
|
||||||
|
DataType: "json",
|
||||||
Limits: 60,
|
Limits: 60,
|
||||||
Form: []notifications.NotificationForm{{
|
Form: []notifications.NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
|
|
|
@ -110,6 +110,9 @@ var email = &emailer{¬ifications.Notification{
|
||||||
Author: "Hunter Long",
|
Author: "Hunter Long",
|
||||||
AuthorUrl: "https://github.com/hunterlong",
|
AuthorUrl: "https://github.com/hunterlong",
|
||||||
Icon: "far fa-envelope",
|
Icon: "far fa-envelope",
|
||||||
|
SuccessData: "Service {{.Service.Name}} is Back Online",
|
||||||
|
FailureData: "Service {{.Service.Name}} is Offline",
|
||||||
|
DataType: "text",
|
||||||
Limits: 30,
|
Limits: 30,
|
||||||
Form: []notifications.NotificationForm{{
|
Form: []notifications.NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
|
@ -232,6 +235,7 @@ func (e *emailer) dialSend(email *emailOutgoing) error {
|
||||||
} else {
|
} else {
|
||||||
mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.SetHeader("From", email.From)
|
m.SetHeader("From", email.From)
|
||||||
m.SetHeader("To", email.To)
|
m.SetHeader("To", email.To)
|
||||||
m.SetHeader("Subject", email.Subject)
|
m.SetHeader("Subject", email.Subject)
|
||||||
|
|
|
@ -35,6 +35,9 @@ var Pushover = &pushover{¬ifications.Notification{
|
||||||
Icon: "fa dot-circle",
|
Icon: "fa dot-circle",
|
||||||
Delay: time.Duration(10 * time.Second),
|
Delay: time.Duration(10 * time.Second),
|
||||||
Limits: 60,
|
Limits: 60,
|
||||||
|
SuccessData: `Your service '{{.Service.Name}}' is currently online!`,
|
||||||
|
FailureData: `Your service '{{.Service.Name}}' is currently offline!`,
|
||||||
|
DataType: "text",
|
||||||
Form: []notifications.NotificationForm{{
|
Form: []notifications.NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Title: "User Token",
|
Title: "User Token",
|
||||||
|
@ -68,15 +71,15 @@ func (t *pushover) sendMessage(message string) (string, error) {
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (t *pushover) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (t *pushover) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf("Your service '%s' is currently offline!", s.Name)
|
message := ReplaceVars(t.FailureData, s, f)
|
||||||
_, err := t.sendMessage(msg)
|
_, err := t.sendMessage(message)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (t *pushover) OnSuccess(s *services.Service) error {
|
func (t *pushover) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf("Your service '%s' is currently online!", s.Name)
|
message := ReplaceVars(t.SuccessData, s, nil)
|
||||||
_, err := t.sendMessage(msg)
|
_, err := t.sendMessage(message)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,9 @@ var _ notifier.Notifier = (*slack)(nil)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
slackMethod = "slack"
|
slackMethod = "slack"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
failingTemplate = `{ "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": ":warning: The service {{.Service.Name}} is currently offline! :warning:" } }, { "type": "divider" }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "*Service:*\n{{.Service.Name}}" }, { "type": "mrkdwn", "text": "*URL:*\n{{.Service.Domain}}" }, { "type": "mrkdwn", "text": "*Status Code:*\n{{.Service.LastStatusCode}}" }, { "type": "mrkdwn", "text": "*When:*\n{{.Failure.CreatedAt}}" }, { "type": "mrkdwn", "text": "*Downtime:*\n{{.Service.DowntimeAgo}}" }, { "type": "plain_text", "text": "*Error:*\n{{.Failure.Issue}}" } ] }, { "type": "divider" }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "View Offline Service", "emoji": true }, "style": "danger", "url": "{{.Core.Domain}}/service/{{.Service.Id}}" }, { "type": "button", "text": { "type": "plain_text", "text": "Go to Statping", "emoji": true }, "url": "{{.Core.Domain}}" } ] } ] }`
|
failingTemplate = `{ "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": ":warning: The service {{.Service.Name}} is currently offline! :warning:" } }, { "type": "divider" }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "*Service:*\n{{.Service.Name}}" }, { "type": "mrkdwn", "text": "*URL:*\n{{.Service.Domain}}" }, { "type": "mrkdwn", "text": "*Status Code:*\n{{.Service.LastStatusCode}}" }, { "type": "mrkdwn", "text": "*When:*\n{{.Failure.CreatedAt}}" }, { "type": "mrkdwn", "text": "*Downtime:*\n{{.Service.DowntimeAgo}}" }, { "type": "plain_text", "text": "*Error:*\n{{.Failure.Issue}}" } ] }, { "type": "divider" }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "View Offline Service", "emoji": true }, "style": "danger", "url": "{{.Core.Domain}}/service/{{.Service.Id}}" }, { "type": "button", "text": { "type": "plain_text", "text": "Go to Statping", "emoji": true }, "url": "{{.Core.Domain}}" } ] } ] }`
|
||||||
successTemplate = `{ "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "The service {{.Service.Name}} is back online." } }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "View Service", "emoji": true }, "style": "primary", "url": "{{.Core.Domain}}/service/{{.Service.Id}}" }, { "type": "button", "text": { "type": "plain_text", "text": "Go to Statping", "emoji": true }, "url": "{{.Core.Domain}}" } ] } ] }`
|
successTemplate = `{ "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": "The service {{.Service.Name}} is back online." } }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "View Service", "emoji": true }, "style": "primary", "url": "{{.Core.Domain}}/service/{{.Service.Id}}" }, { "type": "button", "text": { "type": "plain_text", "text": "Go to Statping", "emoji": true }, "url": "{{.Core.Domain}}" } ] } ] }`
|
||||||
)
|
)
|
||||||
|
@ -37,6 +40,10 @@ var slacker = &slack{¬ifications.Notification{
|
||||||
Delay: time.Duration(10 * time.Second),
|
Delay: time.Duration(10 * time.Second),
|
||||||
Host: "https://webhooksurl.slack.com/***",
|
Host: "https://webhooksurl.slack.com/***",
|
||||||
Icon: "fab fa-slack",
|
Icon: "fab fa-slack",
|
||||||
|
SuccessData: successTemplate,
|
||||||
|
FailureData: failingTemplate,
|
||||||
|
DataType: "json",
|
||||||
|
RequestInfo: "Slack allows you to customize your own messages with many complex components. Checkout the <a target=\"_blank\" href=\"https://api.slack.com/reference/surfaces/formatting\">Slack Message API</a> to learn how you can create your own.",
|
||||||
Limits: 60,
|
Limits: 60,
|
||||||
Form: []notifications.NotificationForm{{
|
Form: []notifications.NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
|
|
|
@ -32,6 +32,9 @@ var Telegram = &telegram{¬ifications.Notification{
|
||||||
AuthorUrl: "https://github.com/hunterlong",
|
AuthorUrl: "https://github.com/hunterlong",
|
||||||
Icon: "fab fa-telegram-plane",
|
Icon: "fab fa-telegram-plane",
|
||||||
Delay: time.Duration(5 * time.Second),
|
Delay: time.Duration(5 * time.Second),
|
||||||
|
SuccessData: "Your service '{{.Service.Name}}' is currently online!",
|
||||||
|
FailureData: "Your service '{{.Service.Name}}' is currently offline!",
|
||||||
|
DataType: "text",
|
||||||
Limits: 60,
|
Limits: 60,
|
||||||
Form: []notifications.NotificationForm{{
|
Form: []notifications.NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
|
@ -72,14 +75,14 @@ func (t *telegram) sendMessage(message string) (string, error) {
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (t *telegram) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (t *telegram) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
msg := ReplaceVars(t.FailureData, s, f)
|
||||||
_, err := t.sendMessage(msg)
|
_, err := t.sendMessage(msg)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (t *telegram) OnSuccess(s *services.Service) error {
|
func (t *telegram) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
msg := ReplaceVars(t.SuccessData, s, nil)
|
||||||
_, err := t.sendMessage(msg)
|
_, err := t.sendMessage(msg)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,9 @@ var Twilio = &twilio{¬ifications.Notification{
|
||||||
AuthorUrl: "https://github.com/hunterlong",
|
AuthorUrl: "https://github.com/hunterlong",
|
||||||
Icon: "far fa-comment-alt",
|
Icon: "far fa-comment-alt",
|
||||||
Delay: time.Duration(10 * time.Second),
|
Delay: time.Duration(10 * time.Second),
|
||||||
|
SuccessData: "Your service '{{.Service.Name}}' is currently online!",
|
||||||
|
FailureData: "Your service '{{.Service.Name}}' is currently offline!",
|
||||||
|
DataType: "text",
|
||||||
Limits: 15,
|
Limits: 15,
|
||||||
Form: []notifications.NotificationForm{{
|
Form: []notifications.NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
|
@ -88,14 +91,14 @@ func (t *twilio) sendMessage(message string) (string, error) {
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (t *twilio) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (t *twilio) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
msg := ReplaceVars(t.FailureData, s, f)
|
||||||
_, err := t.sendMessage(msg)
|
_, err := t.sendMessage(msg)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (t *twilio) OnSuccess(s *services.Service) error {
|
func (t *twilio) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
msg := ReplaceVars(t.SuccessData, s, nil)
|
||||||
_, err := t.sendMessage(msg)
|
_, err := t.sendMessage(msg)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ var Webhook = &webhooker{¬ifications.Notification{
|
||||||
AuthorUrl: "https://github.com/hunterlong",
|
AuthorUrl: "https://github.com/hunterlong",
|
||||||
Icon: "fas fa-code-branch",
|
Icon: "fas fa-code-branch",
|
||||||
Delay: time.Duration(1 * time.Second),
|
Delay: time.Duration(1 * time.Second),
|
||||||
|
SuccessData: `{"id": {{.Service.Id}}, "online": true}`,
|
||||||
|
FailureData: `{"id": {{.Service.Id}}, "online": false}`,
|
||||||
|
DataType: "json",
|
||||||
Limits: 180,
|
Limits: 180,
|
||||||
Form: []notifications.NotificationForm{{
|
Form: []notifications.NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
|
@ -47,12 +50,6 @@ var Webhook = &webhooker{¬ifications.Notification{
|
||||||
SmallText: "Choose a HTTP method for example: GET, POST, DELETE, or PATCH.",
|
SmallText: "Choose a HTTP method for example: GET, POST, DELETE, or PATCH.",
|
||||||
DbField: "Var1",
|
DbField: "Var1",
|
||||||
Required: true,
|
Required: true,
|
||||||
}, {
|
|
||||||
Type: "textarea",
|
|
||||||
Title: "HTTP Body",
|
|
||||||
Placeholder: `{"service_id": {{.Service.Id}}", "service_name": "{{.Service.Name}"}`,
|
|
||||||
SmallText: "Optional HTTP body for a POST request. You can insert variables into your body request.<br>{{.Service.Id}}, {{.Service.Name}}, {{.Service.Online}}<br>{{.Failure.Issue}}",
|
|
||||||
DbField: "Var2",
|
|
||||||
}, {
|
}, {
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Title: "Content Type",
|
Title: "Content Type",
|
||||||
|
@ -114,7 +111,7 @@ func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *webhooker) OnTest() (string, error) {
|
func (w *webhooker) OnTest() (string, error) {
|
||||||
body := ReplaceVars(w.Var2, exampleService, exampleFailure)
|
body := ReplaceVars(w.SuccessData, exampleService, exampleFailure)
|
||||||
resp, err := w.sendHttpWebhook(body)
|
resp, err := w.sendHttpWebhook(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -128,7 +125,7 @@ func (w *webhooker) OnTest() (string, error) {
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := ReplaceVars(w.Var2, s, f)
|
msg := ReplaceVars(w.FailureData, s, f)
|
||||||
resp, err := w.sendHttpWebhook(msg)
|
resp, err := w.sendHttpWebhook(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -139,7 +136,7 @@ func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (w *webhooker) OnSuccess(s *services.Service) error {
|
func (w *webhooker) OnSuccess(s *services.Service) error {
|
||||||
msg := ReplaceVars(w.Var2, s, nil)
|
msg := ReplaceVars(w.SuccessData, s, nil)
|
||||||
resp, err := w.sendHttpWebhook(msg)
|
resp, err := w.sendHttpWebhook(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/statping/statping/utils"
|
"github.com/statping/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
||||||
|
@ -24,6 +25,7 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
||||||
domain := g("domain")
|
domain := g("domain")
|
||||||
email := g("email")
|
email := g("email")
|
||||||
language := g("language")
|
language := g("language")
|
||||||
|
reports, _ := strconv.ParseBool(g("send_reports"))
|
||||||
|
|
||||||
if project == "" || username == "" || password == "" {
|
if project == "" || username == "" || password == "" {
|
||||||
err := errors.New("Missing required elements on setup form")
|
err := errors.New("Missing required elements on setup form")
|
||||||
|
@ -40,6 +42,7 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
||||||
p.Set("NAME", project)
|
p.Set("NAME", project)
|
||||||
p.Set("DESCRIPTION", description)
|
p.Set("DESCRIPTION", description)
|
||||||
p.Set("LANGUAGE", language)
|
p.Set("LANGUAGE", language)
|
||||||
|
p.Set("ALLOW_REPORTS", reports)
|
||||||
|
|
||||||
confg := &DbConfig{
|
confg := &DbConfig{
|
||||||
DbConn: dbConn,
|
DbConn: dbConn,
|
||||||
|
@ -56,8 +59,8 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
||||||
Email: email,
|
Email: email,
|
||||||
Location: utils.Directory,
|
Location: utils.Directory,
|
||||||
Language: language,
|
Language: language,
|
||||||
|
SendReports: reports,
|
||||||
}
|
}
|
||||||
|
|
||||||
return confg, nil
|
return confg, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,12 @@ func LoadConfigFile(configFile string) (*DbConfig, error) {
|
||||||
if db.ApiSecret != "" {
|
if db.ApiSecret != "" {
|
||||||
p.Set("API_SECRET", db.ApiSecret)
|
p.Set("API_SECRET", db.ApiSecret)
|
||||||
}
|
}
|
||||||
|
if db.Language != "" {
|
||||||
|
p.Set("LANGUAGE", "en")
|
||||||
|
}
|
||||||
|
if db.SendReports {
|
||||||
|
p.Set("ALLOW_REPORTS", true)
|
||||||
|
}
|
||||||
|
|
||||||
configs := &DbConfig{
|
configs := &DbConfig{
|
||||||
DbConn: p.GetString("DB_CONN"),
|
DbConn: p.GetString("DB_CONN"),
|
||||||
|
@ -65,6 +71,8 @@ func LoadConfigFile(configFile string) (*DbConfig, error) {
|
||||||
Password: p.GetString("ADMIN_PASSWORD"),
|
Password: p.GetString("ADMIN_PASSWORD"),
|
||||||
Location: utils.Directory,
|
Location: utils.Directory,
|
||||||
SqlFile: p.GetString("SQL_FILE"),
|
SqlFile: p.GetString("SQL_FILE"),
|
||||||
|
Language: p.GetString("LANGUAGE"),
|
||||||
|
SendReports: p.GetBool("ALLOW_REPORTS"),
|
||||||
}
|
}
|
||||||
log.WithFields(utils.ToFields(configs)).Debugln("read config file: " + configFile)
|
log.WithFields(utils.ToFields(configs)).Debugln("read config file: " + configFile)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ type DbConfig struct {
|
||||||
DbPort int `yaml:"port" json:"-"`
|
DbPort int `yaml:"port" json:"-"`
|
||||||
ApiSecret string `yaml:"api_secret" json:"-"`
|
ApiSecret string `yaml:"api_secret" json:"-"`
|
||||||
Language string `yaml:"language" json:"language"`
|
Language string `yaml:"language" json:"language"`
|
||||||
|
SendReports bool `yaml:"send_reports" json:"send_reports"`
|
||||||
Project string `yaml:"-" json:"-"`
|
Project string `yaml:"-" json:"-"`
|
||||||
Description string `yaml:"-" json:"-"`
|
Description string `yaml:"-" json:"-"`
|
||||||
Domain string `yaml:"-" json:"-"`
|
Domain string `yaml:"-" json:"-"`
|
||||||
|
|
|
@ -24,12 +24,20 @@ func Find(method string) (*Notification, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notification) Create() error {
|
func (n *Notification) Create() error {
|
||||||
q := db.Where("method = ?", n.Method).Find(n)
|
var p Notification
|
||||||
|
q := db.Where("method = ?", n.Method).Find(&p)
|
||||||
if q.RecordNotFound() {
|
if q.RecordNotFound() {
|
||||||
if err := db.Create(n).Error(); err != nil {
|
if err := db.Create(n).Error(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if p.FailureData == "" || p.SuccessData == "" {
|
||||||
|
p.SuccessData = n.SuccessData
|
||||||
|
p.FailureData = n.FailureData
|
||||||
|
if err := p.Update(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +52,8 @@ func (n *Notification) UpdateFields(notif *Notification) *Notification {
|
||||||
n.ApiSecret = notif.ApiSecret
|
n.ApiSecret = notif.ApiSecret
|
||||||
n.Var1 = notif.Var1
|
n.Var1 = notif.Var1
|
||||||
n.Var2 = notif.Var2
|
n.Var2 = notif.Var2
|
||||||
|
n.SuccessData = notif.SuccessData
|
||||||
|
n.FailureData = notif.FailureData
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,10 @@ type Notification struct {
|
||||||
Enabled null.NullBool `gorm:"column:enabled;type:boolean;default:false" json:"enabled,omitempty"`
|
Enabled null.NullBool `gorm:"column:enabled;type:boolean;default:false" json:"enabled,omitempty"`
|
||||||
Limits int `gorm:"not null;column:limits" json:"limits"`
|
Limits int `gorm:"not null;column:limits" json:"limits"`
|
||||||
Removable bool `gorm:"column:removable" json:"removable"`
|
Removable bool `gorm:"column:removable" json:"removable"`
|
||||||
|
SuccessData string `gorm:"not null;column:success_data" json:"success_data,omitempty"`
|
||||||
|
FailureData string `gorm:"not null;column:failure_data" json:"failure_data,omitempty"`
|
||||||
|
DataType string `gorm:"-" json:"data_type,omitempty"`
|
||||||
|
RequestInfo string `gorm:"-" json:"request_info,omitempty"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||||
Title string `gorm:"-" json:"title"`
|
Title string `gorm:"-" json:"title"`
|
||||||
|
|
Loading…
Reference in New Issue