mirror of https://github.com/statping/statping
commit
d495bdd559
|
@ -319,7 +319,7 @@ jobs:
|
|||
dockerfile: Dockerfile.base
|
||||
tags: "base"
|
||||
|
||||
- name: Latest/Version Docker Image
|
||||
- name: Dev Docker Image
|
||||
uses: elgohr/Publish-Docker-Github-Action@master
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
|
|
|
@ -5,10 +5,9 @@ on:
|
|||
- '*' # matches every branch
|
||||
- '*/*' # matches every branch containing a single '/'
|
||||
- '!master' # excludes master
|
||||
- '!dev' # excludes dev
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
|
|
|
@ -13,6 +13,6 @@ jobs:
|
|||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: "This issue hasn't had any updates in a while. If this is still a problem, please create a new issue."
|
||||
stale-issue-label: "stale"
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
days-before-stale: 45
|
||||
days-before-close: 14
|
||||
exempt-issue-label: "bug,urgent,feature,pinned,locked"
|
||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -1,3 +1,21 @@
|
|||
# 0.90.56 (06-25-2020)
|
||||
- Modified metrics now include service name for each service metric
|
||||
- Added switch for true/false notifier values
|
||||
- Added list for notifiers that have static values (in drop down)
|
||||
- Fixed oAuth form saving
|
||||
- Fixed some HTTP Cookie issues
|
||||
- Added error if Theme Editor returns an error from API
|
||||
- Added Pushover priority and sounds
|
||||
- Added HTTP headers for outgoing requests (includes User-Agent=Statping and Statping-Version=0.90.55)
|
||||
- Fixed Google oAuth handling
|
||||
- Added Google oAuth email/domain user restrictions
|
||||
- Modified notifiers to use dereferenced services and failures
|
||||
- Added core.Example() function for testing
|
||||
- Added Custom oAuth Authentication method
|
||||
- Fixed setup form not creating user from values inputted in form
|
||||
- Fixed issues with Telegram Notifier
|
||||
- Modified notifier test handler to return notifier based on URL, not JSON payload
|
||||
|
||||
# 0.90.55 (06-18-2020)
|
||||
- Added 404 page
|
||||
- Modified Statping's PR process, dev -> master
|
||||
|
|
|
@ -3391,7 +3391,7 @@
|
|||
" var user = jsonData[0];",
|
||||
" pm.expect(user.id).to.eql(1);",
|
||||
" pm.expect(user.username).to.eql(\"admin\");",
|
||||
" pm.expect(user.email).to.eql(\"info@admin.com\");",
|
||||
" pm.expect(user.email).to.eql(\"info@domain.com\");",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
@ -4233,7 +4233,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"method\": \"success\",\n \"notifier\": {\n \"enabled\": false,\n \"limits\": 60,\n \"method\": \"slack\",\n \"host\": \"https://webhooksurl.slack.com/***\",\n \"success_data\": \"{\\n \\\"blocks\\\": [{\\n \\\"type\\\": \\\"section\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"The service {{.Service.Name}} is back online.\\\"\\n }\\n }, {\\n \\\"type\\\": \\\"actions\\\",\\n \\\"elements\\\": [{\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"View Service\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"style\\\": \\\"primary\\\",\\n \\\"url\\\": \\\"{{.Core.Domain}}/service/{{.Service.Id}}\\\"\\n }, {\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"Go to Statping\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"url\\\": \\\"{{.Core.Domain}}\\\"\\n }]\\n }]\\n}\",\n \"failure_data\": \"{\\n \\\"blocks\\\": [{\\n \\\"type\\\": \\\"section\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\":warning: The service {{.Service.Name}} is currently offline! :warning:\\\"\\n }\\n }, {\\n \\\"type\\\": \\\"divider\\\"\\n }, {\\n \\\"type\\\": \\\"section\\\",\\n \\\"fields\\\": [{\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Service:*\\\\n{{.Service.Name}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*URL:*\\\\n{{.Service.Domain}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Status Code:*\\\\n{{.Service.LastStatusCode}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*When:*\\\\n{{.Failure.CreatedAt}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Downtime:*\\\\n{{.Service.DowntimeAgo}}\\\"\\n }, {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"*Error:*\\\\n{{.Failure.Issue}}\\\"\\n }]\\n }, {\\n \\\"type\\\": \\\"divider\\\"\\n }, {\\n \\\"type\\\": \\\"actions\\\",\\n \\\"elements\\\": [{\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"View Offline Service\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"style\\\": \\\"danger\\\",\\n \\\"url\\\": \\\"{{.Core.Domain}}/service/{{.Service.Id}}\\\"\\n }, {\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"Go to Statping\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"url\\\": \\\"{{.Core.Domain}}\\\"\\n }]\\n }]\\n}\"\n }\n}",
|
||||
"raw": "{\n \"method\": \"success\",\n \"notifier\": {\n \"enabled\": false,\n \"limits\": 60,\n \"method\": \"slack\",\n \"host\": \"https://webhooksurl.slack.com/***\",\n \"success_data\": \"{\\n \\\"blocks\\\": [{\\n \\\"type\\\": \\\"section\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"The service {{.Service.Name}} is back online.\\\"\\n }\\n }, {\\n \\\"type\\\": \\\"actions\\\",\\n \\\"elements\\\": [{\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"View Service\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"style\\\": \\\"primary\\\",\\n \\\"url\\\": \\\"{{.Core.Domain}}/service/{{.Service.Id}}\\\"\\n }, {\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"Go to Statping\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"url\\\": \\\"{{.Core.Domain}}\\\"\\n }]\\n }]\\n}\",\n \"failure_data\": \"{\\n \\\"blocks\\\": [{\\n \\\"type\\\": \\\"section\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\":warning: The service {{.Service.Name}} is currently offline! :warning:\\\"\\n }\\n }, {\\n \\\"type\\\": \\\"divider\\\"\\n }, {\\n \\\"type\\\": \\\"section\\\",\\n \\\"fields\\\": [{\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Service:*\\\\n{{.Service.Name}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*URL:*\\\\n{{.Service.Domain}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Status Code:*\\\\n{{.Service.LastStatusCode}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*When:*\\\\n{{.Failure.CreatedAt}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Downtime:*\\\\n{{.Service.DowntimeAgo}}\\\"\\n }, {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"*Error:*\\\\n{{.Failure.Issue}}\\\"\\n }]\\n }, {\\n \\\"type\\\": \\\"divider\\\"\\n }, {\\n \\\"type\\\": \\\"actions\\\",\\n \\\"elements\\\": [{\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"View Offline Service\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"style\\\": \\\"danger\\\",\\n \\\"url\\\": \\\"{{.Core.Domain}}/service/{{.Service.Id}}\\\"\\n }, {\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"Go to Statping\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"url\\\": \\\"{{.Core.Domain}}\\\"\\n }]\\n }]\\n}\"\n }\n}",
|
||||
"options": {
|
||||
"raw": {}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ const errorReporter = "https://bed4d75404924cb3a799e370733a1b64@sentry.statping.
|
|||
|
||||
class Api {
|
||||
constructor() {
|
||||
|
||||
axios.defaults.withCredentials = true
|
||||
}
|
||||
|
||||
async oauth() {
|
||||
|
@ -251,17 +251,13 @@ class Api {
|
|||
}
|
||||
|
||||
token() {
|
||||
const tk = $cookies.get(tokenKey)
|
||||
if (!tk) {
|
||||
return {admin: false};
|
||||
}
|
||||
return tk;
|
||||
return $cookies.get(tokenKey);
|
||||
}
|
||||
|
||||
authToken() {
|
||||
const tk = $cookies.get(tokenKey)
|
||||
if (tk.token) {
|
||||
return {'Authorization': 'Bearer ' + tk.token};
|
||||
if (tk) {
|
||||
return {'Authorization': 'Bearer ' + tk};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -107,9 +107,14 @@
|
|||
},
|
||||
async createAssets() {
|
||||
this.pending = true
|
||||
const resp = await Api.theme_generate(true)
|
||||
let resp
|
||||
try {
|
||||
resp = await Api.theme_generate(true)
|
||||
} catch(e) {
|
||||
this.error = e.response.data.error
|
||||
}
|
||||
this.pending = false
|
||||
await this.fetchTheme()
|
||||
await this.fetchTheme()
|
||||
},
|
||||
async deleteAssets() {
|
||||
this.pending = true
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
this.$store.commit('setHasAllData', false)
|
||||
this.$store.commit('setToken', null)
|
||||
this.$store.commit('setAdmin', false)
|
||||
this.$cookies.remove("statping_auth")
|
||||
// this.$cookies.remove("statping_auth")
|
||||
await this.$router.push('/logout')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,16 @@
|
|||
<td>8080</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>{{"\{\{.Service.DowntimeAgo\}\}"}}</kbd></td>
|
||||
<td>35 minutes ago</td>
|
||||
<td><kbd>{{"\{\{.Service.Downtime.Human\}\}"}}</kbd></td>
|
||||
<td>2 minutes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>{{"\{\{.Service.Uptime.Human\}\}"}}</kbd></td>
|
||||
<td>13 hours</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>{{"\{\{.Service.Online\}\}"}}</kbd></td>
|
||||
<td>true/false</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>{{"\{\{.Service.LastStatusCode\}\}"}}</kbd></td>
|
||||
|
@ -49,6 +57,22 @@
|
|||
<td><kbd>{{"\{\{.Service.FailuresLast24Hours\}\}"}}</kbd></td>
|
||||
<td>38</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>{{"\{\{.Service.LastOnline\}\}"}}</kbd></td>
|
||||
<td>2020-11-05T13:15:30Z</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>{{"\{\{.Service.LastOffline\}\}"}}</kbd></td>
|
||||
<td>2020-10-01T13:15:30Z</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>{{"\{\{.Service.Online24Hours\}\}"}}</kbd></td>
|
||||
<td>0.99</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>{{"\{\{.Service.Online7Days\}\}"}}</kbd></td>
|
||||
<td>0.97</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<small>Additional variables within the Service struct</small>
|
||||
</table>
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
<font-awesome-icon :icon="['fab', 'google']" /> Login with Google
|
||||
</a>
|
||||
|
||||
<a v-if="oauth && oauth.custom_client_id" @click.prevent="Customlogin" href="#" class="btn btn-block btn-outline-dark">
|
||||
<font-awesome-icon :icon="['fas', 'address-card']" /> Login with {{oauth.custom_name}}
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -80,9 +84,8 @@
|
|||
if (auth.error) {
|
||||
this.error = true
|
||||
} else if (auth.token) {
|
||||
const u = {username: this.username, admin: auth.admin, token: auth.token}
|
||||
this.$cookies.set("statping_auth", JSON.stringify(u))
|
||||
this.$store.dispatch('loadAdmin')
|
||||
// this.$cookies.set("statping_auth", auth.token)
|
||||
await this.$store.dispatch('loadAdmin')
|
||||
this.$store.commit('setAdmin', auth.admin)
|
||||
this.$router.push('/dashboard')
|
||||
}
|
||||
|
@ -96,6 +99,9 @@
|
|||
},
|
||||
Googlelogin() {
|
||||
window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.core.domain}/oauth/google&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`
|
||||
},
|
||||
Customlogin() {
|
||||
window.location = `${this.oauth.custom_endpoint_auth}?client_id=${this.oauth.custom_client_id}&redirect_uri=${this.core.domain}/oauth/custom${this.oauth.custom_scopes !== "" ? "&scope="+this.oauth.custom_scopes : "" }`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,23 @@
|
|||
<div class="card-body">
|
||||
<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-if="notifier.method==='mobile'" class="col-6 offset-3">
|
||||
<img :src="qrcode" class="img-thumbnail">
|
||||
<span class="text-muted small center">Scan this QR Code on the Statping Mobile App for quick setup</span>
|
||||
</div>
|
||||
|
||||
<div v-if="notifier.method!=='mobile'" v-for="(form, index) in notifier.form" v-bind:key="index" class="form-group">
|
||||
<label class="text-capitalize">{{form.title}}</label>
|
||||
<input v-if="form.type === 'text' || 'number' || 'password'" v-model="notifier[form.field.toLowerCase()]" :type="form.type" class="form-control" :placeholder="form.placeholder" >
|
||||
<input v-if="formVisible(['text', 'number', 'password', 'email'], form)" v-model="notifier[form.field.toLowerCase()]" :type="form.type" class="form-control" :placeholder="form.placeholder" >
|
||||
|
||||
<select v-if="formVisible(['list'], form)" v-model="notifier[form.field.toLowerCase()]" class="form-control">
|
||||
<option v-for="(val, k) in form.list_options" :value="val">{{val}}</option>
|
||||
</select>
|
||||
|
||||
<span v-if="formVisible(['switch'], form)" @click="notifier[form.field.toLowerCase()] = !!notifier[form.field.toLowerCase()]" class="switch switch-rd-gr float-right mt-2">
|
||||
<input v-model="notifier[form.field.toLowerCase()]" type="checkbox" class="switch-sm" :id="`switch_${notifier.name}_${form.field}`" v-bind:checked="notifier[form.field.toLowerCase()]">
|
||||
<label class="mb-0" :for="`switch_${notifier.name}_${form.field}`"></label>
|
||||
</span>
|
||||
|
||||
<small class="form-text text-muted" v-html="form.small_text"></small>
|
||||
</div>
|
||||
|
@ -155,17 +169,26 @@ export default {
|
|||
theme: 'neat',
|
||||
mode: "mymode",
|
||||
lineWrapping: true,
|
||||
json: true,
|
||||
json: this.notifier.data_type === "json",
|
||||
autoRefresh: true,
|
||||
mime: this.notifier.data_type === "json" ? "application/json" : "text/plain"
|
||||
},
|
||||
beautifySettings: { indent_size: 2, space_in_empty_paren: true },
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
qrcode() {
|
||||
const u = `statping://setup?domain=${this.core.domain}&api=${this.core.api_secret}`
|
||||
return "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURIComponent(u)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formVisible(want, form) {
|
||||
return !!want.includes(form.type);
|
||||
},
|
||||
visible(isVisible, entry) {
|
||||
if (isVisible) {
|
||||
this.$refs.cmfailure.codemirror.refresh()
|
||||
|
@ -173,13 +196,19 @@ export default {
|
|||
}
|
||||
},
|
||||
onCmSuccessReady(cm) {
|
||||
this.success_data = beautify(this.notifier.success_data, this.beautifySettings)
|
||||
this.success_data = this.notifier.success_data
|
||||
if (this.notifier.data_type === "json") {
|
||||
this.success_data = beautify(this.notifier.success_data, this.beautifySettings)
|
||||
}
|
||||
setTimeout(function() {
|
||||
cm.refresh();
|
||||
},1);
|
||||
},
|
||||
onCmFailureReady(cm) {
|
||||
this.failure_data = beautify(this.notifier.failure_data, this.beautifySettings)
|
||||
this.failure_data = this.notifier.failure_data
|
||||
if (this.notifier.data_type === "json") {
|
||||
this.failure_data = beautify(this.notifier.failure_data, this.beautifySettings)
|
||||
}
|
||||
setTimeout(function() {
|
||||
cm.refresh();
|
||||
},1);
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
<template>
|
||||
<form @submit.prevent="saveOAuth">
|
||||
{{core.oauth}}
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Internal Login</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label for="switch-gh-oauth" class="col-sm-4 col-form-label">OAuth Login Settings</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<label for="switch-gh-oauth" class="col-sm-6 col-form-label">Statping Authentication</label>
|
||||
<div class="col-md-6 col-xs-12 mt-1">
|
||||
<span @click="local_enabled = !!local_enabled" class="switch float-left">
|
||||
<input v-model="local_enabled" type="checkbox" class="switch" id="switch-local-oauth" :checked="local_enabled">
|
||||
<label for="switch-local-oauth">Use email/password Authentication</label>
|
||||
<label for="switch-local-oauth"></label>
|
||||
<span class="small d-block">Use email/password Authentication</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="whitelist_domains" class="col-sm-4 col-form-label">Whitelist Domains</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.oauth_domains" type="text" class="form-control" placeholder="domain.com" id="whitelist_domains">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
|
@ -38,6 +32,20 @@
|
|||
<input v-model="oauth.gh_client_secret" type="text" class="form-control" id="github_secret" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="github_secret" class="col-sm-4 col-form-label">Restrict Users</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.gh_users" type="text" class="form-control" id="github_users" placeholder="octocat,hunterlong,jimbo123">
|
||||
<small>Optional comma delimited list of usernames</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="github_secret" class="col-sm-4 col-form-label">Restrict Organizations</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.gh_orgs" type="text" class="form-control" id="github_orgs" placeholder="statping,github">
|
||||
<small>Optional comma delimited list of Github Organizations</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-gh-oauth" class="col-sm-4 col-form-label">Enable Github Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
|
@ -63,7 +71,7 @@
|
|||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Google Settings</div>
|
||||
<div class="card-body">
|
||||
<span>Go to <a href="https://console.cloud.google.com/apis/credentials">OAuth Consent Screen</a> on Google Console to create a new OAuth application.</span>
|
||||
<span>Go to <a href="https://console.cloud.google.com/apis/credentials">OAuth Consent Screen</a> on Google Console to create a new "Web Application" OAuth application. </span>
|
||||
|
||||
<div class="form-group row mt-3">
|
||||
<label for="github_client" class="col-sm-4 col-form-label">Google Client ID</label>
|
||||
|
@ -77,6 +85,13 @@
|
|||
<input v-model="oauth.google_client_secret" type="text" class="form-control" id="google_secret" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="github_secret" class="col-sm-4 col-form-label">Restrict Users</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.google_users" type="text" class="form-control" id="google_users" placeholder="info@gmail.com,example.com">
|
||||
<small>Optional comma delimited list of emails and/or domains</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-google-oauth" class="col-sm-4 col-form-label">Enable Google Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
|
@ -102,7 +117,7 @@
|
|||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Slack Settings</div>
|
||||
<div class="card-body">
|
||||
<span>Go to <a href="https://console.cloud.google.com/apis/credentials">OAuth Consent Screen</a> on Google Console to create a new OAuth application.</span>
|
||||
<span>Go to <a href="https://api.slack.com/apps">Slack Apps</a> and create a new Application.</span>
|
||||
|
||||
<div class="form-group row mt-3">
|
||||
<label for="slack_client" class="col-sm-4 col-form-label">Slack Client ID</label>
|
||||
|
@ -117,10 +132,17 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="slack_secret" class="col-sm-4 col-form-label">Slack Team ID</label>
|
||||
<label for="slack_secret" class="col-sm-4 col-form-label">Team ID</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.slack_team" type="text" class="form-control" id="slack_team">
|
||||
<small>Optional</small>
|
||||
<small>Optional Slack Team ID</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="slack_secret" class="col-sm-4 col-form-label">Restrict Users</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.slack_users" type="text" class="form-control" id="slack_users" placeholder="info@example.com,info@domain.net">
|
||||
<small>Optional comma delimited list of email addresses</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
|
@ -146,8 +168,72 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-block" @click.prevent="saveOAuth" type="submit">
|
||||
Save OAuth Settings
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Custom oAuth Settings</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row mt-3">
|
||||
<label for="custom_name" class="col-sm-4 col-form-label">Custom Name</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_name" type="text" class="form-control" id="custom_name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row mt-3">
|
||||
<label for="custom_client" class="col-sm-4 col-form-label">Client ID</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_client_id" type="text" class="form-control" id="custom_client" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="custom_secret" class="col-sm-4 col-form-label">Client Secret</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_client_secret" type="text" class="form-control" id="custom_secret" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="custom_endpoint" class="col-sm-4 col-form-label">Auth Endpoint</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_endpoint_auth" type="text" class="form-control" id="custom_endpoint" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="custom_endpoint_token" class="col-sm-4 col-form-label">Token Endpoint</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_endpoint_token" type="text" class="form-control" id="custom_endpoint_token" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="custom_scopes" class="col-sm-4 col-form-label">Scopes</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_scopes" type="text" class="form-control" id="custom_scopes">
|
||||
<small>Optional comma delimited list of oauth scopes</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-custom-oauth" class="col-sm-4 col-form-label">Enable Custom Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="custom_enabled = !!custom_enabled" class="switch float-left">
|
||||
<input v-model="custom_enabled" type="checkbox" class="switch" id="switch-custom-oauth" :checked="custom_enabled">
|
||||
<label for="switch-custom-oauth"> </label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="slack_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input v-bind:value="`${core.domain}/oauth/custom`" type="text" class="form-control" id="custom_callback" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/oauth/custom`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button class="btn btn-primary btn-block" @click.prevent="saveOAuth" type="submit" :disabled="loading">
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/> Save OAuth Settings
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
@ -162,9 +248,6 @@
|
|||
core() {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
auth() {
|
||||
return this.$store.getters.oauth
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -172,25 +255,37 @@
|
|||
slack_enabled: false,
|
||||
github_enabled: false,
|
||||
local_enabled: false,
|
||||
custom_enabled: false,
|
||||
loading: false,
|
||||
oauth: {
|
||||
gh_client_id: "",
|
||||
gh_client_secret: "",
|
||||
gh_users: "",
|
||||
gh_orgs: "",
|
||||
google_client_id: "",
|
||||
google_client_secret: "",
|
||||
oauth_domains: "",
|
||||
google_users: "",
|
||||
oauth_providers: "",
|
||||
slack_client_id: "",
|
||||
slack_client_secret: "",
|
||||
slack_team: ""
|
||||
slack_team: "",
|
||||
slack_users: "",
|
||||
custom_name: "",
|
||||
custom_client_id: "",
|
||||
custom_client_secret: "",
|
||||
custom_endpoint_auth: "",
|
||||
custom_endpoint_token: "",
|
||||
custom_scopes: "",
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.oauth = this.auth
|
||||
async mounted() {
|
||||
this.oauth = await Api.oauth()
|
||||
this.local_enabled = this.has('local')
|
||||
this.github_enabled = this.has('github')
|
||||
this.google_enabled = this.has('google')
|
||||
this.slack_enabled = this.has('slack')
|
||||
this.custom_enabled = this.has('custom')
|
||||
},
|
||||
methods: {
|
||||
providers() {
|
||||
|
@ -207,6 +302,9 @@
|
|||
if (this.slack_enabled) {
|
||||
providers.push("slack")
|
||||
}
|
||||
if (this.custom_enabled) {
|
||||
providers.push("custom")
|
||||
}
|
||||
return providers.join(",")
|
||||
},
|
||||
has(val) {
|
||||
|
@ -216,12 +314,12 @@
|
|||
return this.oauth.oauth_providers.split(",").includes(val)
|
||||
},
|
||||
async saveOAuth() {
|
||||
let c = this.core
|
||||
c.oauth = this.oauth
|
||||
c.oauth.oauth_providers = this.providers()
|
||||
await Api.oauth_save(c)
|
||||
this.loading = true
|
||||
this.oauth.oauth_providers = this.providers()
|
||||
await Api.oauth_save(this.oauth)
|
||||
const oauth = await Api.oauth()
|
||||
this.$store.commit('setOAuth', oauth)
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,12 +74,12 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.project_name') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.project" id="project" type="text" class="form-control" placeholder="Great Uptime" required>
|
||||
<input @keyup="canSubmit" v-model="setup.project" id="project" type="text" class="form-control" placeholder="Work Servers" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.project_description') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.description" id="description" type="text" class="form-control" placeholder="Great Uptime">
|
||||
<input @keyup="canSubmit" v-model="setup.description" id="description" type="text" class="form-control" placeholder="Monitors all of my work services">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -100,6 +100,7 @@
|
|||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.password_confirm') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.confirm_password" id="password_confirm" type="password" class="form-control" placeholder="password" required>
|
||||
<span v-if="passnomatch" class="small text-danger">Both passwords should match</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -108,7 +109,7 @@
|
|||
<label class="text-capitalize">{{ $t('email') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.email" id="email" type="text" class="form-control" placeholder="myemail@domain.com">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="col-4 text-right">
|
||||
<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="send_newsletter" class="switch" id="send_newsletter" :checked="setup.newsletter">
|
||||
|
@ -145,6 +146,7 @@
|
|||
error: null,
|
||||
loading: false,
|
||||
disabled: true,
|
||||
passnomatch: false,
|
||||
setup: {
|
||||
language: "en",
|
||||
db_connection: "sqlite",
|
||||
|
@ -186,6 +188,11 @@
|
|||
canSubmit() {
|
||||
this.error = null
|
||||
const s = this.setup
|
||||
if (s.confirm_password.length > 0 && s.confirm_password !== s.password) {
|
||||
this.passnomatch = true
|
||||
} else {
|
||||
this.passnomatch = false
|
||||
}
|
||||
if (s.db_connection !== 'sqlite') {
|
||||
if (!s.db_host || !s.db_port || !s.db_user || !s.db_password || !s.db_database) {
|
||||
this.disabled = true
|
||||
|
|
|
@ -38,7 +38,7 @@ const routes = [
|
|||
beforeEnter: async (to, from, next) => {
|
||||
if (to.matched.some(record => record.meta.requiresAuth)) {
|
||||
let tk = await Api.token()
|
||||
if (to.path !== '/login' && !tk.admin) {
|
||||
if (to.path !== '/login' && !tk) {
|
||||
next('/login')
|
||||
return
|
||||
}
|
||||
|
|
|
@ -147,9 +147,9 @@ export default new Vuex.Store({
|
|||
const core = await Api.core()
|
||||
const token = await Api.token()
|
||||
context.commit("setCore", core);
|
||||
context.commit('setAdmin', token.admin)
|
||||
context.commit('setAdmin', token)
|
||||
context.commit('setCore', core)
|
||||
context.commit('setUser', token.token!==undefined)
|
||||
context.commit('setUser', token!==undefined)
|
||||
},
|
||||
async loadRequired(context) {
|
||||
const groups = await Api.groups()
|
||||
|
@ -170,14 +170,8 @@ export default new Vuex.Store({
|
|||
const messages = await Api.messages()
|
||||
context.commit("setMessages", messages)
|
||||
context.commit("setHasPublicData", true)
|
||||
try {
|
||||
const checkins = await Api.checkins()
|
||||
context.commit("setCheckins", checkins);
|
||||
} catch(e) {
|
||||
window.console.error(e)
|
||||
await Api.logout()
|
||||
return
|
||||
}
|
||||
const checkins = await Api.checkins()
|
||||
context.commit("setCheckins", checkins);
|
||||
const notifiers = await Api.notifiers()
|
||||
context.commit("setNotifiers", notifiers);
|
||||
const users = await Api.users()
|
||||
|
|
|
@ -3,14 +3,23 @@ module.exports = {
|
|||
assetsDir: 'assets',
|
||||
filenameHashing: false,
|
||||
devServer: {
|
||||
proxy: {
|
||||
disableHostCheck: true,
|
||||
proxyTable: {
|
||||
'/api': {
|
||||
logLevel: 'debug',
|
||||
target: 'http://0.0.0.0:8585'
|
||||
target: 'http://0.0.0.0:8585',
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/api': ''
|
||||
}
|
||||
},
|
||||
'/oauth': {
|
||||
logLevel: 'debug',
|
||||
target: 'http://0.0.0.0:8585/oauth/'
|
||||
target: 'http://0.0.0.0:8585',
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/oauth': ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,15 +45,19 @@ func apiRenewHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiUpdateOAuthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var c core.OAuth
|
||||
err := DecodeJSON(r, &c)
|
||||
if err != nil {
|
||||
var oauth core.OAuth
|
||||
if err := DecodeJSON(r, &oauth); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
app := core.App
|
||||
app.OAuth = c
|
||||
sendJsonAction(app.OAuth, "update", w, r)
|
||||
|
||||
core.App.OAuth = oauth
|
||||
if err := core.App.Update(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
sendJsonAction(core.App.OAuth, "update", w, r)
|
||||
}
|
||||
|
||||
func apiOAuthHandler(r *http.Request) interface{} {
|
||||
|
|
|
@ -19,7 +19,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
jwtKey string
|
||||
jwtKey []byte
|
||||
httpServer *http.Server
|
||||
usingSSL bool
|
||||
mainTmpl = `{{define "main" }} {{ template "base" . }} {{ end }}`
|
||||
|
@ -96,7 +96,11 @@ func IsFullAuthenticated(r *http.Request) bool {
|
|||
if ok := hasAuthorizationHeader(r); ok {
|
||||
return true
|
||||
}
|
||||
return IsAdmin(r)
|
||||
claim, err := getJwtToken(r)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return claim.Admin
|
||||
}
|
||||
|
||||
// ScopeName will show private JSON fields in the API.
|
||||
|
|
|
@ -33,7 +33,7 @@ func setJwtToken(user *users.User, w http.ResponseWriter) (JwtClaim, string) {
|
|||
ExpiresAt: expirationTime.Unix(),
|
||||
}}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwtClaim)
|
||||
tokenString, err := token.SignedString([]byte(jwtKey))
|
||||
tokenString, err := token.SignedString(jwtKey)
|
||||
if err != nil {
|
||||
log.Errorln("error setting token: ", err)
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ func setJwtToken(user *users.User, w http.ResponseWriter) (JwtClaim, string) {
|
|||
Value: tokenString,
|
||||
Expires: expirationTime,
|
||||
MaxAge: int(time.Duration(72 * time.Hour).Seconds()),
|
||||
Path: "/",
|
||||
})
|
||||
return jwtClaim, tokenString
|
||||
}
|
||||
|
@ -56,11 +57,12 @@ func getJwtToken(r *http.Request) (JwtClaim, error) {
|
|||
}
|
||||
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
|
||||
tkn, err := jwt.ParseWithClaims(c.Value, &claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return jwtKey, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if err == jwt.ErrSignatureInvalid {
|
||||
return JwtClaim{}, err
|
||||
|
|
|
@ -70,7 +70,7 @@ type testNotificationReq struct {
|
|||
|
||||
func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
_, err := notifications.Find(vars["notifier"])
|
||||
n, err := notifications.Find(vars["notifier"])
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
@ -82,7 +82,7 @@ func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
notif := services.ReturnNotifier(req.Notification.Method)
|
||||
notif := services.ReturnNotifier(n.Method)
|
||||
|
||||
var out string
|
||||
if req.Method == "success" {
|
||||
|
|
|
@ -1,29 +1,20 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/null"
|
||||
"github.com/statping/statping/types/users"
|
||||
"github.com/statping/statping/utils"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/github"
|
||||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/oauth2/slack"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type oAuth struct {
|
||||
ID string
|
||||
Email string
|
||||
Username string
|
||||
Token string
|
||||
RefreshToken string
|
||||
Valid bool
|
||||
Type string
|
||||
Email string
|
||||
Username string
|
||||
*oauth2.Token
|
||||
}
|
||||
|
||||
func oauthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -39,6 +30,10 @@ func oauthHandler(w http.ResponseWriter, r *http.Request) {
|
|||
oauth, err = githubOAuth(r)
|
||||
case "slack":
|
||||
oauth, err = slackOAuth(r)
|
||||
case "custom":
|
||||
oauth, err = customOAuth(r)
|
||||
default:
|
||||
err = errors.New("unknown oauth provider")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -51,122 +46,14 @@ func oauthHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func oauthLogin(oauth *oAuth, w http.ResponseWriter, r *http.Request) {
|
||||
log.Infoln(oauth)
|
||||
user := &users.User{
|
||||
Id: 0,
|
||||
Username: oauth.Username,
|
||||
Email: oauth.Email,
|
||||
Admin: null.NewNullBool(true),
|
||||
}
|
||||
log.Infoln(fmt.Sprintf("OAuth User %s logged in from IP %s", oauth.Email, r.RemoteAddr))
|
||||
log.Infoln(fmt.Sprintf("OAuth %s User %s logged in from IP %s", oauth.Type(), oauth.Email, r.RemoteAddr))
|
||||
setJwtToken(user, w)
|
||||
|
||||
//returnJson(user, w, r)
|
||||
http.Redirect(w, r, core.App.Domain, http.StatusPermanentRedirect)
|
||||
}
|
||||
|
||||
func githubOAuth(r *http.Request) (*oAuth, error) {
|
||||
c := *core.App
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: c.OAuth.GithubClientID,
|
||||
ClientSecret: c.OAuth.GithubClientSecret,
|
||||
Endpoint: github.Endpoint,
|
||||
}
|
||||
|
||||
gg, err := config.Exchange(r.Context(), code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oAuth{
|
||||
Token: gg.AccessToken,
|
||||
RefreshToken: gg.RefreshToken,
|
||||
Valid: gg.Valid(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func googleOAuth(r *http.Request) (*oAuth, error) {
|
||||
c := core.App
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: c.OAuth.GoogleClientID,
|
||||
ClientSecret: c.OAuth.GoogleClientSecret,
|
||||
Endpoint: google.Endpoint,
|
||||
}
|
||||
|
||||
gg, err := config.Exchange(r.Context(), code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oAuth{
|
||||
Token: gg.AccessToken,
|
||||
RefreshToken: gg.RefreshToken,
|
||||
Valid: gg.Valid(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func slackOAuth(r *http.Request) (*oAuth, error) {
|
||||
c := core.App
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: c.OAuth.SlackClientID,
|
||||
ClientSecret: c.OAuth.SlackClientSecret,
|
||||
Endpoint: slack.Endpoint,
|
||||
RedirectURL: c.Domain + basePath + "oauth/slack",
|
||||
Scopes: []string{"identity.basic"},
|
||||
}
|
||||
|
||||
gg, err := config.Exchange(r.Context(), code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oauther := &oAuth{
|
||||
Token: gg.AccessToken,
|
||||
RefreshToken: gg.RefreshToken,
|
||||
Valid: gg.Valid(),
|
||||
Type: gg.Type(),
|
||||
}
|
||||
|
||||
return oauther.slackIdentity()
|
||||
}
|
||||
|
||||
// slackIdentity will query the Slack API to fetch the users ID, username, and email address.
|
||||
func (a *oAuth) slackIdentity() (*oAuth, error) {
|
||||
url := fmt.Sprintf("https://slack.com/api/users.identity?token=%s", a.Token)
|
||||
out, resp, err := utils.HttpRequest(url, "GET", "application/x-www-form-urlencoded", nil, nil, 10*time.Second, true, nil)
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var i *slackIdentity
|
||||
if err := json.Unmarshal(out, &i); err != nil {
|
||||
return a, err
|
||||
}
|
||||
a.Email = i.User.Email
|
||||
a.ID = i.User.ID
|
||||
a.Username = i.User.Name
|
||||
return a, nil
|
||||
}
|
||||
|
||||
type slackIdentity struct {
|
||||
Ok bool `json:"ok"`
|
||||
User struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
} `json:"user"`
|
||||
Team struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"team"`
|
||||
}
|
||||
|
||||
func secureToken(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
http.Redirect(w, r, core.App.Domain+"/dashboard", http.StatusPermanentRedirect)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"golang.org/x/oauth2"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func customOAuth(r *http.Request) (*oAuth, error) {
|
||||
auth := core.App.OAuth
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
scopes := strings.Split(auth.CustomScopes, ",")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: auth.CustomClientID,
|
||||
ClientSecret: auth.CustomClientSecret,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: auth.CustomEndpointAuth,
|
||||
TokenURL: auth.CustomEndpointToken,
|
||||
},
|
||||
RedirectURL: core.App.Domain + basePath + "oauth/custom",
|
||||
Scopes: scopes,
|
||||
}
|
||||
|
||||
gg, err := config.Exchange(r.Context(), code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !gg.Valid() {
|
||||
return nil, errors.New("oauth token is not valid")
|
||||
}
|
||||
|
||||
return &oAuth{
|
||||
Token: gg,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/github"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func githubOAuth(r *http.Request) (*oAuth, error) {
|
||||
auth := core.App.OAuth
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: auth.GithubClientID,
|
||||
ClientSecret: auth.GithubClientSecret,
|
||||
Endpoint: github.Endpoint,
|
||||
RedirectURL: core.App.Domain + basePath + "oauth/github",
|
||||
}
|
||||
|
||||
gg, err := config.Exchange(r.Context(), code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !gg.Valid() {
|
||||
return nil, errors.New("oauth token is not valid")
|
||||
}
|
||||
|
||||
user, err := returnGithubUser(gg.AccessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgs, err := returnGithubOrganizations(gg.AccessToken, user.Login)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !validateGithub(user, orgs) {
|
||||
return nil, errors.New("github user is not allowed to login")
|
||||
}
|
||||
|
||||
return &oAuth{
|
||||
Token: gg,
|
||||
Username: strings.ToLower(user.Name),
|
||||
Email: strings.ToLower(user.Email),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func returnGithubUser(token string) (githubUser, error) {
|
||||
headers := []string{
|
||||
"Accept=application/vnd.github.machine-man-preview+json",
|
||||
"Authorization=token " + token,
|
||||
}
|
||||
resp, _, err := utils.HttpRequest("https://api.github.com/user", "GET", nil, headers, nil, 10*time.Second, true, nil)
|
||||
if err != nil {
|
||||
return githubUser{}, err
|
||||
}
|
||||
var user githubUser
|
||||
if err := json.Unmarshal(resp, &user); err != nil {
|
||||
return githubUser{}, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func returnGithubOrganizations(token, username string) ([]githubOrgs, error) {
|
||||
headers := []string{
|
||||
"Accept=application/vnd.github.machine-man-preview+json",
|
||||
"Authorization=token " + token,
|
||||
}
|
||||
resp, _, err := utils.HttpRequest("https://api.github.com/users/"+username+"/orgs", "GET", nil, headers, nil, 10*time.Second, true, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var orgs []githubOrgs
|
||||
if err := json.Unmarshal(resp, &orgs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orgs, nil
|
||||
}
|
||||
|
||||
func validateGithub(ghUser githubUser, orgs []githubOrgs) bool {
|
||||
auth := core.App.OAuth
|
||||
if auth.GithubUsers == "" && auth.GithubOrgs == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
if auth.GithubUsers != "" {
|
||||
users := strings.Split(auth.GithubUsers, ",")
|
||||
for _, u := range users {
|
||||
if strings.ToLower(ghUser.Login) == strings.ToLower(u) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if auth.GithubOrgs != "" {
|
||||
orgsAllowed := strings.Split(auth.GithubOrgs, ",")
|
||||
for _, o := range orgsAllowed {
|
||||
for _, org := range orgs {
|
||||
if strings.ToLower(o) == strings.ToLower(org.Login) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type githubOrgs struct {
|
||||
Login string `json:"login"`
|
||||
ID int `json:"id"`
|
||||
NodeID string `json:"node_id"`
|
||||
URL string `json:"url"`
|
||||
ReposURL string `json:"repos_url"`
|
||||
EventsURL string `json:"events_url"`
|
||||
HooksURL string `json:"hooks_url"`
|
||||
IssuesURL string `json:"issues_url"`
|
||||
MembersURL string `json:"members_url"`
|
||||
PublicMembersURL string `json:"public_members_url"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type githubUser struct {
|
||||
Login string `json:"login"`
|
||||
ID int `json:"id"`
|
||||
NodeID string `json:"node_id"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
GravatarID string `json:"gravatar_id"`
|
||||
URL string `json:"url"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
FollowersURL string `json:"followers_url"`
|
||||
FollowingURL string `json:"following_url"`
|
||||
GistsURL string `json:"gists_url"`
|
||||
StarredURL string `json:"starred_url"`
|
||||
SubscriptionsURL string `json:"subscriptions_url"`
|
||||
OrganizationsURL string `json:"organizations_url"`
|
||||
ReposURL string `json:"repos_url"`
|
||||
EventsURL string `json:"events_url"`
|
||||
ReceivedEventsURL string `json:"received_events_url"`
|
||||
Type string `json:"type"`
|
||||
SiteAdmin bool `json:"site_admin"`
|
||||
Name string `json:"name"`
|
||||
Company string `json:"company"`
|
||||
Blog string `json:"blog"`
|
||||
Location string `json:"location"`
|
||||
Email string `json:"email"`
|
||||
Hireable bool `json:"hireable"`
|
||||
Bio string `json:"bio"`
|
||||
TwitterUsername string `json:"twitter_username"`
|
||||
PublicRepos int `json:"public_repos"`
|
||||
PublicGists int `json:"public_gists"`
|
||||
Followers int `json:"followers"`
|
||||
Following int `json:"following"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
PrivateGists int `json:"private_gists"`
|
||||
TotalPrivateRepos int `json:"total_private_repos"`
|
||||
OwnedPrivateRepos int `json:"owned_private_repos"`
|
||||
DiskUsage int `json:"disk_usage"`
|
||||
Collaborators int `json:"collaborators"`
|
||||
TwoFactorAuthentication bool `json:"two_factor_authentication"`
|
||||
Plan struct {
|
||||
Name string `json:"name"`
|
||||
Space int `json:"space"`
|
||||
PrivateRepos int `json:"private_repos"`
|
||||
Collaborators int `json:"collaborators"`
|
||||
} `json:"plan"`
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func googleOAuth(r *http.Request) (*oAuth, error) {
|
||||
auth := core.App.OAuth
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: auth.GoogleClientID,
|
||||
ClientSecret: auth.GoogleClientSecret,
|
||||
Endpoint: google.Endpoint,
|
||||
RedirectURL: core.App.Domain + basePath + "oauth/google",
|
||||
}
|
||||
|
||||
gg, err := config.Exchange(r.Context(), code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !gg.Valid() {
|
||||
return nil, errors.New("oauth token is not valid")
|
||||
}
|
||||
|
||||
info, err := returnGoogleInfo(gg.AccessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !validateGoogle(info) {
|
||||
return nil, errors.New("google user is not allowed to login")
|
||||
}
|
||||
|
||||
return &oAuth{
|
||||
Token: gg,
|
||||
Username: info.Name,
|
||||
Email: info.Email,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateGoogle(info googleUserInfo) bool {
|
||||
auth := core.App.OAuth
|
||||
if auth.GoogleUsers == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
if auth.GoogleUsers != "" {
|
||||
users := strings.Split(auth.GoogleUsers, ",")
|
||||
for _, u := range users {
|
||||
if strings.ToLower(info.Email) == strings.ToLower(u) {
|
||||
return true
|
||||
}
|
||||
if strings.ToLower(info.Hd) == strings.ToLower(u) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func returnGoogleInfo(token string) (googleUserInfo, error) {
|
||||
resp, _, err := utils.HttpRequest("https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token="+token, "GET", nil, nil, nil, 10*time.Second, true, nil)
|
||||
if err != nil {
|
||||
return googleUserInfo{}, err
|
||||
}
|
||||
var user googleUserInfo
|
||||
if err := json.Unmarshal(resp, &user); err != nil {
|
||||
return googleUserInfo{}, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
type googleUserInfo struct {
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
VerifiedEmail bool `json:"verified_email"`
|
||||
Name string `json:"name"`
|
||||
GivenName string `json:"given_name"`
|
||||
FamilyName string `json:"family_name"`
|
||||
Link string `json:"link"`
|
||||
Picture string `json:"picture"`
|
||||
Gender string `json:"gender"`
|
||||
Locale string `json:"locale"`
|
||||
Hd string `json:"hd"`
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/slack"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func slackOAuth(r *http.Request) (*oAuth, error) {
|
||||
auth := core.App.OAuth
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: auth.SlackClientID,
|
||||
ClientSecret: auth.SlackClientSecret,
|
||||
Endpoint: slack.Endpoint,
|
||||
RedirectURL: core.App.Domain + basePath + "oauth/slack",
|
||||
Scopes: []string{"identity.basic"},
|
||||
}
|
||||
|
||||
gg, err := config.Exchange(r.Context(), code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !gg.Valid() {
|
||||
return nil, errors.New("oauth token is not valid")
|
||||
}
|
||||
|
||||
identity, err := returnSlackIdentity(gg.AccessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !identity.Ok {
|
||||
return nil, errors.New("slack identity is invalid")
|
||||
}
|
||||
|
||||
if !validateSlack(identity) {
|
||||
return nil, errors.New("slack user is not whitelisted")
|
||||
}
|
||||
|
||||
return &oAuth{
|
||||
Token: gg,
|
||||
Username: strings.ToLower(identity.User.Name),
|
||||
Email: strings.ToLower(identity.User.Email),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateSlack(id slackIdentity) bool {
|
||||
auth := core.App.OAuth
|
||||
if auth.SlackUsers == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
if auth.SlackUsers != "" {
|
||||
users := strings.Split(auth.SlackUsers, ",")
|
||||
for _, u := range users {
|
||||
if strings.ToLower(u) == strings.ToLower(id.User.Email) {
|
||||
return true
|
||||
}
|
||||
if strings.ToLower(u) == strings.ToLower(id.User.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// slackIdentity will query the Slack API to fetch the users ID, username, and email address.
|
||||
func returnSlackIdentity(token string) (slackIdentity, error) {
|
||||
url := fmt.Sprintf("https://slack.com/api/users.identity?token=%s", token)
|
||||
out, _, err := utils.HttpRequest(url, "GET", "application/x-www-form-urlencoded", nil, nil, 10*time.Second, true, nil)
|
||||
if err != nil {
|
||||
return slackIdentity{}, err
|
||||
}
|
||||
|
||||
var i slackIdentity
|
||||
if err := json.Unmarshal(out, &i); err != nil {
|
||||
return slackIdentity{}, err
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
type slackIdentity struct {
|
||||
Ok bool `json:"ok"`
|
||||
User struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
} `json:"user"`
|
||||
Team struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"team"`
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/statping/statping/source"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/utils"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
|
@ -192,5 +190,5 @@ func resetRouter() {
|
|||
}
|
||||
|
||||
func resetCookies() {
|
||||
jwtKey = fmt.Sprintf("%s_%d", core.App.ApiSecret, utils.Now().Nanosecond())
|
||||
jwtKey = []byte(utils.NewSHA256Hash())
|
||||
}
|
||||
|
|
|
@ -31,10 +31,8 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
project := r.PostForm.Get("project")
|
||||
description := r.PostForm.Get("description")
|
||||
domain := r.PostForm.Get("domain")
|
||||
newsletter := r.PostForm.Get("newsletter")
|
||||
sendNews, _ := strconv.ParseBool(newsletter)
|
||||
reports := r.PostForm.Get("send_reports")
|
||||
sendReports, _ := strconv.ParseBool(reports)
|
||||
sendNews, _ := strconv.ParseBool(r.PostForm.Get("newsletter"))
|
||||
sendReports, _ := strconv.ParseBool(r.PostForm.Get("send_reports"))
|
||||
|
||||
log.WithFields(utils.ToFields(core.App, confgs)).Debugln("new configs posted")
|
||||
|
||||
|
|
|
@ -49,14 +49,14 @@ func runCommand(app string, cmd ...string) (string, string, error) {
|
|||
}
|
||||
|
||||
// OnSuccess for commandLine will trigger successful service
|
||||
func (c *commandLine) OnSuccess(s *services.Service) (string, error) {
|
||||
tmpl := ReplaceVars(c.SuccessData, s, nil)
|
||||
func (c *commandLine) OnSuccess(s services.Service) (string, error) {
|
||||
tmpl := ReplaceVars(c.SuccessData, s, failures.Failure{})
|
||||
out, _, err := runCommand(c.Host, tmpl)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnFailure for commandLine will trigger failing service
|
||||
func (c *commandLine) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
func (c *commandLine) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
tmpl := ReplaceVars(c.FailureData, s, f)
|
||||
_, ouerr, err := runCommand(c.Host, tmpl)
|
||||
return ouerr, err
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -21,6 +22,7 @@ func TestCommandNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
t.Run("Load Command", func(t *testing.T) {
|
||||
Command.Host = "/bin/echo"
|
||||
|
@ -40,6 +42,11 @@ func TestCommandNotifier(t *testing.T) {
|
|||
assert.True(t, Command.CanSend())
|
||||
})
|
||||
|
||||
t.Run("Command OnSave", func(t *testing.T) {
|
||||
_, err := Command.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Command OnFailure", func(t *testing.T) {
|
||||
_, err := Command.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -51,14 +51,14 @@ func (d *discord) Select() *notifications.Notification {
|
|||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (d *discord) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
func (d *discord) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
out, err := d.sendRequest(ReplaceVars(d.FailureData, s, f))
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (d *discord) OnSuccess(s *services.Service) (string, error) {
|
||||
out, err := d.sendRequest(ReplaceVars(d.SuccessData, s, nil))
|
||||
func (d *discord) OnSuccess(s services.Service) (string, error) {
|
||||
out, err := d.sendRequest(ReplaceVars(d.SuccessData, s, failures.Failure{}))
|
||||
return out, err
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -26,6 +27,7 @@ func TestDiscordNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
if DISCORD_URL == "" {
|
||||
t.Log("discord notifier testing skipped, missing DISCORD_URL environment variable")
|
||||
|
@ -47,6 +49,11 @@ func TestDiscordNotifier(t *testing.T) {
|
|||
assert.True(t, Discorder.CanSend())
|
||||
})
|
||||
|
||||
t.Run("discord Notifier Tester OnSave", func(t *testing.T) {
|
||||
_, err := Discorder.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("discord OnFailure", func(t *testing.T) {
|
||||
_, err := Discorder.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -70,7 +70,7 @@ var email = &emailer{¬ifications.Notification{
|
|||
Type: "switch",
|
||||
Title: "Disable TLS/SSL",
|
||||
Placeholder: "",
|
||||
SmallText: "To Disable TLS/SSL insert 'true'",
|
||||
SmallText: "Enabling this will set Insecure Skip Verify to true",
|
||||
DbField: "api_key",
|
||||
}}},
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ type emailOutgoing struct {
|
|||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (e *emailer) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
func (e *emailer) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
subject := fmt.Sprintf("Service %s is Offline", s.Name)
|
||||
tmpl := renderEmail(s, f)
|
||||
email := &emailOutgoing{
|
||||
|
@ -99,9 +99,9 @@ func (e *emailer) OnFailure(s *services.Service, f *failures.Failure) (string, e
|
|||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (e *emailer) OnSuccess(s *services.Service) (string, error) {
|
||||
func (e *emailer) OnSuccess(s services.Service) (string, error) {
|
||||
subject := fmt.Sprintf("Service %s is Back Online", s.Name)
|
||||
tmpl := renderEmail(s, nil)
|
||||
tmpl := renderEmail(s, failures.Failure{})
|
||||
email := &emailOutgoing{
|
||||
To: e.Var2,
|
||||
Subject: subject,
|
||||
|
@ -111,7 +111,7 @@ func (e *emailer) OnSuccess(s *services.Service) (string, error) {
|
|||
return tmpl, e.dialSend(email)
|
||||
}
|
||||
|
||||
func renderEmail(s *services.Service, f *failures.Failure) string {
|
||||
func renderEmail(s services.Service, f failures.Failure) string {
|
||||
wr := bytes.NewBuffer(nil)
|
||||
tmpl := template.New("email")
|
||||
tmpl, err := tmpl.Parse(emailBase)
|
||||
|
@ -121,7 +121,7 @@ func renderEmail(s *services.Service, f *failures.Failure) string {
|
|||
}
|
||||
|
||||
data := replacer{
|
||||
Core: core.App,
|
||||
Core: *core.App,
|
||||
Service: s,
|
||||
Failure: f,
|
||||
Custom: nil,
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -37,6 +38,7 @@ func TestEmailNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
if EMAIL_HOST == "" || EMAIL_USER == "" || EMAIL_PASS == "" {
|
||||
t.Log("email notifier testing skipped, missing EMAIL_ environment variables")
|
||||
|
@ -63,6 +65,11 @@ func TestEmailNotifier(t *testing.T) {
|
|||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("email OnSave", func(t *testing.T) {
|
||||
_, err := email.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("email OnFailure", func(t *testing.T) {
|
||||
_, err := email.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -52,14 +52,14 @@ func (l *lineNotifier) sendMessage(message string) (string, error) {
|
|||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (l *lineNotifier) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||
func (l *lineNotifier) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline! %s", s.Name, f.Issue)
|
||||
out, err := l.sendMessage(msg)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (l *lineNotifier) OnSuccess(s *services.Service) (string, error) {
|
||||
func (l *lineNotifier) OnSuccess(s services.Service) (string, error) {
|
||||
msg := fmt.Sprintf("Service %s is online!", s.Name)
|
||||
out, err := l.sendMessage(msg)
|
||||
return out, err
|
||||
|
|
|
@ -47,19 +47,14 @@ var Mobile = &mobilePush{¬ifications.Notification{
|
|||
}}},
|
||||
}
|
||||
|
||||
func dataJson(s *services.Service, f *failures.Failure) map[string]interface{} {
|
||||
func dataJson(s services.Service, f failures.Failure) map[string]interface{} {
|
||||
serviceId := "0"
|
||||
if s != nil {
|
||||
serviceId = utils.ToString(s.Id)
|
||||
}
|
||||
serviceId = utils.ToString(s.Id)
|
||||
online := "online"
|
||||
if !s.Online {
|
||||
online = "offline"
|
||||
}
|
||||
issue := ""
|
||||
if f != nil {
|
||||
issue = f.Issue
|
||||
}
|
||||
issue := f.Issue
|
||||
link := fmt.Sprintf("statping://service?id=%v", serviceId)
|
||||
out := map[string]interface{}{
|
||||
"status": online,
|
||||
|
@ -71,7 +66,7 @@ func dataJson(s *services.Service, f *failures.Failure) map[string]interface{} {
|
|||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (m *mobilePush) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
func (m *mobilePush) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
data := dataJson(s, f)
|
||||
msg := &pushArray{
|
||||
Message: fmt.Sprintf("Your service '%v' is currently failing! Reason: %v", s.Name, f.Issue),
|
||||
|
@ -82,8 +77,8 @@ func (m *mobilePush) OnFailure(s *services.Service, f *failures.Failure) (string
|
|||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (m *mobilePush) OnSuccess(s *services.Service) (string, error) {
|
||||
data := dataJson(s, nil)
|
||||
func (m *mobilePush) OnSuccess(s services.Service) (string, error) {
|
||||
data := dataJson(s, failures.Failure{})
|
||||
msg := &pushArray{
|
||||
Message: "Service is Online!",
|
||||
Title: "Service Online",
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -29,6 +30,7 @@ func TestMobileNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
Mobile.Var1 = mobileToken
|
||||
if mobileToken == "" {
|
||||
|
@ -52,6 +54,11 @@ func TestMobileNotifier(t *testing.T) {
|
|||
assert.True(t, Mobile.CanSend())
|
||||
})
|
||||
|
||||
t.Run("Mobile OnSave", func(t *testing.T) {
|
||||
_, err := Mobile.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Mobile OnFailure", func(t *testing.T) {
|
||||
_, err := Mobile.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -13,9 +13,9 @@ import (
|
|||
var log = utils.Log.WithField("type", "notifier")
|
||||
|
||||
type replacer struct {
|
||||
Core *core.Core
|
||||
Service *services.Service
|
||||
Failure *failures.Failure
|
||||
Core core.Core
|
||||
Service services.Service
|
||||
Failure failures.Failure
|
||||
Custom map[string]string
|
||||
}
|
||||
|
||||
|
@ -59,8 +59,8 @@ func Add(notifs ...services.ServiceNotifier) {
|
|||
}
|
||||
}
|
||||
|
||||
func ReplaceVars(input string, s *services.Service, f *failures.Failure) string {
|
||||
return ReplaceTemplate(input, replacer{Service: s, Failure: f, Core: core.App})
|
||||
func ReplaceVars(input string, s services.Service, f failures.Failure) string {
|
||||
return ReplaceTemplate(input, replacer{Service: s, Failure: f, Core: *core.App})
|
||||
}
|
||||
|
||||
var exampleFailure = &failures.Failure{
|
||||
|
|
|
@ -41,7 +41,7 @@ var Pushover = &pushover{¬ifications.Notification{
|
|||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
Title: "User Token",
|
||||
Placeholder: "Insert your device's Pushover Token",
|
||||
Placeholder: "Insert your Pushover User Token",
|
||||
DbField: "api_key",
|
||||
Required: true,
|
||||
}, {
|
||||
|
@ -50,16 +50,51 @@ var Pushover = &pushover{¬ifications.Notification{
|
|||
Placeholder: "Create an Application and insert the API Key here",
|
||||
DbField: "api_secret",
|
||||
Required: true,
|
||||
}, {
|
||||
Type: "list",
|
||||
Title: "Priority",
|
||||
Placeholder: "Set the notification priority level",
|
||||
DbField: "Var1",
|
||||
Required: true,
|
||||
ListOptions: []string{"Lowest", "Low", "Normal", "High", "Emergency"},
|
||||
}, {
|
||||
Type: "list",
|
||||
Title: "Notification Sound",
|
||||
Placeholder: "Choose a sound for this Pushover notification",
|
||||
DbField: "Var2",
|
||||
Required: true,
|
||||
ListOptions: []string{"none", "pushover", "bike", "bugle", "cashregister", "classical", "cosmic", "falling", "gamelan", "incoming", "intermissioon", "magic", "mechanical", "painobar", "siren", "spacealarm", "tugboat", "alien", "climb", "persistent", "echo", "updown"},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
func priority(val string) string {
|
||||
switch strings.ToLower(val) {
|
||||
case "lowest":
|
||||
return "-2"
|
||||
case "low":
|
||||
return "-1"
|
||||
case "normal":
|
||||
return "0"
|
||||
case "high":
|
||||
return "1"
|
||||
case "emergency":
|
||||
return "2"
|
||||
default:
|
||||
return "0"
|
||||
}
|
||||
}
|
||||
|
||||
// Send will send a HTTP Post to the Pushover API. It accepts type: string
|
||||
func (t *pushover) sendMessage(message string) (string, error) {
|
||||
v := url.Values{}
|
||||
v.Set("token", t.ApiSecret)
|
||||
v.Set("user", t.ApiKey)
|
||||
v.Set("message", message)
|
||||
v.Set("priority", priority(t.Var1))
|
||||
if t.Var2 != "" {
|
||||
v.Set("sound", t.Var2)
|
||||
}
|
||||
rb := strings.NewReader(v.Encode())
|
||||
|
||||
content, _, err := utils.HttpRequest(pushoverUrl, "POST", "application/x-www-form-urlencoded", nil, rb, time.Duration(10*time.Second), true, nil)
|
||||
|
@ -70,15 +105,15 @@ func (t *pushover) sendMessage(message string) (string, error) {
|
|||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *pushover) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
func (t *pushover) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
message := ReplaceVars(t.FailureData, s, f)
|
||||
out, err := t.sendMessage(message)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *pushover) OnSuccess(s *services.Service) (string, error) {
|
||||
message := ReplaceVars(t.SuccessData, s, nil)
|
||||
func (t *pushover) OnSuccess(s services.Service) (string, error) {
|
||||
message := ReplaceVars(t.SuccessData, s, failures.Failure{})
|
||||
out, err := t.sendMessage(message)
|
||||
return out, err
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -28,6 +29,7 @@ func TestPushoverNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
if PUSHOVER_TOKEN == "" || PUSHOVER_API == "" {
|
||||
t.Log("Pushover notifier testing skipped, missing PUSHOVER_TOKEN and PUSHOVER_API environment variable")
|
||||
|
@ -37,6 +39,8 @@ func TestPushoverNotifier(t *testing.T) {
|
|||
t.Run("Load Pushover", func(t *testing.T) {
|
||||
Pushover.ApiKey = PUSHOVER_TOKEN
|
||||
Pushover.ApiSecret = PUSHOVER_API
|
||||
Pushover.Var1 = "Normal"
|
||||
Pushover.Var2 = "vibrate"
|
||||
Pushover.Enabled = null.NewNullBool(true)
|
||||
|
||||
Add(Pushover)
|
||||
|
@ -50,6 +54,11 @@ func TestPushoverNotifier(t *testing.T) {
|
|||
assert.True(t, Pushover.CanSend())
|
||||
})
|
||||
|
||||
t.Run("Pushover OnSave", func(t *testing.T) {
|
||||
_, err := Pushover.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Pushover OnFailure", func(t *testing.T) {
|
||||
_, err := Pushover.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -61,7 +61,7 @@ func (s *slack) sendSlack(msg string) (string, error) {
|
|||
|
||||
func (s *slack) OnTest() (string, error) {
|
||||
example := services.Example(true)
|
||||
testMsg := ReplaceVars(s.SuccessData, example, nil)
|
||||
testMsg := ReplaceVars(s.SuccessData, example, failures.Failure{})
|
||||
contents, resp, err := utils.HttpRequest(s.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(testMsg)), time.Duration(10*time.Second), true, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -74,15 +74,15 @@ func (s *slack) OnTest() (string, error) {
|
|||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (s *slack) OnFailure(srv *services.Service, f *failures.Failure) (string, error) {
|
||||
func (s *slack) OnFailure(srv services.Service, f failures.Failure) (string, error) {
|
||||
msg := ReplaceVars(s.FailureData, srv, f)
|
||||
out, err := s.sendSlack(msg)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (s *slack) OnSuccess(srv *services.Service) (string, error) {
|
||||
msg := ReplaceVars(s.SuccessData, srv, nil)
|
||||
func (s *slack) OnSuccess(srv services.Service) (string, error) {
|
||||
msg := ReplaceVars(s.SuccessData, srv, failures.Failure{})
|
||||
out, err := s.sendSlack(msg)
|
||||
return out, err
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -24,6 +25,7 @@ func TestSlackNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
SLACK_URL = utils.Params.GetString("SLACK_URL")
|
||||
slacker.Host = SLACK_URL
|
||||
|
@ -48,6 +50,11 @@ func TestSlackNotifier(t *testing.T) {
|
|||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("slack OnSave", func(t *testing.T) {
|
||||
_, err := slacker.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("slack OnFailure", func(t *testing.T) {
|
||||
_, err := slacker.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -60,17 +60,17 @@ func (s *statpingEmailer) OnTest() (string, error) {
|
|||
}
|
||||
|
||||
type statpingMail struct {
|
||||
Email string `json:"email"`
|
||||
Core *core.Core `json:"core,omitempty"`
|
||||
Service *services.Service `json:"service,omitempty"`
|
||||
Failure *failures.Failure `json:"failure,omitempty"`
|
||||
Email string `json:"email"`
|
||||
Core core.Core `json:"core,omitempty"`
|
||||
Service services.Service `json:"service,omitempty"`
|
||||
Failure failures.Failure `json:"failure,omitempty"`
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (s *statpingEmailer) OnFailure(srv *services.Service, f *failures.Failure) (string, error) {
|
||||
func (s *statpingEmailer) OnFailure(srv services.Service, f failures.Failure) (string, error) {
|
||||
ee := statpingMail{
|
||||
Email: s.Host,
|
||||
Core: core.App,
|
||||
Core: *core.App,
|
||||
Service: srv,
|
||||
Failure: f,
|
||||
}
|
||||
|
@ -78,12 +78,12 @@ func (s *statpingEmailer) OnFailure(srv *services.Service, f *failures.Failure)
|
|||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (s *statpingEmailer) OnSuccess(srv *services.Service) (string, error) {
|
||||
func (s *statpingEmailer) OnSuccess(srv services.Service) (string, error) {
|
||||
ee := statpingMail{
|
||||
Email: s.Host,
|
||||
Core: core.App,
|
||||
Core: *core.App,
|
||||
Service: srv,
|
||||
Failure: nil,
|
||||
Failure: failures.Failure{},
|
||||
}
|
||||
return s.sendStatpingEmail(ee)
|
||||
}
|
||||
|
@ -92,9 +92,9 @@ func (s *statpingEmailer) OnSuccess(srv *services.Service) (string, error) {
|
|||
func (s *statpingEmailer) OnSave() (string, error) {
|
||||
ee := statpingMail{
|
||||
Email: s.Host,
|
||||
Core: core.App,
|
||||
Service: nil,
|
||||
Failure: nil,
|
||||
Core: *core.App,
|
||||
Service: services.Service{},
|
||||
Failure: failures.Failure{},
|
||||
}
|
||||
out, err := s.sendStatpingEmail(ee)
|
||||
log.Println("statping emailer response", out)
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -24,6 +25,7 @@ func TestStatpingEmailerNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
testEmail = utils.Params.GetString("TEST_EMAIL")
|
||||
statpingMailer.Host = testEmail
|
||||
|
@ -48,6 +50,11 @@ func TestStatpingEmailerNotifier(t *testing.T) {
|
|||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("statping emailer OnSave", func(t *testing.T) {
|
||||
_, err := statpingMailer.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("statping emailer OnFailure", func(t *testing.T) {
|
||||
_, err := statpingMailer.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -45,9 +45,9 @@ var Telegram = &telegram{¬ifications.Notification{
|
|||
Required: true,
|
||||
}, {
|
||||
Type: "text",
|
||||
Title: "Channel or User",
|
||||
Title: "Channel",
|
||||
Placeholder: "@statping_channel",
|
||||
SmallText: "Insert your Telegram Channel or User here.",
|
||||
SmallText: "Insert your Telegram Channel including the @ symbol. The bot will need to be an administrator of this channel.",
|
||||
DbField: "var1",
|
||||
Required: true,
|
||||
}}},
|
||||
|
@ -60,9 +60,8 @@ func (t *telegram) sendMessage(message string) (string, error) {
|
|||
v := url.Values{}
|
||||
v.Set("chat_id", t.Var1)
|
||||
v.Set("text", message)
|
||||
rb := *strings.NewReader(v.Encode())
|
||||
|
||||
contents, _, err := utils.HttpRequest(apiEndpoint, "GET", "application/x-www-form-urlencoded", nil, &rb, time.Duration(10*time.Second), true, nil)
|
||||
contents, _, err := utils.HttpRequest(apiEndpoint, "POST", "application/x-www-form-urlencoded", nil, strings.NewReader(v.Encode()), time.Duration(10*time.Second), true, nil)
|
||||
|
||||
success, _ := telegramSuccess(contents)
|
||||
if !success {
|
||||
|
@ -74,29 +73,29 @@ func (t *telegram) sendMessage(message string) (string, error) {
|
|||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *telegram) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
func (t *telegram) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
msg := ReplaceVars(t.FailureData, s, f)
|
||||
out, err := t.sendMessage(msg)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *telegram) OnSuccess(s *services.Service) (string, error) {
|
||||
msg := ReplaceVars(t.SuccessData, s, nil)
|
||||
func (t *telegram) OnSuccess(s services.Service) (string, error) {
|
||||
msg := ReplaceVars(t.SuccessData, s, failures.Failure{})
|
||||
out, err := t.sendMessage(msg)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnTest will test the Twilio SMS messaging
|
||||
func (t *telegram) OnTest() (string, error) {
|
||||
msg := fmt.Sprintf("Testing the Twilio SMS Notifier on your Statping server")
|
||||
content, err := t.sendMessage(msg)
|
||||
return content, err
|
||||
msg := fmt.Sprintf("Testing the Telegram Notifier on your Statping server")
|
||||
return t.sendMessage(msg)
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (t *telegram) OnSave() (string, error) {
|
||||
return "", nil
|
||||
msg := fmt.Sprintf("The Telegram Notifier on your Statping server was just saved")
|
||||
return t.sendMessage(msg)
|
||||
}
|
||||
|
||||
func telegramSuccess(res []byte) (bool, telegramResponse) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -31,6 +32,7 @@ func TestTelegramNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
if telegramToken == "" || telegramChannel == "" {
|
||||
t.Log("Telegram notifier testing skipped, missing TELEGRAM_TOKEN and TELEGRAM_CHANNEL environment variable")
|
||||
|
@ -54,6 +56,11 @@ func TestTelegramNotifier(t *testing.T) {
|
|||
assert.True(t, Telegram.CanSend())
|
||||
})
|
||||
|
||||
t.Run("Telegram OnSave", func(t *testing.T) {
|
||||
_, err := Telegram.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Telegram OnFailure", func(t *testing.T) {
|
||||
_, err := Telegram.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -50,13 +50,13 @@ var Twilio = &twilio{¬ifications.Notification{
|
|||
DbField: "api_secret",
|
||||
Required: true,
|
||||
}, {
|
||||
Type: "text",
|
||||
Type: "number",
|
||||
Title: "SMS to Phone Number",
|
||||
Placeholder: "18555555555",
|
||||
DbField: "Var1",
|
||||
Required: true,
|
||||
}, {
|
||||
Type: "text",
|
||||
Type: "number",
|
||||
Title: "From Phone Number",
|
||||
Placeholder: "18555555555",
|
||||
DbField: "Var2",
|
||||
|
@ -90,14 +90,14 @@ func (t *twilio) sendMessage(message string) (string, error) {
|
|||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *twilio) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
func (t *twilio) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
msg := ReplaceVars(t.FailureData, s, f)
|
||||
return t.sendMessage(msg)
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *twilio) OnSuccess(s *services.Service) (string, error) {
|
||||
msg := ReplaceVars(t.SuccessData, s, nil)
|
||||
func (t *twilio) OnSuccess(s services.Service) (string, error) {
|
||||
msg := ReplaceVars(t.SuccessData, s, failures.Failure{})
|
||||
return t.sendMessage(msg)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -29,6 +30,7 @@ func TestTwilioNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
if TWILIO_SID == "" || TWILIO_SECRET == "" {
|
||||
t.Log("twilio notifier testing skipped, missing TWILIO_SID and TWILIO_SECRET environment variable")
|
||||
|
@ -54,6 +56,11 @@ func TestTwilioNotifier(t *testing.T) {
|
|||
assert.True(t, Twilio.CanSend())
|
||||
})
|
||||
|
||||
t.Run("Twilio OnSave", func(t *testing.T) {
|
||||
_, err := Twilio.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Twilio OnFailure", func(t *testing.T) {
|
||||
_, err := Twilio.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -31,9 +31,9 @@ var Webhook = &webhooker{¬ifications.Notification{
|
|||
Author: "Hunter Long",
|
||||
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}`,
|
||||
Delay: time.Duration(3 * time.Second),
|
||||
SuccessData: `{"id": "{{.Service.Id}}", "online": true}`,
|
||||
FailureData: `{"id": "{{.Service.Id}}", "online": false}`,
|
||||
DataType: "json",
|
||||
Limits: 180,
|
||||
Form: []notifications.NotificationForm{{
|
||||
|
@ -44,12 +44,13 @@ var Webhook = &webhooker{¬ifications.Notification{
|
|||
DbField: "Host",
|
||||
Required: true,
|
||||
}, {
|
||||
Type: "text",
|
||||
Type: "list",
|
||||
Title: "HTTP Method",
|
||||
Placeholder: "POST",
|
||||
SmallText: "Choose a HTTP method for example: GET, POST, DELETE, or PATCH.",
|
||||
DbField: "Var1",
|
||||
Required: true,
|
||||
ListOptions: []string{"GET", "POST", "PATCH", "DELETE"},
|
||||
}, {
|
||||
Type: "text",
|
||||
Title: "Content Type",
|
||||
|
@ -82,8 +83,7 @@ func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
|||
utils.Log.Infoln(fmt.Sprintf("sending body: '%v' to %v as a %v request", body, w.Host, w.Var1))
|
||||
client := new(http.Client)
|
||||
client.Timeout = time.Duration(10 * time.Second)
|
||||
var buf *bytes.Buffer
|
||||
buf = bytes.NewBuffer(nil)
|
||||
buf := bytes.NewBuffer(nil)
|
||||
if w.Var2 != "" {
|
||||
buf = bytes.NewBuffer([]byte(body))
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
|||
req.Header.Add("Content-Type", w.ApiKey)
|
||||
}
|
||||
req.Header.Set("User-Agent", "Statping")
|
||||
req.Header.Set("Statping-Version", utils.Version)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -126,7 +127,7 @@ func (w *webhooker) OnTest() (string, error) {
|
|||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
func (w *webhooker) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
msg := ReplaceVars(w.FailureData, s, f)
|
||||
resp, err := w.sendHttpWebhook(msg)
|
||||
if err != nil {
|
||||
|
@ -138,8 +139,8 @@ func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) (string,
|
|||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (w *webhooker) OnSuccess(s *services.Service) (string, error) {
|
||||
msg := ReplaceVars(w.SuccessData, s, nil)
|
||||
func (w *webhooker) OnSuccess(s services.Service) (string, error) {
|
||||
msg := ReplaceVars(w.SuccessData, s, failures.Failure{})
|
||||
resp, err := w.sendHttpWebhook(msg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -2,6 +2,7 @@ package notifiers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
|
@ -27,6 +28,7 @@ func TestWebhookNotifier(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
core.Example()
|
||||
|
||||
t.Run("Load webhooker", func(t *testing.T) {
|
||||
Webhook.Host = webhookTestUrl
|
||||
|
@ -46,6 +48,11 @@ func TestWebhookNotifier(t *testing.T) {
|
|||
assert.True(t, Webhook.CanSend())
|
||||
})
|
||||
|
||||
t.Run("webhooker OnSave", func(t *testing.T) {
|
||||
_, err := Webhook.OnSave()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("webhooker OnFailure", func(t *testing.T) {
|
||||
_, err := Webhook.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -43,6 +43,9 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
|||
p.Set("DESCRIPTION", description)
|
||||
p.Set("LANGUAGE", language)
|
||||
p.Set("ALLOW_REPORTS", reports)
|
||||
p.Set("ADMIN_USER", username)
|
||||
p.Set("ADMIN_PASSWORD", password)
|
||||
p.Set("ADMIN_EMAIL", email)
|
||||
|
||||
confg := &DbConfig{
|
||||
DbConn: dbConn,
|
||||
|
|
|
@ -75,10 +75,9 @@ func initModels(db database.Database) {
|
|||
}
|
||||
|
||||
func CreateAdminUser(c *DbConfig) error {
|
||||
log.Infoln(fmt.Sprintf("Default Admininstrator user does not exist, creating now! (admin/admin)"))
|
||||
|
||||
adminUser := utils.Params.GetString("ADMIN_USER")
|
||||
adminPass := utils.Params.GetString("ADMIN_PASSWORD")
|
||||
adminEmail := utils.Params.GetString("ADMIN_EMAIL")
|
||||
|
||||
if adminUser == "" || adminPass == "" {
|
||||
adminUser = "admin"
|
||||
|
@ -88,7 +87,7 @@ func CreateAdminUser(c *DbConfig) error {
|
|||
admin := &users.User{
|
||||
Username: adminUser,
|
||||
Password: adminPass,
|
||||
Email: "info@admin.com",
|
||||
Email: adminEmail,
|
||||
Admin: null.NewNullBool(true),
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,22 @@ import (
|
|||
"github.com/statping/statping/utils"
|
||||
)
|
||||
|
||||
func Example() *Core {
|
||||
core := &Core{
|
||||
Name: "Statping Testing",
|
||||
Description: "This is a instance that runs for tests",
|
||||
ApiSecret: "exampleapisecret",
|
||||
Domain: "http://localhost:8080",
|
||||
CreatedAt: utils.Now(),
|
||||
UseCdn: null.NewNullBool(false),
|
||||
Footer: null.NewNullString(""),
|
||||
MigrationId: utils.Now().Unix(),
|
||||
Language: "en",
|
||||
}
|
||||
App = core
|
||||
return App
|
||||
}
|
||||
|
||||
func Samples() error {
|
||||
apiSecret := utils.Params.GetString("API_SECRET")
|
||||
|
||||
|
@ -12,6 +28,8 @@ func Samples() error {
|
|||
apiSecret = utils.RandomString(32)
|
||||
}
|
||||
|
||||
oauth := OAuth{Providers: "local"}
|
||||
|
||||
core := &Core{
|
||||
Name: utils.Params.GetString("NAME"),
|
||||
Description: utils.Params.GetString("DESCRIPTION"),
|
||||
|
@ -22,6 +40,7 @@ func Samples() error {
|
|||
Footer: null.NewNullString(""),
|
||||
MigrationId: utils.Now().Unix(),
|
||||
Language: utils.Params.GetString("LANGUAGE"),
|
||||
OAuth: oauth,
|
||||
}
|
||||
|
||||
return core.Create()
|
||||
|
|
|
@ -43,15 +43,24 @@ type Core struct {
|
|||
}
|
||||
|
||||
type OAuth struct {
|
||||
Domains string `gorm:"column:oauth_domains" json:"oauth_domains" scope:"admin"`
|
||||
Providers string `gorm:"column:oauth_providers;" json:"oauth_providers"`
|
||||
GithubClientID string `gorm:"column:gh_client_id" json:"gh_client_id"`
|
||||
GithubClientSecret string `gorm:"column:gh_client_secret" json:"gh_client_secret" scope:"admin"`
|
||||
GoogleClientID string `gorm:"column:google_client_id" json:"google_client_id"`
|
||||
GoogleClientSecret string `gorm:"column:google_client_secret" json:"google_client_secret" scope:"admin"`
|
||||
SlackClientID string `gorm:"column:slack_client_id" json:"slack_client_id"`
|
||||
SlackClientSecret string `gorm:"column:slack_client_secret" json:"slack_client_secret" scope:"admin"`
|
||||
SlackTeam string `gorm:"column:slack_team" json:"slack_team"`
|
||||
Providers string `gorm:"column:oauth_providers;" json:"oauth_providers"`
|
||||
GithubClientID string `gorm:"column:gh_client_id" json:"gh_client_id"`
|
||||
GithubClientSecret string `gorm:"column:gh_client_secret" json:"gh_client_secret" scope:"admin"`
|
||||
GithubUsers string `gorm:"column:gh_users" json:"gh_users" scope:"admin"`
|
||||
GithubOrgs string `gorm:"column:gh_orgs" json:"gh_orgs" scope:"admin"`
|
||||
GoogleClientID string `gorm:"column:google_client_id" json:"google_client_id"`
|
||||
GoogleClientSecret string `gorm:"column:google_client_secret" json:"google_client_secret" scope:"admin"`
|
||||
GoogleUsers string `gorm:"column:google_users" json:"google_users" scope:"admin"`
|
||||
SlackClientID string `gorm:"column:slack_client_id" json:"slack_client_id"`
|
||||
SlackClientSecret string `gorm:"column:slack_client_secret" json:"slack_client_secret" scope:"admin"`
|
||||
SlackTeam string `gorm:"column:slack_team" json:"slack_team" scope:"admin"`
|
||||
SlackUsers string `gorm:"column:slack_users" json:"slack_users" scope:"admin"`
|
||||
CustomName string `gorm:"column:custom_name" json:"custom_name"`
|
||||
CustomClientID string `gorm:"column:custom_client_id" json:"custom_client_id"`
|
||||
CustomClientSecret string `gorm:"column:custom_client_secret" json:"custom_client_secret" scope:"admin"`
|
||||
CustomEndpointAuth string `gorm:"column:custom_endpoint_auth" json:"custom_endpoint_auth"`
|
||||
CustomEndpointToken string `gorm:"column:custom_endpoint_token" json:"custom_endpoint_token" scope:"admin"`
|
||||
CustomScopes string `gorm:"column:custom_scopes" json:"custom_scopes"`
|
||||
}
|
||||
|
||||
// AllNotifiers contains all the Notifiers loaded
|
||||
|
|
|
@ -28,7 +28,6 @@ func (f *Failure) AfterDelete() {
|
|||
}
|
||||
|
||||
func (f *Failure) AfterCreate() {
|
||||
metrics.Inc("failure", f.Service)
|
||||
metrics.Query("failure", "create")
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ var (
|
|||
log = utils.Log.WithField("type", "failure")
|
||||
)
|
||||
|
||||
func Example() *Failure {
|
||||
return &Failure{
|
||||
func Example() Failure {
|
||||
return Failure{
|
||||
Id: 48533,
|
||||
Issue: "Response did not response a 200 status code",
|
||||
Method: "",
|
||||
|
|
|
@ -27,7 +27,6 @@ func (h *Hit) AfterDelete() {
|
|||
}
|
||||
|
||||
func (h *Hit) AfterCreate() {
|
||||
metrics.Inc("success", h.Service)
|
||||
metrics.Query("hit", "create")
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ var (
|
|||
Name: "service_online",
|
||||
Help: "If service is online",
|
||||
},
|
||||
[]string{"service", "name", "type"},
|
||||
[]string{"service", "type"},
|
||||
)
|
||||
|
||||
// service failures
|
||||
|
|
|
@ -7,16 +7,15 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func (n *Notification) Name() string {
|
||||
func (n Notification) Name() string {
|
||||
newName := strings.ToLower(n.Method)
|
||||
newName = strings.ReplaceAll(newName, " ", "_")
|
||||
return newName
|
||||
}
|
||||
|
||||
// LastSent returns a time.Duration of the last sent notification for the notifier
|
||||
func (n *Notification) LastSent() time.Duration {
|
||||
since := time.Since(n.lastSent)
|
||||
return since
|
||||
func (n Notification) LastSent() time.Duration {
|
||||
return time.Since(n.lastSent)
|
||||
}
|
||||
|
||||
func (n *Notification) CanSend() bool {
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
|
||||
// Notifier interface is required to create a new Notifier
|
||||
type Notifier interface {
|
||||
OnSuccess(*services.Service) (string, error) // OnSuccess is triggered when a service is successful
|
||||
OnFailure(*services.Service, *failures.Failure) (string, error) // OnFailure is triggered when a service is failing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
OnSave() (string, error) // OnSave is triggered for when saved
|
||||
OnSuccess(services.Service) (string, error) // OnSuccess is triggered when a service is successful
|
||||
OnFailure(services.Service, failures.Failure) (string, error) // OnFailure is triggered when a service is failing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
OnSave() (string, error) // OnSave is triggered for when saved
|
||||
}
|
||||
|
|
|
@ -20,15 +20,14 @@ func (s *Service) FailuresSince(t time.Time) failures.Failurer {
|
|||
return failures.Since(t, s)
|
||||
}
|
||||
|
||||
func (s *Service) DowntimeAgo() string {
|
||||
last := s.LastOnline
|
||||
if last.IsZero() {
|
||||
func (s Service) DowntimeAgo() string {
|
||||
if s.LastOnline.IsZero() {
|
||||
return "Never been online"
|
||||
}
|
||||
return humanize.Time(last)
|
||||
return humanize.Time(s.LastOnline)
|
||||
}
|
||||
|
||||
func (s *Service) DowntimeText() string {
|
||||
func (s Service) DowntimeText() string {
|
||||
last := s.AllFailures().Last()
|
||||
if last == nil {
|
||||
return ""
|
||||
|
|
|
@ -61,12 +61,12 @@ func (s *Service) LoadTLSCert() (*tls.Config, error) {
|
|||
return config, nil
|
||||
}
|
||||
|
||||
func (s *Service) Duration() time.Duration {
|
||||
func (s Service) Duration() time.Duration {
|
||||
return time.Duration(s.Interval) * time.Second
|
||||
}
|
||||
|
||||
// Start will create a channel for the service checking go routine
|
||||
func (s *Service) UptimeData(hits []*hits.Hit, fails []*failures.Failure) (*UptimeSeries, error) {
|
||||
func (s Service) UptimeData(hits []*hits.Hit, fails []*failures.Failure) (*UptimeSeries, error) {
|
||||
if len(hits) == 0 {
|
||||
return nil, errors.New("service does not have any successful hits")
|
||||
}
|
||||
|
@ -291,12 +291,12 @@ func (s *Service) UpdateStats() *Service {
|
|||
}
|
||||
|
||||
// AvgTime will return the average amount of time for a service to response back successfully
|
||||
func (s *Service) AvgTime() int64 {
|
||||
func (s Service) AvgTime() int64 {
|
||||
return s.AllHits().Avg()
|
||||
}
|
||||
|
||||
// OnlineDaysPercent returns the service's uptime percent within last 24 hours
|
||||
func (s *Service) OnlineDaysPercent(days int) float32 {
|
||||
func (s Service) OnlineDaysPercent(days int) float32 {
|
||||
ago := utils.Now().Add(-time.Duration(days) * types.Day)
|
||||
return s.OnlineSince(ago)
|
||||
}
|
||||
|
@ -326,12 +326,12 @@ func (s *Service) OnlineSince(ago time.Time) float32 {
|
|||
return s.Online24Hours
|
||||
}
|
||||
|
||||
func (s *Service) Uptime() utils.Duration {
|
||||
func (s Service) Uptime() utils.Duration {
|
||||
return utils.Duration{Duration: utils.Now().Sub(s.LastOffline)}
|
||||
}
|
||||
|
||||
// Downtime returns the amount of time of a offline service
|
||||
func (s *Service) Downtime() utils.Duration {
|
||||
func (s Service) Downtime() utils.Duration {
|
||||
return utils.Duration{Duration: utils.Now().Sub(s.LastOnline)}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,9 +26,9 @@ func FindNotifier(method string) *notifications.Notification {
|
|||
}
|
||||
|
||||
type ServiceNotifier interface {
|
||||
OnSuccess(*Service) (string, error) // OnSuccess is triggered when a service is successful
|
||||
OnFailure(*Service, *failures.Failure) (string, error) // OnFailure is triggered when a service is failing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
OnSave() (string, error) // OnSave is triggered for testing
|
||||
Select() *notifications.Notification // OnTest is triggered for testing
|
||||
OnSuccess(Service) (string, error) // OnSuccess is triggered when a service is successful
|
||||
OnFailure(Service, failures.Failure) (string, error) // OnFailure is triggered when a service is failing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
OnSave() (string, error) // OnSave is triggered for testing
|
||||
Select() *notifications.Notification // OnTest is triggered for testing
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ func isIPv6(address string) bool {
|
|||
// checkIcmp will send a ICMP ping packet to the service
|
||||
func CheckIcmp(s *Service, record bool) (*Service, error) {
|
||||
defer s.updateLastCheck()
|
||||
timer := prometheus.NewTimer(metrics.ServiceTimer(s.Id))
|
||||
timer := prometheus.NewTimer(metrics.ServiceTimer(s.Name))
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
if err := utils.Ping(s.Domain, s.Timeout); err != nil {
|
||||
|
@ -105,7 +105,7 @@ func CheckIcmp(s *Service, record bool) (*Service, error) {
|
|||
// CheckGrpc will check a gRPC service
|
||||
func CheckGrpc(s *Service, record bool) (*Service, error) {
|
||||
defer s.updateLastCheck()
|
||||
timer := prometheus.NewTimer(metrics.ServiceTimer(s.Id))
|
||||
timer := prometheus.NewTimer(metrics.ServiceTimer(s.Name))
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
dnsLookup, err := dnsCheck(s)
|
||||
|
@ -152,7 +152,7 @@ func CheckGrpc(s *Service, record bool) (*Service, error) {
|
|||
// checkTcp will check a TCP service
|
||||
func CheckTcp(s *Service, record bool) (*Service, error) {
|
||||
defer s.updateLastCheck()
|
||||
timer := prometheus.NewTimer(metrics.ServiceTimer(s.Id))
|
||||
timer := prometheus.NewTimer(metrics.ServiceTimer(s.Name))
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
dnsLookup, err := dnsCheck(s)
|
||||
|
@ -219,7 +219,7 @@ func (s *Service) updateLastCheck() {
|
|||
// checkHttp will check a HTTP service
|
||||
func CheckHttp(s *Service, record bool) (*Service, error) {
|
||||
defer s.updateLastCheck()
|
||||
timer := prometheus.NewTimer(metrics.ServiceTimer(s.Id))
|
||||
timer := prometheus.NewTimer(metrics.ServiceTimer(s.Name))
|
||||
defer timer.ObserveDuration()
|
||||
|
||||
dnsLookup, err := dnsCheck(s)
|
||||
|
@ -285,7 +285,7 @@ func CheckHttp(s *Service, record bool) (*Service, error) {
|
|||
s.LastResponse = string(content)
|
||||
s.LastStatusCode = res.StatusCode
|
||||
|
||||
metrics.Gauge("status_code", float64(res.StatusCode), s.Id)
|
||||
metrics.Gauge("status_code", float64(res.StatusCode), s.Name)
|
||||
|
||||
if s.Expected.String != "" {
|
||||
match, err := regexp.MatchString(s.Expected.String, string(content))
|
||||
|
@ -329,7 +329,8 @@ func recordSuccess(s *Service) {
|
|||
fmt.Sprintf("Service #%d '%v' Successful Response: %s | Lookup in: %s | Online: %v | Interval: %d seconds", s.Id, s.Name, humanMicro(hit.Latency), humanMicro(hit.PingTime), s.Online, s.Interval))
|
||||
s.LastLookupTime = hit.PingTime
|
||||
s.LastLatency = hit.Latency
|
||||
metrics.Gauge("online", 1., s.Id, s.Name, s.Type)
|
||||
metrics.Gauge("online", 1., s.Name, s.Type)
|
||||
metrics.Inc("success", s.Name)
|
||||
sendSuccess(s)
|
||||
s.SuccessNotified = true
|
||||
}
|
||||
|
@ -352,7 +353,7 @@ func sendSuccess(s *Service) {
|
|||
notif := n.Select()
|
||||
if notif.CanSend() {
|
||||
log.Infof("Sending notification to: %s!", notif.Method)
|
||||
if _, err := n.OnSuccess(s); err != nil {
|
||||
if _, err := n.OnSuccess(*s); err != nil {
|
||||
notif.Logger().Errorln(err)
|
||||
}
|
||||
s.UserNotified = true
|
||||
|
@ -383,7 +384,8 @@ func recordFailure(s *Service, issue string) {
|
|||
s.Online = false
|
||||
s.SuccessNotified = false
|
||||
s.DownText = s.DowntimeText()
|
||||
metrics.Gauge("online", 0., s.Id, s.Name, s.Type)
|
||||
metrics.Gauge("online", 0., s.Name, s.Type)
|
||||
metrics.Inc("failure", s.Name)
|
||||
sendFailure(s, fail)
|
||||
}
|
||||
|
||||
|
@ -403,7 +405,7 @@ func sendFailure(s *Service, f *failures.Failure) {
|
|||
notif := n.Select()
|
||||
if notif.CanSend() {
|
||||
log.Infof("Sending Failure notification to: %s!", notif.Method)
|
||||
if _, err := n.OnFailure(s, f); err != nil {
|
||||
if _, err := n.OnFailure(*s, *f); err != nil {
|
||||
notif.Logger().WithField("failure", f.Issue).Errorln(err)
|
||||
}
|
||||
s.UserNotified = true
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func Example(online bool) *Service {
|
||||
return &Service{
|
||||
func Example(online bool) Service {
|
||||
return Service{
|
||||
Id: 6283,
|
||||
Name: "Statping Example",
|
||||
Domain: "https://statping.com",
|
||||
|
|
|
@ -24,7 +24,7 @@ func InitEnvs() {
|
|||
Log.Errorln(err)
|
||||
defaultDir = "."
|
||||
}
|
||||
Params.Set("VERSION", version)
|
||||
Params.Set("VERSION", Version)
|
||||
Params.SetDefault("DISABLE_HTTP", false)
|
||||
Params.SetDefault("STATPING_DIR", defaultDir)
|
||||
Params.SetDefault("GO_ENV", "production")
|
||||
|
@ -35,6 +35,7 @@ func InitEnvs() {
|
|||
Params.SetDefault("BASE_PATH", "")
|
||||
Params.SetDefault("ADMIN_USER", "admin")
|
||||
Params.SetDefault("ADMIN_PASSWORD", "admin")
|
||||
Params.SetDefault("ADMIN_EMAIL", "info@admin.com")
|
||||
Params.SetDefault("MAX_OPEN_CONN", 25)
|
||||
Params.SetDefault("MAX_IDLE_CONN", 25)
|
||||
Params.SetDefault("MAX_LIFE_CONN", 5*time.Minute)
|
||||
|
|
|
@ -21,7 +21,7 @@ var (
|
|||
LastLines []*logRow
|
||||
LockLines sync.Mutex
|
||||
VerboseMode int
|
||||
version string
|
||||
Version string
|
||||
allowReports bool
|
||||
)
|
||||
|
||||
|
@ -36,7 +36,7 @@ func SentryInit(v *string, allow bool) {
|
|||
if *v == "" {
|
||||
*v = "development"
|
||||
}
|
||||
version = *v
|
||||
Version = *v
|
||||
}
|
||||
goEnv := Params.GetString("GO_ENV")
|
||||
allowReports := Params.GetBool("ALLOW_REPORTS")
|
||||
|
@ -44,7 +44,7 @@ func SentryInit(v *string, allow bool) {
|
|||
if err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: errorReporter,
|
||||
Environment: goEnv,
|
||||
Release: version,
|
||||
Release: Version,
|
||||
AttachStacktrace: true,
|
||||
}); err != nil {
|
||||
Log.Errorln(err)
|
||||
|
@ -63,7 +63,7 @@ func SentryErr(err error) {
|
|||
func SentryLogEntry(entry *Logger.Entry) {
|
||||
e := sentry.NewEvent()
|
||||
e.Message = entry.Message
|
||||
e.Release = version
|
||||
e.Release = Version
|
||||
e.Contexts = entry.Data
|
||||
sentry.CaptureEvent(e)
|
||||
}
|
||||
|
|
|
@ -217,6 +217,10 @@ func HttpRequest(url, method string, content interface{}, headers []string, body
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", "Statping")
|
||||
req.Header.Set("Statping-Version", Version)
|
||||
|
||||
var resp *http.Response
|
||||
|
||||
dialer := &net.Dialer{
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.90.55
|
||||
0.90.56
|
||||
|
|
Loading…
Reference in New Issue