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",
|
||||
"core-js": "^3.4.4",
|
||||
"date-fns": "^2.9.0",
|
||||
"js-beautify": "^1.11.0",
|
||||
"querystring": "^0.2.0",
|
||||
"vue": "^2.6.11",
|
||||
"vue-apexcharts": "^1.5.2",
|
||||
|
|
|
@ -395,6 +395,30 @@ HTML,BODY {
|
|||
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 {
|
||||
background-color: $service-background;
|
||||
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="col-4">
|
||||
<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 class="col-4">
|
||||
<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 class="col-4">
|
||||
<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>
|
||||
|
||||
|
|
|
@ -121,20 +121,18 @@
|
|||
},
|
||||
changeTab (v) {
|
||||
this.tab = v
|
||||
if (v === 'base') {
|
||||
this.$refs.base.codemirror.refresh();
|
||||
} else if (v === 'vars') {
|
||||
this.$refs.vars.codemirror.refresh();
|
||||
} else if (v === 'mobile') {
|
||||
this.$refs.mobile.codemirror.refresh();
|
||||
}
|
||||
// if (v === 'base') {
|
||||
// this.$refs.base.codemirror.refresh();
|
||||
// } else if (v === 'vars') {
|
||||
// this.$refs.vars.codemirror.refresh();
|
||||
// } else if (v === 'mobile') {
|
||||
// this.$refs.mobile.codemirror.refresh();
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style>
|
||||
<style scoped>
|
||||
.CodeMirror {
|
||||
border: 1px solid #eee;
|
||||
height: 550px;
|
||||
|
|
|
@ -9,22 +9,22 @@
|
|||
<div class="navbar-collapse" :class="{collapse: !navopen}" id="navbarText">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<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 @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 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 @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 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 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>
|
||||
</ul>
|
||||
<span class="navbar-text">
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
<template>
|
||||
<form @submit.prevent="saveSettings">
|
||||
<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">
|
||||
</div>
|
||||
|
||||
<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">
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<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">
|
||||
</div>
|
||||
<div class="col-4 col-sm-3 mt-sm-1 mt-0">
|
||||
|
@ -26,13 +26,13 @@
|
|||
</div>
|
||||
|
||||
<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>
|
||||
<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 class="form-group">
|
||||
<label>Language</label>
|
||||
<label>{{ $t('setup.language') }}</label>
|
||||
<select v-model="core.language" class="form-control">
|
||||
<option value="en">English</option>
|
||||
<option value="es">Spanish</option>
|
||||
|
@ -40,11 +40,10 @@
|
|||
<option value="ru">Russian</option>
|
||||
<option value="de">German</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">HTML is allowed inside the footer</small>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<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">
|
||||
|
@ -52,12 +51,12 @@
|
|||
</span>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
</form>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<form @submit.prevent="saveNotifier">
|
||||
<div class="card contain-card text-black-50 bg-white mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
{{notifier.title}}
|
||||
|
@ -10,8 +11,6 @@
|
|||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form @submit.prevent="saveNotifier">
|
||||
|
||||
<p class="small text-muted" v-html="notifier.description"/>
|
||||
|
||||
<div v-for="(form, index) in notifier.form" v-bind:key="index" class="form-group">
|
||||
|
@ -30,31 +29,72 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="error && !success" class="alert alert-danger col-12" role="alert">
|
||||
{{error}}<p v-if="response">Response:<br>{{response}}</p>
|
||||
<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 v-if="success" class="alert alert-success col-12" role="alert">
|
||||
{{notifier.title}} appears to be working!
|
||||
<p v-if="response">Response:<br>{{response}}</p>
|
||||
|
||||
<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>
|
||||
|
||||
<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">
|
||||
{{error}}<p v-if="response">Response:<br>{{response}}</p>
|
||||
</div>
|
||||
<div v-if="success" class="alert alert-success col-12" role="alert">
|
||||
{{notifier.title}} appears to be working!
|
||||
<p v-if="response">Response:<br>{{response}}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-body">
|
||||
|
||||
<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">
|
||||
<i class="fa fa-check-circle"></i> {{loading ? "Loading..." : saved ? "Saved" : "Save Settings"}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-6 col-sm-6 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="testNotifier" class="btn btn-outline-dark btn-block text-capitalize test-notifier"><i class="fa fa-vial"></i>
|
||||
{{loadingTest ? "Loading..." : "Test Notifier"}}</button>
|
||||
<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 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>
|
||||
|
||||
|
@ -69,16 +109,31 @@
|
|||
</template>
|
||||
|
||||
<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 {
|
||||
name: 'Notifier',
|
||||
components: {
|
||||
codemirror
|
||||
},
|
||||
props: {
|
||||
notifier: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
|
@ -87,10 +142,55 @@ export default {
|
|||
response: null,
|
||||
success: false,
|
||||
saved: false,
|
||||
success_data: null,
|
||||
failure_data: null,
|
||||
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: {
|
||||
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() {
|
||||
this.notifier.enabled = !!this.notifier.enabled
|
||||
const form = {
|
||||
|
@ -112,6 +212,9 @@ export default {
|
|||
}
|
||||
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)
|
||||
const notifiers = await Api.notifiers()
|
||||
await this.$store.commit('setNotifiers', notifiers)
|
||||
|
@ -142,7 +245,10 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
.CodeMirror {
|
||||
border: 1px solid #eee;
|
||||
height: 550px;
|
||||
font-size: 9pt;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<form v-if="service.type" @submit.prevent="saveService">
|
||||
<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="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">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<select v-model="service.type" class="form-control" id="service_type">
|
||||
<option value="http">HTTP Service</option>
|
||||
|
@ -24,7 +24,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<select v-model="service.group_id" class="form-control">
|
||||
<option value="0" >No Group</option>
|
||||
|
|
|
@ -26,13 +26,19 @@
|
|||
<option value="mysql">MySQL</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<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">
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<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">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<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">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label class="text-capitalize">{{ $t('port') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_port" id="db_port" type="number" class="form-control" placeholder="5432">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<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">
|
||||
</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 class="col-6">
|
||||
|
@ -72,7 +93,7 @@
|
|||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
|
@ -90,7 +111,7 @@
|
|||
<div class="col-4">
|
||||
<label class="d-none d-sm-block text-capitalize text-capitalize">{{ $t('setup.newsletter') }}</label>
|
||||
<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>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -140,6 +161,7 @@
|
|||
confirm_password: "",
|
||||
sample_data: true,
|
||||
newsletter: true,
|
||||
send_reports: true,
|
||||
email: "",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
const english = {
|
||||
top_nav: {
|
||||
dashboard: "Dashboard",
|
||||
services: "Services",
|
||||
users: "Users",
|
||||
announcements: "Announcements",
|
||||
settings: "Settings",
|
||||
logs: "Logs",
|
||||
},
|
||||
setup: {
|
||||
language: "Language",
|
||||
connection: "Database Connection",
|
||||
|
@ -9,19 +17,41 @@ const english = {
|
|||
domain: "Domain URL",
|
||||
username: "Admin Username",
|
||||
password: "Admin Password",
|
||||
password_confirm: "Confirm Admin Username",
|
||||
password_confirm: "Confirm Admin Password",
|
||||
newsletter: "Newsletter",
|
||||
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",
|
||||
port: "Database Port",
|
||||
setting: "Settings",
|
||||
username: "Username",
|
||||
password: 'password',
|
||||
service: 'service',
|
||||
password: 'Password',
|
||||
services: 'Services',
|
||||
domain: 'Domain',
|
||||
online: 'online',
|
||||
offline: 'offline',
|
||||
incident: 'incident',
|
||||
group: 'group',
|
||||
group: 'Group',
|
||||
message: 'message',
|
||||
logout: 'logout',
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.11.0.tgz#afb873dc47d58986360093dcb69951e8bcd5ded2"
|
||||
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/golang/protobuf v1.3.5 // indirect
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/jinzhu/gorm v1.9.12
|
||||
github.com/magiconair/properties v1.8.1
|
||||
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/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
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/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=
|
||||
|
|
|
@ -3,14 +3,12 @@ package handlers
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/statping/statping/source"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/users"
|
||||
"github.com/statping/statping/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
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) {
|
||||
form := parseForm(r)
|
||||
username := form.Get("username")
|
||||
|
|
|
@ -3,7 +3,6 @@ package handlers
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
@ -15,8 +14,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
cookieKey = "statping_auth"
|
||||
timeout = time.Second * 30
|
||||
cookieName = "statping_auth"
|
||||
timeout = time.Second * 30
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -100,31 +99,6 @@ func IsFullAuthenticated(r *http.Request) bool {
|
|||
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.
|
||||
// It will return "admin" if request has valid admin authentication.
|
||||
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
|
||||
}
|
||||
|
||||
log.Infof("Updating %s Notifier", notifer.Title)
|
||||
|
||||
err = notifer.Update()
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
|
|
|
@ -34,6 +34,8 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
domain := r.PostForm.Get("domain")
|
||||
newsletter := r.PostForm.Get("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")
|
||||
|
||||
|
@ -81,16 +83,17 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
notifiers.InitNotifiers()
|
||||
|
||||
c := &core.Core{
|
||||
Name: project,
|
||||
Description: description,
|
||||
ApiSecret: utils.Params.GetString("API_SECRET"),
|
||||
Domain: domain,
|
||||
Version: core.App.Version,
|
||||
Started: utils.Now(),
|
||||
CreatedAt: utils.Now(),
|
||||
UseCdn: null.NewNullBool(false),
|
||||
Footer: null.NewNullString(""),
|
||||
Language: confgs.Language,
|
||||
Name: project,
|
||||
Description: description,
|
||||
ApiSecret: utils.Params.GetString("API_SECRET"),
|
||||
Domain: domain,
|
||||
Version: core.App.Version,
|
||||
Started: utils.Now(),
|
||||
CreatedAt: utils.Now(),
|
||||
UseCdn: null.NewNullBool(false),
|
||||
Footer: null.NewNullString(""),
|
||||
Language: confgs.Language,
|
||||
AllowReports: null.NewNullBool(sendReports),
|
||||
}
|
||||
|
||||
log.Infoln("Creating new Core")
|
||||
|
|
|
@ -29,6 +29,9 @@ var Command = &commandLine{¬ifications.Notification{
|
|||
Delay: time.Duration(1 * time.Second),
|
||||
Icon: "fas fa-terminal",
|
||||
Host: "/bin/bash",
|
||||
SuccessData: "curl -L http://localhost:8080",
|
||||
FailureData: "curl -L http://localhost:8080",
|
||||
DataType: "text",
|
||||
Limits: 60,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
|
@ -36,18 +39,6 @@ var Command = &commandLine{¬ifications.Notification{
|
|||
Placeholder: "/usr/bin/curl",
|
||||
DbField: "host",
|
||||
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
|
||||
func (c *commandLine) OnSuccess(s *services.Service) error {
|
||||
msg := c.GetValue("var1")
|
||||
tmpl := ReplaceVars(msg, s, nil)
|
||||
tmpl := ReplaceVars(c.SuccessData, s, nil)
|
||||
_, _, err := runCommand(c.Host, tmpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnFailure for commandLine will trigger failing service
|
||||
func (c *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := c.GetValue("var2")
|
||||
tmpl := ReplaceVars(msg, s, f)
|
||||
tmpl := ReplaceVars(c.FailureData, s, f)
|
||||
_, _, err := runCommand(c.Host, tmpl)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -19,6 +19,11 @@ type discord struct {
|
|||
*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{
|
||||
Method: "discord",
|
||||
Title: "discord",
|
||||
|
@ -28,6 +33,9 @@ var Discorder = &discord{¬ifications.Notification{
|
|||
Delay: time.Duration(5 * time.Second),
|
||||
Host: "https://discordapp.com/api/webhooks/****/*****",
|
||||
Icon: "fab fa-discord",
|
||||
SuccessData: successData,
|
||||
FailureData: failureData,
|
||||
DataType: "json",
|
||||
Limits: 60,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
|
|
|
@ -110,6 +110,9 @@ var email = &emailer{¬ifications.Notification{
|
|||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
Icon: "far fa-envelope",
|
||||
SuccessData: "Service {{.Service.Name}} is Back Online",
|
||||
FailureData: "Service {{.Service.Name}} is Offline",
|
||||
DataType: "text",
|
||||
Limits: 30,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
|
@ -232,6 +235,7 @@ func (e *emailer) dialSend(email *emailOutgoing) error {
|
|||
} else {
|
||||
mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
m.SetHeader("From", email.From)
|
||||
m.SetHeader("To", email.To)
|
||||
m.SetHeader("Subject", email.Subject)
|
||||
|
|
|
@ -35,6 +35,9 @@ var Pushover = &pushover{¬ifications.Notification{
|
|||
Icon: "fa dot-circle",
|
||||
Delay: time.Duration(10 * time.Second),
|
||||
Limits: 60,
|
||||
SuccessData: `Your service '{{.Service.Name}}' is currently online!`,
|
||||
FailureData: `Your service '{{.Service.Name}}' is currently offline!`,
|
||||
DataType: "text",
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
Title: "User Token",
|
||||
|
@ -68,15 +71,15 @@ func (t *pushover) sendMessage(message string) (string, error) {
|
|||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *pushover) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := fmt.Sprintf("Your service '%s' is currently offline!", s.Name)
|
||||
_, err := t.sendMessage(msg)
|
||||
message := ReplaceVars(t.FailureData, s, f)
|
||||
_, err := t.sendMessage(message)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *pushover) OnSuccess(s *services.Service) error {
|
||||
msg := fmt.Sprintf("Your service '%s' is currently online!", s.Name)
|
||||
_, err := t.sendMessage(msg)
|
||||
message := ReplaceVars(t.SuccessData, s, nil)
|
||||
_, err := t.sendMessage(message)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,10 @@ import (
|
|||
var _ notifier.Notifier = (*slack)(nil)
|
||||
|
||||
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}}" } ] } ] }`
|
||||
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),
|
||||
Host: "https://webhooksurl.slack.com/***",
|
||||
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,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
|
|
|
@ -32,6 +32,9 @@ var Telegram = &telegram{¬ifications.Notification{
|
|||
AuthorUrl: "https://github.com/hunterlong",
|
||||
Icon: "fab fa-telegram-plane",
|
||||
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,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
|
@ -72,14 +75,14 @@ func (t *telegram) sendMessage(message string) (string, error) {
|
|||
|
||||
// OnFailure will trigger failing service
|
||||
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)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
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)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -33,6 +33,9 @@ var Twilio = &twilio{¬ifications.Notification{
|
|||
AuthorUrl: "https://github.com/hunterlong",
|
||||
Icon: "far fa-comment-alt",
|
||||
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,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
|
@ -88,14 +91,14 @@ func (t *twilio) sendMessage(message string) (string, error) {
|
|||
|
||||
// OnFailure will trigger failing service
|
||||
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)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
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)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ var Webhook = &webhooker{¬ifications.Notification{
|
|||
AuthorUrl: "https://github.com/hunterlong",
|
||||
Icon: "fas fa-code-branch",
|
||||
Delay: time.Duration(1 * time.Second),
|
||||
SuccessData: `{"id": {{.Service.Id}}, "online": true}`,
|
||||
FailureData: `{"id": {{.Service.Id}}, "online": false}`,
|
||||
DataType: "json",
|
||||
Limits: 180,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
|
@ -47,12 +50,6 @@ var Webhook = &webhooker{¬ifications.Notification{
|
|||
SmallText: "Choose a HTTP method for example: GET, POST, DELETE, or PATCH.",
|
||||
DbField: "Var1",
|
||||
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",
|
||||
Title: "Content Type",
|
||||
|
@ -114,7 +111,7 @@ func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
|||
}
|
||||
|
||||
func (w *webhooker) OnTest() (string, error) {
|
||||
body := ReplaceVars(w.Var2, exampleService, exampleFailure)
|
||||
body := ReplaceVars(w.SuccessData, exampleService, exampleFailure)
|
||||
resp, err := w.sendHttpWebhook(body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -128,7 +125,7 @@ func (w *webhooker) OnTest() (string, error) {
|
|||
|
||||
// OnFailure will trigger failing service
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -139,7 +136,7 @@ func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) error {
|
|||
|
||||
// OnSuccess will trigger successful service
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
||||
|
@ -24,6 +25,7 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
|||
domain := g("domain")
|
||||
email := g("email")
|
||||
language := g("language")
|
||||
reports, _ := strconv.ParseBool(g("send_reports"))
|
||||
|
||||
if project == "" || username == "" || password == "" {
|
||||
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("DESCRIPTION", description)
|
||||
p.Set("LANGUAGE", language)
|
||||
p.Set("ALLOW_REPORTS", reports)
|
||||
|
||||
confg := &DbConfig{
|
||||
DbConn: dbConn,
|
||||
|
@ -56,8 +59,8 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
|||
Email: email,
|
||||
Location: utils.Directory,
|
||||
Language: language,
|
||||
SendReports: reports,
|
||||
}
|
||||
|
||||
return confg, nil
|
||||
|
||||
}
|
||||
|
|
|
@ -49,6 +49,12 @@ func LoadConfigFile(configFile string) (*DbConfig, error) {
|
|||
if 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{
|
||||
DbConn: p.GetString("DB_CONN"),
|
||||
|
@ -65,6 +71,8 @@ func LoadConfigFile(configFile string) (*DbConfig, error) {
|
|||
Password: p.GetString("ADMIN_PASSWORD"),
|
||||
Location: utils.Directory,
|
||||
SqlFile: p.GetString("SQL_FILE"),
|
||||
Language: p.GetString("LANGUAGE"),
|
||||
SendReports: p.GetBool("ALLOW_REPORTS"),
|
||||
}
|
||||
log.WithFields(utils.ToFields(configs)).Debugln("read config file: " + configFile)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ type DbConfig struct {
|
|||
DbPort int `yaml:"port" json:"-"`
|
||||
ApiSecret string `yaml:"api_secret" json:"-"`
|
||||
Language string `yaml:"language" json:"language"`
|
||||
SendReports bool `yaml:"send_reports" json:"send_reports"`
|
||||
Project string `yaml:"-" json:"-"`
|
||||
Description string `yaml:"-" json:"-"`
|
||||
Domain string `yaml:"-" json:"-"`
|
||||
|
|
|
@ -24,12 +24,20 @@ func Find(method string) (*Notification, 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 err := db.Create(n).Error(); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -44,6 +52,8 @@ func (n *Notification) UpdateFields(notif *Notification) *Notification {
|
|||
n.ApiSecret = notif.ApiSecret
|
||||
n.Var1 = notif.Var1
|
||||
n.Var2 = notif.Var2
|
||||
n.SuccessData = notif.SuccessData
|
||||
n.FailureData = notif.FailureData
|
||||
return n
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ type Notification struct {
|
|||
Enabled null.NullBool `gorm:"column:enabled;type:boolean;default:false" json:"enabled,omitempty"`
|
||||
Limits int `gorm:"not null;column:limits" json:"limits"`
|
||||
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"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
Title string `gorm:"-" json:"title"`
|
||||
|
|
Loading…
Reference in New Issue