static services, additional testing, postman update

pull/753/head^2
hunterlong 2020-07-20 15:02:01 -07:00
parent 6e26c7c6c1
commit e14cfd3dd4
17 changed files with 312 additions and 130 deletions

View File

@ -4,6 +4,10 @@
- Modified Service View page to show data inside cards
- Fixed issue with uptime_data sending incorrect start/end timestamps
- Modified http cache to bypass if url has a "v" query param
- Added "Static Services" (a fake service that requires you to update the online/offline status)
- Added Update Static Service PATCH route (/api/services/{id})
- Modified SASS api endpoints (base, layout, forms, mixins, mobile, variables)
- Added additional testing
# 0.90.60 (07-15-2020)
- Added LETSENCRYPT_ENABLE (boolean) env to enable/disable letsencrypt SSL

69
dev/postman.json vendored
View File

@ -840,7 +840,7 @@
"",
"pm.test(\"View All Services\", function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData.length).to.eql(5);",
" pm.expect(jsonData.length).to.eql(6);",
"});"
],
"type": "text/javascript"
@ -1714,7 +1714,7 @@
"services"
]
},
"description": "Create a new service and begin monitoring."
"description": "View a specific service, this will include the service's failures and checkins.\n\n#### Service Type Field\n- `http` - HTTP Service\n- `tcp` - TCP Service\n- `udp` - UDP Service\n- `icmp` - ICMP Service\n- `grpc` - gRPC Service\n- `static` - Static Service"
},
"response": [
{
@ -1876,6 +1876,69 @@
}
]
},
{
"name": "Update Static Service",
"event": [
{
"listen": "test",
"script": {
"id": "18cfae1e-4025-4338-a734-a552c8ac85ca",
"exec": [
"pm.test(\"Response is ok\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"Update Service\", function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData.status).to.eql(\"success\");",
" pm.expect(jsonData.output.type).to.eql(\"static\");",
" pm.expect(jsonData.output.online).to.eql(false);",
"});"
],
"type": "text/javascript"
}
}
],
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{api_key}}",
"type": "string"
}
]
},
"method": "PATCH",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"online\": false,\n \"latency\": 30500,\n \"issue\": \"This is a failure string you can create\"\n}",
"options": {
"raw": {}
}
},
"url": {
"raw": "{{endpoint}}/api/services/7",
"host": [
"{{endpoint}}"
],
"path": [
"api",
"services",
"7"
]
},
"description": "Update a Static Service by setting it's online status to true or false. If false, you can include a issue string in the `issue` JSON field."
},
"response": []
},
{
"name": "Delete Service Failures",
"event": [
@ -2026,7 +2089,7 @@
]
}
],
"description": "With the Statping API, you can add, remove, edit all your services fields from the API directly. This includes viewing Service chart data for latency/up-time, and even viewing a log of failures. ",
"description": "With the Statping API, you can add, remove, edit all your services fields from the API directly. This includes viewing Service chart data for latency/up-time, and even viewing a log of failures. \n\n### HTTP Services\nA HTTP service is a basic service that sends a HTTP request (GET, POST, PATCH, DELETE, etc) to check if that web service is online or not. You can expect a specific status code, and response body (including regex). \n\n### TCP and UDP Services\nTCP and UDP Services will send a request to the hostname and port of your choice.\n\n### ICMP Ping Services\nICMP Services will send a ICMP (ping) packet to your server to test if it's online.\n\n### gRPC Services\ngRPC Services will request your gRPC server and check the response\n\n### Static Services\nA Static Service is a \"fake\" service that is set online/offline by you.\n",
"auth": {
"type": "bearer",
"bearer": [

View File

@ -6,6 +6,10 @@
{{error}}
</div>
<h6 v-if="directory" id="assets_dir" class="text-muted text-monospace text-sm-center font-1 mb-4">
Asset Directory: {{directory}}
</h6>
<div v-if="loaded && !directory" class="jumbotron jumbotron-fluid">
<div class="text-center col-12">
<h1 class="display-5">Enable Local Assets</h1>
@ -24,17 +28,31 @@
<h3 class="mt-3">Base Theme</h3>
<codemirror v-show="loaded" v-model="base" ref="base" :options="cmOptions" class="codemirrorInput"/>
<h3 class="mt-3">Layout Theme</h3>
<codemirror v-show="loaded" v-model="layout" ref="layout" :options="cmOptions" class="codemirrorInput"/>
<h3 class="mt-3">Forms Theme</h3>
<codemirror v-show="loaded" v-model="forms" ref="forms" :options="cmOptions" class="codemirrorInput"/>
<h3 class="mt-3">Mixins</h3>
<codemirror v-show="loaded" v-model="mixins" ref="mixins" :options="cmOptions" class="codemirrorInput"/>
<h3 class="mt-3">Mobile Overwrites</h3>
<codemirror v-show="loaded" v-model="mobile" ref="mobile" :options="cmOptions" class="codemirrorInput"/>
<button id="save_assets" @submit.prevent="saveAssets" type="submit" class="btn btn-primary btn-block mt-2" :disabled="pending">{{pending ? "Saving..." : "Save Style"}}</button>
<button id="delete_assets" v-if="directory" @click.prevent="deleteAssets" href="#" class="btn btn-danger btn-block confirm-btn" :disabled="pending">Delete Local Assets</button>
<h6 id="assets_dir" class="text-muted text-monospace text-sm-center font-1 mt-3">
Asset Directory: {{directory}}
</h6>
</form>
</div>
<div class="card-footer">
<div class="row">
<div class="col-6">
<button id="save_assets" @click.prevent="saveAssets" type="submit" class="btn btn-primary btn-block" :disabled="pending">{{pending ? "Saving..." : "Save Styles"}}</button>
</div>
<div class="col-6">
<button id="delete_assets" v-if="directory" @click.prevent="deleteAssets" class="btn btn-danger btn-block confirm-btn" :disabled="pending">Delete Local Assets</button>
</div>
</div>
</div>
</div>
</template>
@ -62,6 +80,9 @@
data () {
return {
base: null,
layout: null,
forms: null,
mixins: null,
vars: null,
mobile: null,
error: null,
@ -90,6 +111,9 @@
this.$refs.vars.codemirror.refresh()
this.$refs.base.codemirror.refresh()
this.$refs.mobile.codemirror.refresh()
this.$refs.layout.codemirror.refresh()
this.$refs.forms.codemirror.refresh()
this.$refs.mixins.codemirror.refresh()
}
},
async fetchTheme() {
@ -101,6 +125,9 @@
this.base = theme.base
this.vars = theme.variables
this.mobile = theme.mobile
this.layout = theme.layout
this.forms = theme.forms
this.mixins = theme.mixins
}
this.pending = false
this.loaded = true
@ -127,7 +154,14 @@
},
async saveAssets() {
this.pending = true
const data = {base: this.base, variables: this.vars, mobile: this.mobile}
const data = {
base: this.base,
layout: this.layout,
forms: this.forms,
mixins: this.mixins,
variables: this.vars,
mobile: this.mobile
}
let resp
try {
resp = await Api.theme_save(data)

View File

@ -1,64 +1,70 @@
<template>
<form @submit.prevent="saveSettings">
<div class="form-group">
<label>{{ $t('settings.name') }}</label>
<input v-model="core.name" type="text" class="form-control" placeholder="Great Uptime" id="project">
<div class="card">
<div class="card-header">Statping Settings</div>
<div class="card-body">
<div class="form-group">
<label>{{ $t('settings.name') }}</label>
<input v-model="core.name" type="text" class="form-control" placeholder="Great Uptime" id="project">
</div>
<div class="form-group">
<label>{{ $t('settings.description') }}</label>
<input v-model="core.description" type="text" class="form-control" placeholder="Great Uptime" id="description">
</div>
<div class="form-group row">
<div class="col-8 col-sm-9">
<label>{{ $t('domain') }}</label>
<input v-model="core.domain" type="url" class="form-control" id="domain">
</div>
<div class="col-4 col-sm-3 mt-sm-1 mt-0">
<label class="d-inline d-sm-none">Enable CDN</label>
<label class="d-none d-sm-block">Enable CDN</label>
<span @click="core.using_cdn = !!core.using_cdn" class="switch" id="using_cdn">
<input v-model="core.using_cdn" type="checkbox" name="using_cdn" class="switch" id="switch-normal" :checked="core.using_cdn">
<label for="switch-normal"></label>
</span>
</div>
</div>
<div class="form-group">
<label>{{ $t('settings.footer') }}</label>
<textarea v-model="core.footer" rows="4" class="form-control" id="footer">{{core.footer}}</textarea>
<small class="form-text text-muted">{{ $t('settings.footer_notes') }}</small>
</div>
<div class="form-group">
<label>{{ $t('setup.language') }}</label>
<select v-model="core.language" class="form-control">
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="ru">Russian</option>
<option value="de">German</option>
</select>
</div>
<div class="form-group row mt-3">
<label class="col-sm-10 col-form-label">{{ $t('settings.error_reporting') }}</label>
<div class="col-sm-2 float-right">
<span @click="core.allow_reports = !!core.allow_reports" class="switch" id="allow_report">
<input v-model="core.allow_reports" type="checkbox" name="allow_report" class="switch" id="switch_allow_report" :checked="core.allow_reports">
<label for="switch_allow_report"></label>
</span>
</div>
<div class="col-12">
<small>{{ $t('settings.error_reporting_notes') }}</small>
</div>
</div>
</div>
<div class="form-group">
<label>{{ $t('settings.description') }}</label>
<input v-model="core.description" type="text" class="form-control" placeholder="Great Uptime" id="description">
<div class="card-footer">
<button @click.prevent="saveSettings" id="save_core" type="submit" class="btn btn-primary btn-block" v-bind:disabled="loading">
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{ $t('settings.save') }}
</button>
</div>
<div class="form-group row">
<div class="col-8 col-sm-9">
<label>{{ $t('domain') }}</label>
<input v-model="core.domain" type="url" class="form-control" id="domain">
</div>
<div class="col-4 col-sm-3 mt-sm-1 mt-0">
<label class="d-inline d-sm-none">Enable CDN</label>
<label class="d-none d-sm-block">Enable CDN</label>
<span @click="core.using_cdn = !!core.using_cdn" class="switch" id="using_cdn">
<input v-model="core.using_cdn" type="checkbox" name="using_cdn" class="switch" id="switch-normal" :checked="core.using_cdn">
<label for="switch-normal"></label>
</span>
</div>
</div>
<div class="form-group">
<label>{{ $t('settings.footer') }}</label>
<textarea v-model="core.footer" rows="4" class="form-control" id="footer">{{core.footer}}</textarea>
<small class="form-text text-muted">{{ $t('settings.footer_notes') }}</small>
</div>
<div class="form-group">
<label>{{ $t('setup.language') }}</label>
<select v-model="core.language" class="form-control">
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="ru">Russian</option>
<option value="de">German</option>
</select>
</div>
<div class="form-group row mt-3">
<label class="col-sm-10 col-form-label">{{ $t('settings.error_reporting') }}</label>
<div class="col-sm-2 float-right">
<span @click="core.allow_reports = !!core.allow_reports" class="switch" id="allow_report">
<input v-model="core.allow_reports" type="checkbox" name="allow_report" class="switch" id="switch_allow_report" :checked="core.allow_reports">
<label for="switch_allow_report"></label>
</span>
</div>
<div class="col-12">
<small>{{ $t('settings.error_reporting_notes') }}</small>
</div>
</div>
<button @click.prevent="saveSettings" id="save_core" type="submit" class="btn btn-primary btn-block mt-3" v-bind:disabled="loading">
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{ $t('settings.save') }}
</button>
</div>
</form>
</template>

View File

@ -19,6 +19,7 @@
<option value="udp">UDP Service</option>
<option value="icmp">ICMP Ping</option>
<option value="grpc">gRPC Service</option>
<option value="static">Static Service</option>
</select>
<small class="form-text text-muted">Use HTTP if you are checking a website or use TCP if you are checking a server</small>
</div>
@ -52,7 +53,7 @@
</div>
</div>
<div class="form-group row">
<div v-if="service.type !== 'static'" class="form-group row">
<label for="service_interval" class="col-sm-4 col-form-label">Check Interval</label>
<div class="col-sm-6">
<span class="slider-info">{{secondsHumanize(service.check_interval)}}</span>
@ -67,7 +68,7 @@
</div>
</div>
<div class="card contain-card mb-4">
<div v-if="service.type !== 'static'" class="card contain-card mb-4">
<div class="card-header">Request Details</div>
<div class="card-body">

View File

@ -60,12 +60,7 @@
<div class="tab-content" id="v-pills-tabContent">
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-home-tab'), show: liClass('v-pills-home-tab')}" id="v-pills-home" role="tabpanel" aria-labelledby="v-pills-home-tab">
<div class="card">
<div class="card-header">Statping Settings</div>
<div class="card-body">
<CoreSettings/>
</div>
</div>
<CoreSettings/>
<div class="card mt-3">
<div class="card-header">API Settings</div>
@ -82,10 +77,12 @@
<small class="form-text text-muted">API Secret is used for read, create, update and delete routes</small>
<small class="form-text text-muted">You can Regenerate API Keys if you need to.</small>
<button id="regenkeys" @click="renewApiKeys" class="btn btn-sm btn-danger mt-2">Regenerate API Keys</button>
</div>
</div>
</div>
<div class="card-footer">
<button id="regenkeys" @click="renewApiKeys" class="btn btn-sm btn-danger float-right">Regenerate API Keys</button>
</div>
</div>
</div>

8
go.mod
View File

@ -5,9 +5,7 @@ go 1.14
require (
github.com/GeertJohan/go.rice v1.0.0
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dustin/go-humanize v1.0.0
github.com/fatih/structs v1.1.0
github.com/foomo/simplecert v1.7.5
github.com/foomo/tlsconfig v0.0.0-20180418120404-b67861b076c9
@ -15,16 +13,12 @@ require (
github.com/getsentry/sentry-go v0.5.1
github.com/go-mail/mail v2.3.1+incompatible
github.com/gorilla/mux v1.7.4
github.com/gorilla/securecookie v1.1.1
github.com/hako/durafmt v0.0.0-20200605151348-3a43fc422dd9
github.com/jinzhu/gorm v1.9.12
github.com/magiconair/properties v1.8.1
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/mitchellh/mapstructure v1.2.2 // indirect
github.com/pelletier/go-toml v1.7.0 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.1.0
github.com/russross/blackfriday/v2 v2.0.1
github.com/sirupsen/logrus v1.5.0
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1 // indirect
@ -36,10 +30,8 @@ require (
github.com/t-tiger/gorm-bulk-insert/v2 v2.0.1
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/text v0.3.2 // indirect
google.golang.org/grpc v1.28.1
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.55.0 // indirect
gopkg.in/mail.v2 v2.3.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.2.8

16
go.sum
View File

@ -54,6 +54,7 @@ github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSW
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8=
@ -98,8 +99,6 @@ github.com/aliyun/alibaba-cloud-sdk-go v1.61.112/go.mod h1:pUKYbK5JQ+1Dfxk80P0qx
github.com/aliyun/alibaba-cloud-sdk-go v1.61.131 h1:ePFkFbwr/u1HM9/p+azqI5UaxwY1hYKv+H8dkaRCLs4=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.131/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d h1:ZX0t+GA3MWiP7LWt5xWOphWRQd5JwL4VW5uLW83KM8g=
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d/go.mod h1:EcJ034SpbWy4heOSDiBZJRn3b5wKJM1b4sFfYeVAkI4=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.30.20 h1:ktsy2vodSZxz/arYqo7DlpkIeNohHL+4Rmjdo7YGtrE=
github.com/aws/aws-sdk-go v1.30.20/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
@ -287,18 +286,18 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hako/durafmt v0.0.0-20200605151348-3a43fc422dd9 h1:IEhIezS5kcD4ZzOwVl8dAyJ9JCi4Xo6tg44Vj/z7UsI=
github.com/hako/durafmt v0.0.0-20200605151348-3a43fc422dd9/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
@ -368,6 +367,7 @@ github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -439,6 +439,7 @@ github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@ -561,6 +562,7 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@ -613,6 +615,7 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
@ -714,6 +717,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -753,8 +757,6 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -829,6 +831,7 @@ google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO50
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -887,6 +890,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=

View File

@ -33,12 +33,15 @@ func logsHandler(w http.ResponseWriter, r *http.Request) {
type themeApi struct {
Directory string `json:"directory,omitempty"`
Base string `json:"base"`
Variables string `json:"variables"`
Forms string `json:"forms"`
Layout string `json:"layout"`
Mixins string `json:"mixins"`
Mobile string `json:"mobile"`
Variables string `json:"variables"`
}
func apiThemeViewHandler(w http.ResponseWriter, r *http.Request) {
var base, variables, mobile, dir string
var base, forms, layout, mixins, variables, mobile, dir string
assets := utils.Directory + "/assets"
if _, err := os.Stat(assets); err == nil {
@ -49,10 +52,16 @@ func apiThemeViewHandler(w http.ResponseWriter, r *http.Request) {
base, _ = utils.OpenFile(dir + "/scss/base.scss")
variables, _ = utils.OpenFile(dir + "/scss/variables.scss")
mobile, _ = utils.OpenFile(dir + "/scss/mobile.scss")
layout, _ = utils.OpenFile(dir + "/scss/layout.scss")
forms, _ = utils.OpenFile(dir + "/scss/forms.scss")
mixins, _ = utils.OpenFile(dir + "/scss/mixin.scss")
} else {
base, _ = source.TmplBox.String("scss/base.scss")
variables, _ = source.TmplBox.String("scss/variables.scss")
mobile, _ = source.TmplBox.String("scss/mobile.scss")
layout, _ = source.TmplBox.String("scss/layout.scss")
forms, _ = source.TmplBox.String("scss/forms.scss")
mixins, _ = source.TmplBox.String("scss/mixin.scss")
}
resp := &themeApi{
@ -60,6 +69,9 @@ func apiThemeViewHandler(w http.ResponseWriter, r *http.Request) {
Base: base,
Variables: variables,
Mobile: mobile,
Layout: layout,
Forms: forms,
Mixins: mixins,
}
returnJson(resp, w, r)
}

View File

@ -128,6 +128,7 @@ func Router() *mux.Router {
api.Handle("/api/services/{id}", scoped(apiServiceHandler)).Methods("GET")
api.Handle("/api/reorder/services", authenticated(reorderServiceHandler, false)).Methods("POST")
api.Handle("/api/services/{id}", authenticated(apiServiceUpdateHandler, false)).Methods("POST")
api.Handle("/api/services/{id}", authenticated(apiServicePatchHandler, false)).Methods("PATCH")
api.Handle("/api/services/{id}", authenticated(apiServiceDeleteHandler, false)).Methods("DELETE")
api.Handle("/api/services/{id}/failures", scoped(apiServiceFailuresHandler)).Methods("GET")
api.Handle("/api/services/{id}/failures", authenticated(servicesDeleteFailuresHandler, false)).Methods("DELETE")

View File

@ -74,6 +74,46 @@ func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
sendJsonAction(service, "create", w, r)
}
type servicePatchReq struct {
Online bool `json:"online"`
Issue string `json:"issue,omitempty"`
Latency int64 `json:"latency,omitempty"`
}
func apiServicePatchHandler(w http.ResponseWriter, r *http.Request) {
service, err := findService(r)
if err != nil {
sendErrorJson(err, w, r)
return
}
var req servicePatchReq
if err := DecodeJSON(r, &req); err != nil {
sendErrorJson(err, w, r)
return
}
service.Online = req.Online
service.Latency = req.Latency
issueDefault := "Service was triggered to be offline"
if req.Issue != "" {
issueDefault = req.Issue
}
if !req.Online {
services.RecordFailure(service, issueDefault)
} else {
services.RecordSuccess(service)
}
if err := service.Update(); err != nil {
sendErrorJson(err, w, r)
return
}
sendJsonAction(service, "update", w, r)
}
func apiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
service, err := findService(r)
if err != nil {
@ -84,9 +124,7 @@ func apiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
sendErrorJson(err, w, r)
return
}
err = service.Update()
if err != nil {
if err := service.Update(); err != nil {
sendErrorJson(err, w, r)
return
}

View File

@ -61,11 +61,11 @@ func TestApiServiceRoutes(t *testing.T) {
Method: "GET",
ExpectedContains: []string{`"name":"Google"`},
ExpectedStatus: 200,
ResponseLen: 6,
ResponseLen: 7,
BeforeTest: SetTestENV,
FuncTest: func(t *testing.T) error {
count := len(services.Services())
if count != 6 {
if count != 7 {
return errors.Errorf("incorrect services count: %d", count)
}
return nil
@ -77,11 +77,11 @@ func TestApiServiceRoutes(t *testing.T) {
Method: "GET",
ExpectedContains: []string{`"name":"Google"`},
ExpectedStatus: 200,
ResponseLen: 5,
ResponseLen: 6,
BeforeTest: UnsetTestENV,
FuncTest: func(t *testing.T) error {
count := len(services.Services())
if count != 6 {
if count != 7 {
return errors.Errorf("incorrect services count: %d", count)
}
return nil
@ -261,10 +261,10 @@ func TestApiServiceRoutes(t *testing.T) {
"order_id": 0
}`,
ExpectedStatus: 200,
ExpectedContains: []string{Success, `"type":"service","method":"create"`, `"public":false`, `"group_id":1`},
ExpectedContains: []string{Success, MethodCreate, `"type":"service",`, `"public":false`, `"group_id":1`},
FuncTest: func(t *testing.T) error {
count := len(services.Services())
if count != 7 {
if count != 8 {
return errors.Errorf("incorrect services count: %d", count)
}
return nil
@ -326,7 +326,27 @@ func TestApiServiceRoutes(t *testing.T) {
ExpectedContains: []string{Success, MethodDelete},
FuncTest: func(t *testing.T) error {
count := len(services.Services())
if count != 6 {
if count != 7 {
return errors.Errorf("incorrect services count: %d", count)
}
return nil
},
SecureRoute: true,
},
{
Name: "Statping Patch Static Service",
URL: "/api/services/7",
Method: "PATCH",
ExpectedStatus: 200,
Body: `{
"online": false,
"latency": 30500,
"issue": "This is a failure string you can create"
},`,
ExpectedContains: []string{Success, MethodUpdate},
FuncTest: func(t *testing.T) error {
count := len(services.Services())
if count != 7 {
return errors.Errorf("incorrect services count: %d", count)
}
return nil

View File

@ -1,9 +1,9 @@
package notifiers
import (
"github.com/magiconair/properties/assert"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/services"
"github.com/stretchr/testify/assert"
"testing"
)

View File

@ -2,6 +2,7 @@ package configs
import (
"github.com/statping/statping/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
@ -51,3 +52,9 @@ func TestPostgresConfig(t *testing.T) {
err := Connect(postgres, false)
require.Nil(t, err)
}
func TestFileSQLFile(t *testing.T) {
file, err := findSQLin(utils.Directory)
require.Nil(t, err)
assert.Equal(t, "statping.db", file)
}

View File

@ -2,7 +2,6 @@ package services
import (
"fmt"
humanize "github.com/dustin/go-humanize"
"github.com/statping/statping/types/failures"
"strings"
"time"
@ -20,13 +19,6 @@ func (s *Service) FailuresSince(t time.Time) failures.Failurer {
return failures.Since(t, s)
}
func (s Service) DowntimeAgo() string {
if s.LastOnline.IsZero() {
return "Never been online"
}
return humanize.Time(s.LastOnline)
}
func (s Service) DowntimeText() string {
last := s.AllFailures().Last()
if last == nil {

View File

@ -94,7 +94,7 @@ func CheckIcmp(s *Service, record bool) (*Service, error) {
dur, err := utils.Ping(s.Domain, s.Timeout)
if err != nil {
if record {
recordFailure(s, fmt.Sprintf("Could not send ICMP to service %v, %v", s.Domain, err))
RecordFailure(s, fmt.Sprintf("Could not send ICMP to service %v, %v", s.Domain, err))
}
return s, err
}
@ -104,7 +104,7 @@ func CheckIcmp(s *Service, record bool) (*Service, error) {
s.LastResponse = ""
s.Online = true
if record {
recordSuccess(s)
RecordSuccess(s)
}
return s, nil
}
@ -118,7 +118,7 @@ func CheckGrpc(s *Service, record bool) (*Service, error) {
dnsLookup, err := dnsCheck(s)
if err != nil {
if record {
recordFailure(s, fmt.Sprintf("Could not get IP address for GRPC service %v, %v", s.Domain, err))
RecordFailure(s, fmt.Sprintf("Could not get IP address for GRPC service %v, %v", s.Domain, err))
}
return s, err
}
@ -137,13 +137,13 @@ func CheckGrpc(s *Service, record bool) (*Service, error) {
}
if err != nil {
if record {
recordFailure(s, fmt.Sprintf("Dial Error %v", err))
RecordFailure(s, fmt.Sprintf("Dial Error %v", err))
}
return s, err
}
if err := conn.Close(); err != nil {
if record {
recordFailure(s, fmt.Sprintf("%v Socket Close Error %v", strings.ToUpper(s.Type), err))
RecordFailure(s, fmt.Sprintf("%v Socket Close Error %v", strings.ToUpper(s.Type), err))
}
return s, err
}
@ -151,7 +151,7 @@ func CheckGrpc(s *Service, record bool) (*Service, error) {
s.LastResponse = ""
s.Online = true
if record {
recordSuccess(s)
RecordSuccess(s)
}
return s, nil
}
@ -165,7 +165,7 @@ func CheckTcp(s *Service, record bool) (*Service, error) {
dnsLookup, err := dnsCheck(s)
if err != nil {
if record {
recordFailure(s, fmt.Sprintf("Could not get IP address for TCP service %v, %v", s.Domain, err))
RecordFailure(s, fmt.Sprintf("Could not get IP address for TCP service %v, %v", s.Domain, err))
}
return s, err
}
@ -189,7 +189,7 @@ func CheckTcp(s *Service, record bool) (*Service, error) {
conn, err := net.DialTimeout(s.Type, domain, time.Duration(s.Timeout)*time.Second)
if err != nil {
if record {
recordFailure(s, fmt.Sprintf("Dial Error: %v", err))
RecordFailure(s, fmt.Sprintf("Dial Error: %v", err))
}
return s, err
}
@ -203,7 +203,7 @@ func CheckTcp(s *Service, record bool) (*Service, error) {
conn, err := tls.DialWithDialer(dialer, s.Type, domain, tlsConfig)
if err != nil {
if record {
recordFailure(s, fmt.Sprintf("Dial Error: %v", err))
RecordFailure(s, fmt.Sprintf("Dial Error: %v", err))
}
return s, err
}
@ -214,7 +214,7 @@ func CheckTcp(s *Service, record bool) (*Service, error) {
s.LastResponse = ""
s.Online = true
if record {
recordSuccess(s)
RecordSuccess(s)
}
return s, nil
}
@ -232,7 +232,7 @@ func CheckHttp(s *Service, record bool) (*Service, error) {
dnsLookup, err := dnsCheck(s)
if err != nil {
if record {
recordFailure(s, fmt.Sprintf("Could not get IP address for domain %v, %v", s.Domain, err))
RecordFailure(s, fmt.Sprintf("Could not get IP address for domain %v, %v", s.Domain, err))
}
return s, err
}
@ -284,7 +284,7 @@ func CheckHttp(s *Service, record bool) (*Service, error) {
content, res, err = utils.HttpRequest(s.Domain, s.Method, contentType, headers, data, timeout, s.VerifySSL.Bool, customTLS)
if err != nil {
if record {
recordFailure(s, fmt.Sprintf("HTTP Error %v", err))
RecordFailure(s, fmt.Sprintf("HTTP Error %v", err))
}
return s, err
}
@ -301,26 +301,26 @@ func CheckHttp(s *Service, record bool) (*Service, error) {
}
if !match {
if record {
recordFailure(s, fmt.Sprintf("HTTP Response Body did not match '%v'", s.Expected))
RecordFailure(s, fmt.Sprintf("HTTP Response Body did not match '%v'", s.Expected))
}
return s, err
}
}
if s.ExpectedStatus != res.StatusCode {
if record {
recordFailure(s, fmt.Sprintf("HTTP Status Code %v did not match %v", res.StatusCode, s.ExpectedStatus))
RecordFailure(s, fmt.Sprintf("HTTP Status Code %v did not match %v", res.StatusCode, s.ExpectedStatus))
}
return s, err
}
if record {
recordSuccess(s)
RecordSuccess(s)
}
s.Online = true
return s, err
}
// recordSuccess will create a new 'hit' record in the database for a successful/online service
func recordSuccess(s *Service) {
// RecordSuccess will create a new 'hit' record in the database for a successful/online service
func RecordSuccess(s *Service) {
s.LastOnline = utils.Now()
s.Online = true
hit := &hits.Hit{
@ -371,8 +371,8 @@ func sendSuccess(s *Service) {
s.notifyAfterCount = 0
}
// recordFailure will create a new 'Failure' record in the database for a offline service
func recordFailure(s *Service, issue string) {
// RecordFailure will create a new 'Failure' record in the database for a offline service
func RecordFailure(s *Service, issue string) {
s.LastOffline = utils.Now()
fail := &failures.Failure{

View File

@ -176,5 +176,16 @@ func Samples() error {
return err
}
s7 := &Service{
Name: "Static Service",
Type: "static",
Order: 7,
Public: null.NewNullBool(true),
CreatedAt: createdOn,
}
if err := s7.Create(); err != nil {
return err
}
return nil
}