mirror of https://github.com/statping/statping
Merge pull request #755 from statping/checkin-updates
v0.90.61 - SASS templating, Checkin updates (backend and UI), UI fixespull/753/head
commit
6e26c7c6c1
|
@ -1,3 +1,4 @@
|
|||
github: hunterlong
|
||||
patreon: statping
|
||||
custom: ['https://www.nfoservers.com/donate.pl?force_recipient=1&recipient=info%40socialeck.com', 'https://opencollective.com/statping', 'https://www.buymeacoffee.com/hunterlong']
|
||||
open_collective: statping
|
||||
custom: ['https://www.nfoservers.com/donate.pl?force_recipient=1&recipient=info%40socialeck.com', 'https://www.buymeacoffee.com/hunterlong']
|
||||
|
|
|
@ -328,12 +328,12 @@ jobs:
|
|||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
id: cache
|
||||
id: buildx-docker-master
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
key: buildx-docker-master
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
buildx-docker-master
|
||||
|
||||
- name: Docker Build :base
|
||||
run: make buildx-base
|
||||
|
|
|
@ -428,12 +428,12 @@ jobs:
|
|||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
id: cache
|
||||
id: buildx-docker
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
key: buildx-docker
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
buildx-docker
|
||||
|
||||
- name: Docker Build :base
|
||||
run: make buildx-base
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
# 0.90.61 (07-19-2020)
|
||||
- Modified sass layouts, organized and split up sections
|
||||
- Modified Checkins to seconds rather than milliseconds (for cronjob)
|
||||
- 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
|
||||
|
||||
# 0.90.60 (07-15-2020)
|
||||
- Added LETSENCRYPT_ENABLE (boolean) env to enable/disable letsencrypt SSL
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -353,5 +353,5 @@ buildx-base: multiarch
|
|||
multiarch:
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
|
||||
.PHONY: all build multiarch build-all buildx-base buildx-dev buildx-latest build-alpine test-all test test-api docker frontend up down print_details lite sentry-release snapcraft build-linux build-mac build-win build-all postman
|
||||
.PHONY: all build certs multiarch build-all buildx-base buildx-dev buildx-latest build-alpine test-all test test-api docker frontend up down print_details lite sentry-release snapcraft build-linux build-mac build-win build-all postman
|
||||
.SILENT: travis_s3_creds
|
||||
|
|
|
@ -4911,8 +4911,7 @@
|
|||
" var first = jsonData[0];",
|
||||
" var id = pm.globals.get(\"checkin_id\");",
|
||||
" pm.expect(first.name).to.eql(\"Demo Checkin 1\");",
|
||||
" pm.expect(first.grace).to.eql(300);",
|
||||
" pm.expect(first.interval).to.eql(300);",
|
||||
" pm.expect(first.interval).to.eql(3);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
@ -4990,8 +4989,7 @@
|
|||
" pm.expect(jsonData.status).to.eql(\"success\");",
|
||||
" pm.expect(jsonData.type).to.eql(\"checkin\");",
|
||||
" pm.expect(jsonData.output.name).to.eql(\"Server Checkin\");",
|
||||
" pm.expect(jsonData.output.grace).to.eql(60);",
|
||||
" pm.expect(jsonData.output.interval).to.eql(900);",
|
||||
" pm.expect(jsonData.output.interval).to.eql(3);",
|
||||
" var id = jsonData.output.api_key;",
|
||||
" pm.globals.set(\"checkin_id\", id);",
|
||||
"});"
|
||||
|
@ -5022,7 +5020,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"service_id\": 2,\n \"name\": \"Server Checkin\",\n \"interval\": 900,\n \"grace\": 60\n}",
|
||||
"raw": "{\n \"service_id\": 2,\n \"name\": \"Server Checkin\",\n \"interval\": 3\n}",
|
||||
"options": {
|
||||
"raw": {}
|
||||
}
|
||||
|
@ -5182,8 +5180,7 @@
|
|||
" var id = pm.globals.get(\"checkin_id\");",
|
||||
" pm.expect(jsonData.name).to.eql(\"Server Checkin\");",
|
||||
" pm.expect(jsonData.api_key).to.eql(id);",
|
||||
" pm.expect(jsonData.grace).to.eql(60);",
|
||||
" pm.expect(jsonData.interval).to.eql(900);",
|
||||
" pm.expect(jsonData.interval).to.eql(3);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
|
|
@ -113,10 +113,6 @@ HTML, BODY {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
|
|
@ -155,6 +155,10 @@ class Api {
|
|||
return axios.delete('api/incidents/'+incident.id).then(response => (response.data))
|
||||
}
|
||||
|
||||
async checkin(api) {
|
||||
return axios.get('api/checkins/'+api).then(response => (response.data))
|
||||
}
|
||||
|
||||
async checkin_create(data) {
|
||||
return axios.post('api/checkins', data).then(response => (response.data))
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
@import 'variables';
|
||||
|
||||
HTML,BODY {
|
||||
background-color: $background-color;
|
||||
}
|
||||
@import 'mixin';
|
||||
|
||||
.index-chart {
|
||||
height: $service-card-height;
|
||||
|
@ -26,26 +23,11 @@ HTML,BODY {
|
|||
box-shadow: 0px 3px 6px 1px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.btn-xs {
|
||||
font-size: 8pt;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.copy-btn BUTTON {
|
||||
background-color: white;
|
||||
margin: 6px;
|
||||
height: 26px;
|
||||
font-size: 8pt;
|
||||
padding: 5px 7px;
|
||||
border: 1px solid #a7a7a7;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.dim {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
@ -55,41 +37,6 @@ HTML,BODY {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* The slider itself */
|
||||
.slider {
|
||||
-webkit-appearance: none; /* Override default CSS styles */
|
||||
appearance: none;
|
||||
width: 100%; /* Full-width */
|
||||
height: 5px; /* Specified height */
|
||||
background: #d3d3d3; /* Grey background */
|
||||
outline: none; /* Remove outline */
|
||||
-webkit-transition: .2s; /* 0.2 seconds transition on hover */
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
/* Mouse-over effects */
|
||||
.slider:hover {
|
||||
opacity: 1; /* Fully shown on mouse-over */
|
||||
}
|
||||
|
||||
/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none; /* Override default look */
|
||||
appearance: none;
|
||||
border-radius: 50%;
|
||||
width: 20px; /* Set a specific slider handle width */
|
||||
height: 20px; /* Slider handle height */
|
||||
background: #4CAF50; /* Green background */
|
||||
cursor: pointer; /* Cursor on hover */
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 15px; /* Set a specific slider handle width */
|
||||
height: 15px; /* Slider handle height */
|
||||
background: #4CAF50; /* Green background */
|
||||
cursor: pointer; /* Cursor on hover */
|
||||
}
|
||||
|
||||
@-o-keyframes fadeIt {
|
||||
0% { background-color: #f5f5f5; }
|
||||
50% { background-color: #f2f2f2; }
|
||||
|
@ -151,18 +98,6 @@ HTML,BODY {
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
.contain-card {
|
||||
|
||||
.card-header {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
background-color: rgba(239, 239, 239, 0.65);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.dropup .dropdown-menu {
|
||||
border-radius: 8px 8px 8px 0;
|
||||
background-color: #efefef;
|
||||
|
@ -202,13 +137,6 @@ HTML,BODY {
|
|||
opacity: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 25px;
|
||||
max-width: $max-width;
|
||||
box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
color: $title-color;
|
||||
}
|
||||
|
@ -226,10 +154,6 @@ HTML,BODY {
|
|||
padding: 5px 7px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 1.3;
|
||||
font-size: 0.75rem;
|
||||
|
@ -274,20 +198,6 @@ HTML,BODY {
|
|||
color: $service-description-color
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-decoration: none;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer A {
|
||||
color: $footer-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer A:HOVER {
|
||||
color: #6d6d6d;
|
||||
}
|
||||
|
||||
.font-0 {
|
||||
font-size: 0.35rem;
|
||||
}
|
||||
|
@ -342,22 +252,6 @@ HTML,BODY {
|
|||
}
|
||||
}
|
||||
|
||||
.card-body .badge {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
border-radius: $global-border-radius;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: $global-border-radius;
|
||||
}
|
||||
|
||||
.mini_success {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.no-decoration {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
|
@ -368,10 +262,6 @@ HTML,BODY {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.mini_error {
|
||||
background-color: #ffbbbb;
|
||||
}
|
||||
|
||||
.btn-white {
|
||||
background-color: white;
|
||||
border: 1px solid #d8d8d8;
|
||||
|
@ -418,27 +308,6 @@ HTML,BODY {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.card {
|
||||
background-color: $service-background;
|
||||
border: $service-border;
|
||||
box-shadow: 0px 3px 6px 1px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-body H4 A {
|
||||
color: $service-title;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-title A {
|
||||
color: $service-title;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 240px;
|
||||
|
@ -458,79 +327,6 @@ HTML,BODY {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.inputTags-field {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
padding-top: .13rem;
|
||||
}
|
||||
|
||||
input.inputTags-field:focus {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.inputTags-list {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: calc(2.25rem + 2px);
|
||||
padding: .2rem .35rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
}
|
||||
|
||||
.inputTags-item {
|
||||
background-color: #3aba39;
|
||||
margin-right: 5px;
|
||||
padding: 5px 8px;
|
||||
font-size: 10pt;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.inputTags-item .close-item {
|
||||
margin-left: 6px;
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@mixin dynamic-color-hov($color) {
|
||||
&.dyn-dark {
|
||||
background-color: darken($color, 12%) !important;
|
||||
border-color: darken($color, 17%) !important;
|
||||
}
|
||||
&.dyn-dark:HOVER {
|
||||
background-color: darken($color, 17%) !important;
|
||||
border-color: darken($color, 20%) !important;
|
||||
}
|
||||
&.dyn-light {
|
||||
background-color: lighten($color, 12%) !important;
|
||||
border-color: lighten($color, 17%) !important;
|
||||
}
|
||||
&.dyn-light:HOVER {
|
||||
background-color: lighten($color, 17%) !important;
|
||||
border-color: lighten($color, 20%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dynamic-color($color) {
|
||||
&.dyn-dark {
|
||||
background-color: darken($color, 12%) !important;
|
||||
border-color: darken($color, 17%) !important;
|
||||
}
|
||||
&.dyn-light {
|
||||
background-color: lighten($color, 12%) !important;
|
||||
border-color: lighten($color, 17%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn-primary {
|
||||
background-color: $primary-color;
|
||||
border-color: darken($primary-color, 17%);
|
||||
|
@ -564,18 +360,6 @@ HTML,BODY {
|
|||
background-color: darken($danger-color, 10%) !important;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show>.nav-link {
|
||||
background-color: $nav-tab-color;
|
||||
}
|
||||
|
||||
.nav-pills A {
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.nav-pills I {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
@keyframes fadeInOut {
|
||||
0% { opacity:1; }
|
||||
50% { opacity:0.3; }
|
||||
|
@ -600,181 +384,13 @@ HTML,BODY {
|
|||
-webkit-animation: fadeInOut 1s infinite;
|
||||
-moz-animation: fadeInOut 1s infinite;
|
||||
-o-animation: fadeInOut 1s infinite;
|
||||
animation: fadeInOut 21 infinite;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
/* Bootstrap Settings */
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
overflow: auto;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
/* Code Mirror Settings */
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
height:80vh;
|
||||
}
|
||||
|
||||
.CodeMirror-focused {
|
||||
/* Bootstrap Settings */
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
}
|
||||
|
||||
.switch {
|
||||
font-size: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
.switch input {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
background: none;
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
.switch input + label {
|
||||
position: relative;
|
||||
min-width: calc(calc(2.375rem * .8) * 2);
|
||||
border-radius: calc(2.375rem * .8);
|
||||
height: calc(2.375rem * .8);
|
||||
line-height: calc(2.375rem * .8);
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch input + label::before,
|
||||
.switch input + label::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(calc(2.375rem * .8) * 2);
|
||||
bottom: 0;
|
||||
display: block;
|
||||
}
|
||||
.switch input + label::before {
|
||||
right: 0;
|
||||
background-color: #dee2e6;
|
||||
border-radius: calc(2.375rem * .8);
|
||||
transition: 0.2s all;
|
||||
}
|
||||
.switch input + label::after {
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
transition: 0.2s all;
|
||||
}
|
||||
.switch-rd-gr input:checked + label::before {
|
||||
background-color: #29b10c !important;
|
||||
}
|
||||
.switch input:checked + label::before {
|
||||
background-color: #08d;
|
||||
}
|
||||
.switch input:checked + label::after {
|
||||
margin-left: calc(2.375rem * .8);
|
||||
}
|
||||
.switch input:focus + label::before {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25);
|
||||
}
|
||||
.switch input:disabled + label {
|
||||
color: #868e96;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.switch input:disabled + label::before {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
.switch.switch-sm {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.switch.switch-sm input + label {
|
||||
min-width: calc(calc(1.9375rem * .8) * 2);
|
||||
height: calc(1.9375rem * .8);
|
||||
line-height: calc(1.9375rem * .8);
|
||||
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch.switch-sm input + label::before {
|
||||
width: calc(calc(1.9375rem * .8) * 2);
|
||||
}
|
||||
.switch.switch-sm input + label::after {
|
||||
width: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
}
|
||||
.switch.switch-sm input:checked + label::after {
|
||||
margin-left: calc(1.9375rem * .8);
|
||||
}
|
||||
.switch.switch-lg {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.switch.switch-lg input + label {
|
||||
min-width: calc(calc(3rem * .8) * 2);
|
||||
height: calc(3rem * .8);
|
||||
line-height: calc(3rem * .8);
|
||||
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch.switch-lg input + label::before {
|
||||
width: calc(calc(3rem * .8) * 2);
|
||||
}
|
||||
.switch.switch-lg input + label::after {
|
||||
width: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
}
|
||||
.switch.switch-lg input:checked + label::after {
|
||||
margin-left: calc(3rem * .8);
|
||||
}
|
||||
.switch + .switch {
|
||||
margin-left: 1rem;
|
||||
animation: fadeInOut 1s infinite;
|
||||
}
|
||||
|
||||
.sortable_drag {
|
||||
background-color: #0000000f;
|
||||
}
|
||||
|
||||
.drag_icon {
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
margin-left: -10px;
|
||||
text-align: center;
|
||||
color: #b1b1b1;
|
||||
}
|
||||
|
||||
/* (Optional) Apply a "closed-hand" cursor during drag operation. */
|
||||
.drag_icon:active {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
|
||||
.switch_btn {
|
||||
float: right;
|
||||
margin: -1px 0px 0px 0px;
|
||||
|
@ -799,7 +415,7 @@ HTML,BODY {
|
|||
}
|
||||
|
||||
.jumbotron {
|
||||
background-color: white;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.toggle-service {
|
||||
|
@ -809,19 +425,6 @@ HTML,BODY {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
min-height: 85pt;
|
||||
}
|
||||
|
||||
.list-group-item:HOVER {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.index_container {
|
||||
min-height: 980pt;
|
||||
background-color: $container-color;
|
||||
}
|
||||
|
||||
/* Enter and leave animations can use different */
|
||||
/* durations and timing functions. */
|
||||
.slide-fade-enter-active {
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
@import 'variables';
|
||||
@import 'mixin';
|
||||
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.copy-btn BUTTON {
|
||||
background-color: white;
|
||||
margin: 6px;
|
||||
height: 26px;
|
||||
font-size: 8pt;
|
||||
padding: 5px 7px;
|
||||
border: 1px solid #a7a7a7;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background-color: $input-background;
|
||||
border: $input-border;
|
||||
color: $input-color;
|
||||
}
|
||||
|
||||
.form-control:FOCUS {
|
||||
background-color: lighten($input-background, 4%) !important;
|
||||
border: $input-border;
|
||||
color: $input-color;
|
||||
}
|
||||
|
||||
.form-control[readonly] {
|
||||
background-color: lighten($background-color, 12%) !important;
|
||||
color: darken($background-color, 5%) !important;
|
||||
}
|
||||
|
||||
/* The slider itself */
|
||||
.slider {
|
||||
-webkit-appearance: none; /* Override default CSS styles */
|
||||
appearance: none;
|
||||
width: 100%; /* Full-width */
|
||||
height: 5px; /* Specified height */
|
||||
background: #d3d3d3; /* Grey background */
|
||||
outline: none; /* Remove outline */
|
||||
-webkit-transition: .2s; /* 0.2 seconds transition on hover */
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
/* Mouse-over effects */
|
||||
.slider:hover {
|
||||
opacity: 1; /* Fully shown on mouse-over */
|
||||
}
|
||||
|
||||
/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none; /* Override default look */
|
||||
appearance: none;
|
||||
border-radius: 50%;
|
||||
width: 20px; /* Set a specific slider handle width */
|
||||
height: 20px; /* Slider handle height */
|
||||
background: #4CAF50; /* Green background */
|
||||
cursor: pointer; /* Cursor on hover */
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 15px; /* Set a specific slider handle width */
|
||||
height: 15px; /* Slider handle height */
|
||||
background: #4CAF50; /* Green background */
|
||||
cursor: pointer; /* Cursor on hover */
|
||||
}
|
||||
|
||||
|
||||
.inputTags-field {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
padding-top: .13rem;
|
||||
}
|
||||
|
||||
input.inputTags-field:focus {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.inputTags-list {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: calc(2.25rem + 2px);
|
||||
padding: .2rem .35rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
}
|
||||
|
||||
.inputTags-item {
|
||||
background-color: #3aba39;
|
||||
margin-right: 5px;
|
||||
padding: 5px 8px;
|
||||
font-size: 10pt;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.inputTags-item .close-item {
|
||||
margin-left: 6px;
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.switch {
|
||||
font-size: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
.switch input {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
background: none;
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
.switch input + label {
|
||||
position: relative;
|
||||
min-width: calc(calc(2.375rem * .8) * 2);
|
||||
border-radius: calc(2.375rem * .8);
|
||||
height: calc(2.375rem * .8);
|
||||
line-height: calc(2.375rem * .8);
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch input + label::before,
|
||||
.switch input + label::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(calc(2.375rem * .8) * 2);
|
||||
bottom: 0;
|
||||
display: block;
|
||||
}
|
||||
.switch input + label::before {
|
||||
right: 0;
|
||||
background-color: #dee2e6;
|
||||
border-radius: calc(2.375rem * .8);
|
||||
transition: 0.2s all;
|
||||
}
|
||||
.switch input + label::after {
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
transition: 0.2s all;
|
||||
}
|
||||
.switch-rd-gr input:checked + label::before {
|
||||
background-color: #29b10c !important;
|
||||
}
|
||||
.switch input:checked + label::before {
|
||||
background-color: #08d;
|
||||
}
|
||||
.switch input:checked + label::after {
|
||||
margin-left: calc(2.375rem * .8);
|
||||
}
|
||||
.switch input:focus + label::before {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25);
|
||||
}
|
||||
.switch input:disabled + label {
|
||||
color: #868e96;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.switch input:disabled + label::before {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
.switch.switch-sm {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.switch.switch-sm input + label {
|
||||
min-width: calc(calc(1.9375rem * .8) * 2);
|
||||
height: calc(1.9375rem * .8);
|
||||
line-height: calc(1.9375rem * .8);
|
||||
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch.switch-sm input + label::before {
|
||||
width: calc(calc(1.9375rem * .8) * 2);
|
||||
}
|
||||
.switch.switch-sm input + label::after {
|
||||
width: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
}
|
||||
.switch.switch-sm input:checked + label::after {
|
||||
margin-left: calc(1.9375rem * .8);
|
||||
}
|
||||
.switch.switch-lg {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.switch.switch-lg input + label {
|
||||
min-width: calc(calc(3rem * .8) * 2);
|
||||
height: calc(3rem * .8);
|
||||
line-height: calc(3rem * .8);
|
||||
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch.switch-lg input + label::before {
|
||||
width: calc(calc(3rem * .8) * 2);
|
||||
}
|
||||
.switch.switch-lg input + label::after {
|
||||
width: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
}
|
||||
.switch.switch-lg input:checked + label::after {
|
||||
margin-left: calc(3rem * .8);
|
||||
}
|
||||
.switch + .switch {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.CodeMirror {
|
||||
/* Bootstrap Settings */
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
overflow: auto;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
/* Code Mirror Settings */
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
height:80vh;
|
||||
}
|
||||
|
||||
.CodeMirror-focused {
|
||||
/* Bootstrap Settings */
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show>.nav-link {
|
||||
background-color: $nav-tab-color;
|
||||
}
|
||||
|
||||
.nav-pills A {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.nav-pills I {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
.drag_icon {
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
margin-left: -10px;
|
||||
text-align: center;
|
||||
color: #b1b1b1;
|
||||
}
|
||||
|
||||
/* (Optional) Apply a "closed-hand" cursor during drag operation. */
|
||||
.drag_icon:active {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
@import 'variables';
|
||||
@import 'mixin';
|
||||
|
||||
HTML,BODY {
|
||||
background-color: $background-color;
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
A {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
A:HOVER {
|
||||
color: lighten($text-color, 12%) !important;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: darken($text-color, 30%) !important;
|
||||
}
|
||||
|
||||
.day-success {
|
||||
background-color: $day-success-background;
|
||||
}
|
||||
|
||||
.day-success:HOVER {
|
||||
background-color: lighten($day-success-background, 2%) !important;
|
||||
}
|
||||
|
||||
.day-error {
|
||||
background-color: $day-error-background;
|
||||
}
|
||||
|
||||
.day-error:HOVER {
|
||||
background-color: lighten($day-error-background, 2%) !important;
|
||||
}
|
||||
|
||||
.contain-card {
|
||||
|
||||
.card-header {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
background-color: rgba(239, 239, 239, 0.65);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 30px;
|
||||
color: $navbar-color;
|
||||
background-color: $navbar-background;
|
||||
}
|
||||
|
||||
.table {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.nav-pills {
|
||||
border-radius: $global-border-radius;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: $navbar-color;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: $global-border-radius;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: $card-background;
|
||||
border: $card-border;
|
||||
box-shadow: $card-shadow;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-body H4 A {
|
||||
color: $service-title;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-title A {
|
||||
color: $service-title;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-body .badge {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
min-height: 85pt;
|
||||
background-color: $group-list-background;
|
||||
}
|
||||
|
||||
.list-group-item:HOVER {
|
||||
background-color: lighten($group-list-background, 2%) !important;
|
||||
}
|
||||
|
||||
.list-group-item A {
|
||||
color: $group-list-title;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 25px;
|
||||
max-width: $max-width;
|
||||
box-shadow: 0 .5rem 1rem rgba(0, 0, 0, 0.15) !important;
|
||||
background-color: $container-color;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-decoration: none;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer A {
|
||||
color: $footer-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer A:HOVER {
|
||||
color: #6d6d6d;
|
||||
}
|
|
@ -1,2 +1,4 @@
|
|||
@import 'base';
|
||||
@import 'layout';
|
||||
@import 'forms';
|
||||
@import 'mobile';
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
@mixin dynamic-color-hov($color) {
|
||||
&.dyn-dark {
|
||||
background-color: darken($color, 12%) !important;
|
||||
border-color: darken($color, 17%) !important;
|
||||
}
|
||||
&.dyn-dark:HOVER {
|
||||
background-color: darken($color, 17%) !important;
|
||||
border-color: darken($color, 20%) !important;
|
||||
}
|
||||
&.dyn-light {
|
||||
background-color: lighten($color, 12%) !important;
|
||||
border-color: lighten($color, 17%) !important;
|
||||
}
|
||||
&.dyn-light:HOVER {
|
||||
background-color: lighten($color, 17%) !important;
|
||||
border-color: lighten($color, 20%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dynamic-color($color) {
|
||||
&.dyn-dark {
|
||||
background-color: darken($color, 12%) !important;
|
||||
border-color: darken($color, 17%) !important;
|
||||
}
|
||||
&.dyn-light {
|
||||
background-color: lighten($color, 12%) !important;
|
||||
border-color: lighten($color, 17%) !important;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,28 @@
|
|||
/* Index Page */
|
||||
$background-color: #fcfcfc;
|
||||
$container-color: #fcfcfc;
|
||||
$background-color: #f5f5f5;
|
||||
$container-color: #ffffff;
|
||||
$text-color: #1d1d1d;
|
||||
$max-width: 860px;
|
||||
$title-color: #464646;
|
||||
$description-color: #939393;
|
||||
$title-color: #4e4e4e;
|
||||
$description-color: #828282;
|
||||
$subtitle-color: #747474;
|
||||
$mobile-card-shadow: 2px 3px 10px #b7b7b7;
|
||||
|
||||
$group-list-background: #fafafa;
|
||||
$group-list-title: #474747;
|
||||
$navbar-color: #1c1c1c;
|
||||
$navbar-background: #ffffff;
|
||||
$input-background: #fdfdfd;
|
||||
$input-color: #4e4e4e;
|
||||
$input-border: 1px solid #c9c9c9;
|
||||
$day-success-background: #18ce08;
|
||||
$day-error-background: #d50a0a;
|
||||
|
||||
/* Status Container */
|
||||
$service-background: #ffffff;
|
||||
$service-border: 1px solid rgba(0,0,0,.125);
|
||||
$service-title: #444444;
|
||||
$card-background: #fcfcfc;
|
||||
$card-border: 1px solid rgba(76, 76, 76, 0.12);
|
||||
$card-shadow: 0px 3px 6px 1px rgba(0, 0, 0, 0.08);
|
||||
$service-title: #3e3e3e;
|
||||
$service-title-size: 1.8rem;
|
||||
$service-stats-color: #4f4f4f;
|
||||
$service-description-color: #fff;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="card text-black-50 bg-white mb-5">
|
||||
<div class="card mb-5">
|
||||
<div class="card-header">Cache</div>
|
||||
<div class="card-body">
|
||||
<span v-if="!cache" class="text-muted">There are no cached pages yet!</span>
|
||||
|
|
|
@ -2,40 +2,81 @@
|
|||
<div class="col-12">
|
||||
<h2>{{service.name}} Checkins</h2>
|
||||
<p class="mb-3">Tell your service to send a routine HTTP request to a Statping Checkin.</p>
|
||||
<div v-for="(checkin, i) in checkins" class="col-12 alert alert-light" role="alert">
|
||||
<span class="badge badge-pill badge-info text-uppercase">{{checkin.name}}</span>
|
||||
<span class="float-right font-2">Last checkin {{ago(checkin.last_hit)}}</span>
|
||||
<span class="float-right font-2 mr-3">Check Every {{checkin.interval}} seconds</span>
|
||||
<span class="float-right font-2 mr-3">Grace Period {{checkin.grace}} seconds</span>
|
||||
<span class="d-block mt-2">
|
||||
<input type="text" class="form-control" :value="`${core.domain}/checkin/${checkin.api_key}`" readonly>
|
||||
<span class="small">Send a GET request to this URL every {{checkin.interval}} seconds
|
||||
<button @click="deleteCheckin(checkin)" type="button" class="btn btn-danger btn-xs float-right mt-1">Delete</button>
|
||||
|
||||
<div v-for="(checkin, i) in checkins" class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">
|
||||
{{checkin.name}}
|
||||
<button @click="deleteCheckin(checkin)" class="btn btn-sm btn-danger float-right text-uppercase">Delete</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" :value="`${core.domain}/checkin/${checkin.api_key}`" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/checkin/${checkin.api_key}`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="small">Send a GET request to this URL every {{checkin.interval}} minutes</span>
|
||||
<span class="small float-right mt-1">Requested {{ago(checkin.last_hit)}} ago</span>
|
||||
<span class="small float-right mt-1 mr-3">Request expected every {{checkin.interval}} minutes</span>
|
||||
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded = !expanded" :icon="expanded ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
{{checkin.name}} Records
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded}">
|
||||
<div class="alert alert-primary small" :class="{'alert-success': hit.success, 'alert-danger': !hit.success}" v-for="(hit, i) in records(checkin)">
|
||||
Checkin {{hit.success ? "Request" : "Failure"}} at {{hit.created_at}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="curl_expanded = !curl_expanded" :icon="curl_expanded ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Cronjob Task
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !curl_expanded}">
|
||||
This cronjob script will request the checkin endpoint every {{checkin.interval}} minutes. Add this cronjob task to the machine running this service.
|
||||
<div class="input-group mt-2">
|
||||
<input type="text" class="form-control" :value="`${checkin.interval} * * * * /usr/bin/curl ${core.domain}/checkin/${checkin.api_key} >/dev/null 2>&1`" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${checkin.interval} * * * * /usr/bin/curl ${core.domain}/checkin/${checkin.api_key} >/dev/null 2>&1`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="small d-block">Using CURL</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<span :class="{'text-success': last_record(checkin).success, 'text-danger': !last_record(checkin).success}">
|
||||
{{last_record(checkin).success ? "Checkin is currently working correctly" : "Checkin is currently failing"}}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 alert alert-light">
|
||||
<div class="card text-black-50 bg-white mt-4">
|
||||
<div class="card-header text-capitalize">Create Checkin</div>
|
||||
<div class="card-body">
|
||||
<form @submit.prevent="saveCheckin">
|
||||
<div class="form-group row">
|
||||
<div class="col-5">
|
||||
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
||||
<input v-model="checkin.name" type="text" name="name" class="form-control" id="checkin_name" placeholder="New Checkin">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="checkin_interval" class="col-form-label">Interval</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="60">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="grace_period" class="col-form-label">Grace Period</label>
|
||||
<input v-model="checkin.grace" type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
||||
<div class="col-3">
|
||||
<label for="checkin_interval" class="col-form-label">Interval (minutes)</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="1" min="1">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label class="col-form-label"></label>
|
||||
<button @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-primary d-block mt-2">Save Checkin</button>
|
||||
<button :disabled="btn_disabled" @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-primary d-block mt-2">Save Checkin</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -49,11 +90,14 @@ export default {
|
|||
return {
|
||||
service: {},
|
||||
ready: false,
|
||||
expanded: false,
|
||||
curl_expanded: false,
|
||||
checkin: {
|
||||
name: "",
|
||||
interval: 60,
|
||||
grace: 60,
|
||||
service_id: 0
|
||||
interval: 1,
|
||||
service_id: 0,
|
||||
hits: [],
|
||||
failures: []
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -64,6 +108,12 @@ export default {
|
|||
core() {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
btn_disabled() {
|
||||
if (this.checkin.name === "" || this.checkin.interval <= 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
if (this.$route.params) {
|
||||
|
@ -74,22 +124,37 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
records(checkin) {
|
||||
let hits = []
|
||||
let failures = []
|
||||
checkin.hits.forEach((hit) => {
|
||||
hits.push({success: true, created_at: this.parseISO(hit.created_at), id: hit.id})
|
||||
})
|
||||
checkin.failures.forEach((failure) => {
|
||||
failures.push({success: false, created_at: this.parseISO(failure.created_at), id: failure.id})
|
||||
})
|
||||
return hits.concat(failures).sort((a, b) => {return a.created_at-b.created_at}).reverse().slice(0,32)
|
||||
},
|
||||
last_record(checkin) {
|
||||
const r = this.records(checkin)
|
||||
return r[0]
|
||||
},
|
||||
fixInts() {
|
||||
const c = this.checkin
|
||||
this.checkin.interval = parseInt(c.interval)
|
||||
this.checkin.grace = parseInt(c.grace)
|
||||
return this.checkin
|
||||
},
|
||||
async saveCheckin() {
|
||||
const c = this.fixInts()
|
||||
await Api.checkin_create(c)
|
||||
await this.updateCheckins()
|
||||
this.checkin.name = ""
|
||||
await this.load()
|
||||
},
|
||||
async deleteCheckin(checkin) {
|
||||
await Api.checkin_delete(checkin)
|
||||
await this.updateCheckins()
|
||||
await this.load()
|
||||
},
|
||||
async updateCheckins() {
|
||||
async load() {
|
||||
const checkins = await Api.checkins()
|
||||
this.$store.commit('setCheckins', checkins)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('top_nav.announcements') }}</div>
|
||||
<div class="card-body pt-0">
|
||||
<table class="table table-striped">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('top_nav.services') }}
|
||||
<router-link v-if="$store.state.admin" to="/dashboard/create_service" class="btn btn-sm btn-outline-success float-right">
|
||||
<font-awesome-icon icon="plus"/> Create
|
||||
|
@ -11,7 +11,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('top_nav.groups') }}</div>
|
||||
<div class="card-body pt-0">
|
||||
<table class="table">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('top_nav.users') }}</div>
|
||||
<div class="card-body pt-0">
|
||||
<table class="table table-striped">
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
<div class="text-center">
|
||||
<span class="text-black-50">{{total}} Total</span>
|
||||
<span>{{total}} Failures</span>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
@ -50,7 +50,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
service: {},
|
||||
failures: [],
|
||||
fails: [],
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
total: 0,
|
||||
|
@ -58,6 +58,9 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
failures() {
|
||||
return this.fails.sort(function(a,b) {return b.id - a.id;});
|
||||
},
|
||||
pages() {
|
||||
return Math.floor(this.total / this.limit)
|
||||
},
|
||||
|
@ -91,7 +94,7 @@ export default {
|
|||
await this.load()
|
||||
},
|
||||
async load() {
|
||||
this.failures = await Api.service_failures(this.service.id, 0, 9999999999, this.limit, this.offset)
|
||||
this.fails = await Api.service_failures(this.service.id, 0, 9999999999, this.limit, this.offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
|
||||
<div v-for="incident in incidents" :key="incident.id" class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div v-for="incident in incidents" :key="incident.id" class="card contain-card mb-4">
|
||||
<div class="card-header">Incident: {{incident.title}}
|
||||
<button @click="deleteIncident(incident)" class="btn btn-sm btn-danger float-right">
|
||||
<font-awesome-icon icon="times" /> Delete
|
||||
|
@ -14,7 +14,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div class="card contain-card text-black-50 bg-white">
|
||||
<div class="card contain-card">
|
||||
<div class="card-header">Create Incident</div>
|
||||
<div class="card-body">
|
||||
<form @submit.prevent="createIncident">
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
</router-link>
|
||||
</div>
|
||||
<div class="col-12 col-md-3 mb-2 mb-md-0 mt-0 mt-md-1">
|
||||
<span class="text-black-50 float-md-right">
|
||||
<span class="float-md-right">
|
||||
{{$t('uptime', [service.online_7_days])}}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="card text-black-50 bg-white mb-5">
|
||||
<div class="card mb-5">
|
||||
<div class="card-header">Theme Editor</div>
|
||||
<div class="card-body">
|
||||
<div v-if="error" class="alert alert-danger mt-3" style="white-space: pre-line;">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<router-link to="/" class="navbar-brand">Statping</router-link>
|
||||
<button @click="navopen = !navopen" class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<font-awesome-icon v-if="!navopen" icon="bars"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="d-flex mt-3 mb-2">
|
||||
<div class="flex-fill service_day" v-for="(d, index) in failureData" :class="{'mini_error': d.amount > 0, 'mini_success': d.amount === 0}">
|
||||
<div class="flex-fill service_day" v-for="(d, index) in failureData" :class="{'day-error': d.amount > 0, 'day-success': d.amount === 0}">
|
||||
<span v-if="d.amount != 0" class="small">{{d.amount}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<span class="badge text-uppercase" :class="badgeClass(update.type)">{{update.type}}</span>
|
||||
</div>
|
||||
<div class="col-md-12 col-12 mt-2 font-3">{{update.message}}</div>
|
||||
<div class="col-12 font-1 float-right text-black-50 mt-2">{{ago(update.created_at)}} ago</div>
|
||||
<div class="col-12 font-1 float-right mt-2">{{ago(update.created_at)}} ago</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="service-chart-container">
|
||||
<apexchart width="100%" height="420" type="area" :options="main_chart_options" :series="main_chart"></apexchart>
|
||||
<div class="card text-black-50 bg-white mt-3 mb-3">
|
||||
<div class="card-header text-capitalize">Service Latency</div>
|
||||
<div class="card-body">
|
||||
<div class="service-chart-container">
|
||||
<apexchart width="100%" height="420" type="area" :options="main_chart_options" :series="main_chart"></apexchart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
<input v-model="checkin.name" type="text" name="name" class="form-control" id="checkin_name" placeholder="New Checkin">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="checkin_interval" class="col-form-label">Interval</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="60">
|
||||
<label for="checkin_interval" class="col-form-label">Interval (minutes)</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="1" min="1">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="grace_period" class="col-form-label">Grace Period</label>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="card contain-card text-black-50 bg-white mb-3">
|
||||
<div class="card contain-card mb-3">
|
||||
<div class="card-header">{{group.id ? `Update ${group.name}` : "Create Group"}}
|
||||
<transition name="slide-fade">
|
||||
<button @click="removeEdit" v-if="group.id" class="btn float-right btn-danger btn-sm">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="card-body bg-light pt-3">
|
||||
<div class="card-body pt-3">
|
||||
|
||||
<div v-if="updates.length===0" class="alert alert-link text-danger">
|
||||
No updates found, create a new Incident Update below.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="card contain-card text-black-50 bg-white mb-5">
|
||||
<div class="card contain-card mb-5">
|
||||
<div class="card-header">{{message.id ? `Update ${message.title}` : "Create Announcement"}}
|
||||
<transition name="slide-fade">
|
||||
<button @click="removeEdit" v-if="message.id" class="btn btn-sm float-right btn-danger btn-sm">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<form @submit.prevent="saveNotifier">
|
||||
<div class="card contain-card text-black-50 bg-white mb-3">
|
||||
<div class="card contain-card mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
{{notifier.title}}
|
||||
<span @click="enableToggle" class="switch switch-sm switch-rd-gr float-right">
|
||||
|
@ -69,7 +69,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="notifier.data_type" class="card text-black-50 bg-white mb-3">
|
||||
<div v-if="notifier.data_type" class="card mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded = !expanded" :icon="expanded ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
{{notifier.title}} Outgoing Request
|
||||
|
@ -103,7 +103,7 @@
|
|||
|
||||
</form>
|
||||
|
||||
<div v-if="error || success" class="card text-black-50 bg-white mb-3">
|
||||
<div v-if="error || success" class="card mb-3">
|
||||
<div class="card-body">
|
||||
|
||||
<div v-if="error && !success" class="alert alert-danger col-12" role="alert">
|
||||
|
@ -119,7 +119,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
|
@ -128,11 +128,11 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="testNotifier('success')" :disabled="loadingTest" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||
<button @click.prevent="testNotifier('success')" :disabled="loadingTest" class="btn btn-secondary btn-block text-capitalize test-notifier">
|
||||
<font-awesome-icon v-if="loadingTest" icon="circle-notch" class="mr-2" spin/>{{loadingTest ? "Loading..." : "Test Success"}}</button>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="testNotifier('failure')" :disabled="loadingTest" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||
<button @click.prevent="testNotifier('failure')" :disabled="loadingTest" class="btn btn-secondary btn-block text-capitalize test-notifier">
|
||||
<font-awesome-icon v-if="loadingTest" icon="circle-notch" class="mr-2" spin/>{{loadingTest ? "Loading..." : "Test Failure"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<form @submit.prevent="saveOAuth">
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
Internal Login
|
||||
<span @click="local_enabled = !!local_enabled" class="switch switch-sm switch-rd-gr float-right">
|
||||
|
@ -12,7 +12,7 @@
|
|||
Use Statping's default authentication to allow users you've created to login.
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded.github = !expanded.github" :icon="expanded.github ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Github Settings
|
||||
|
@ -69,7 +69,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<font-awesome-icon @click="expanded.google = !expanded.google" :icon="expanded.google ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Google Settings
|
||||
|
@ -113,7 +113,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<font-awesome-icon @click="expanded.slack = !expanded.slack" :icon="expanded.slack ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Slack Settings
|
||||
|
@ -165,7 +165,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<font-awesome-icon @click="expanded.custom = !expanded.custom" :icon="expanded.custom ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Custom oAuth Settings
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<form v-if="service.type" @submit.prevent="saveService">
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('service.info') }}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
|
@ -67,7 +67,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">Request Details</div>
|
||||
<div class="card-body">
|
||||
|
||||
|
@ -202,7 +202,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">Notification Options</div>
|
||||
<div class="card-body">
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="card contain-card text-black-50 bg-white mb-3">
|
||||
<div class="card contain-card mb-3">
|
||||
<div class="card-header"> {{user.id ? `Update ${user.username}` : "Create User"}}
|
||||
<transition name="slide-fade">
|
||||
<button @click.prevent="removeEdit" v-if="user.id" class="btn btn-sm float-right btn-danger btn-sm">Close</button>
|
||||
|
|
|
@ -85,7 +85,6 @@ export default Vue.mixin({
|
|||
copy(txt) {
|
||||
this.$copyText(txt).then(function (e) {
|
||||
alert('Copied: \n'+txt)
|
||||
console.log(e)
|
||||
});
|
||||
},
|
||||
serviceLink(service) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5">
|
||||
<TopNav :admin="admin"/>
|
||||
<router-view :admin="admin"/>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div class="col-12 bg-white p-4" v-html="<a class=\"scrollclick\" href=\"#\" data-id=\"page_0\">Types of Monitoring</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_1\">Features</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_2\">Start Statping</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_3\">Linux</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_4\">Mac</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_5\">Windows</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_6\">AWS EC2</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_7\">Docker</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_8\">Mobile App</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_9\">Heroku</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_10\">API</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_11\">Makefile</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_12\">Notifiers</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_13\">Notifier Events</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_14\">Notifier Example</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_15\">Prometheus Exporter</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_16\">SSL</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_17\">Config with .env File</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_18\">Static Export</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_19\">Statping Plugins</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_20\">Statuper</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_21\">Build and Test</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_22\">Contributing</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_23\">PGP Signature</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_24\">Testing</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_25\">Deployment</a><br>\n\n<div class=\"mt-5\" id=\"page_0\"><h1>Types of Monitoring</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_1\"><h1>Features</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_2\"><h1>Start Statping</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_3\"><h1>Linux</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_4\"><h1>Mac</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_5\"><h1>Windows</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_6\"><h1>AWS EC2</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_7\"><h1>Docker</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_8\"><h1>Mobile App</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_9\"><h1>Heroku</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_10\"><h1>API</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_11\"><h1>Makefile</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_12\"><h1>Notifiers</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_13\"><h1>Notifier Events</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_14\"><h1>Notifier Example</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_15\"><h1>Prometheus Exporter</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_16\"><h1>SSL</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_17\"><h1>Config with .env File</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_18\"><h1>Static Export</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_19\"><h1>Statping Plugins</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_20\"><h1>Statuper</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_21\"><h1>Build and Test</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_22\"><h1>Contributing</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_23\"><h1>PGP Signature</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_24\"><h1>Testing</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_25\"><h1>Deployment</h1></div>\n"></div>
|
||||
<div class="col-12 p-4" v-html="<a class=\"scrollclick\" href=\"#\" data-id=\"page_0\">Types of Monitoring</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_1\">Features</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_2\">Start Statping</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_3\">Linux</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_4\">Mac</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_5\">Windows</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_6\">AWS EC2</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_7\">Docker</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_8\">Mobile App</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_9\">Heroku</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_10\">API</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_11\">Makefile</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_12\">Notifiers</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_13\">Notifier Events</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_14\">Notifier Example</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_15\">Prometheus Exporter</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_16\">SSL</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_17\">Config with .env File</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_18\">Static Export</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_19\">Statping Plugins</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_20\">Statuper</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_21\">Build and Test</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_22\">Contributing</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_23\">PGP Signature</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_24\">Testing</a><br><a class=\"scrollclick\" href=\"#\" data-id=\"page_25\">Deployment</a><br>\n\n<div class=\"mt-5\" id=\"page_0\"><h1>Types of Monitoring</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_1\"><h1>Features</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_2\"><h1>Start Statping</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_3\"><h1>Linux</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_4\"><h1>Mac</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_5\"><h1>Windows</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_6\"><h1>AWS EC2</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_7\"><h1>Docker</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_8\"><h1>Mobile App</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_9\"><h1>Heroku</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_10\"><h1>API</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_11\"><h1>Makefile</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_12\"><h1>Notifiers</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_13\"><h1>Notifier Events</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_14\"><h1>Notifier Example</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_15\"><h1>Prometheus Exporter</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_16\"><h1>SSL</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_17\"><h1>Config with .env File</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_18\"><h1>Static Export</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_19\"><h1>Statping Plugins</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_20\"><h1>Statuper</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_21\"><h1>Build and Test</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_22\"><h1>Contributing</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_23\"><h1>PGP Signature</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_24\"><h1>Testing</h1></div>\n\n\n<div class=\"mt-5\" id=\"page_25\"><h1>Deployment</h1></div>\n"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Help',
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 sm-container index_container">
|
||||
<div class="container col-md-7 col-sm-12 sm-container">
|
||||
|
||||
<Header/>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5">
|
||||
<div class="col-10 offset-1 col-md-8 offset-md-2 mt-md-2">
|
||||
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
||||
<img alt="Statping Login" class="col-12 mt-5 mt-md-0" style="max-width:650px" src="banner.png">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="col-12 bg-white p-4">
|
||||
<div class="col-12 p-4">
|
||||
<p v-if="logs.length === 0" class="text-monospace sm">
|
||||
Loading Logs...
|
||||
</p>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5">
|
||||
|
||||
<div class="col-12 mb-4">
|
||||
|
||||
|
@ -18,41 +18,51 @@
|
|||
|
||||
<MessageBlock v-for="message in messagesInRange" v-bind:key="message.id" :message="message"/>
|
||||
|
||||
<div class="row mt-5 mb-4">
|
||||
<div class="col-12 col-md-5 font-2 mb-3 mb-md-0">
|
||||
<flatPickr :disabled="loading" @on-change="onnn" v-model="start_time" :config="{ enableTime: true, altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date() }" type="text" class="btn btn-white text-left" required />
|
||||
<small class="d-block">From {{this.format(new Date(start_time))}}</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-5 font-2 mb-3 mb-md-0">
|
||||
<flatPickr :disabled="loading" @on-change="onnn" v-model="end_time" :config="{ enableTime: true, altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date()}" type="text" class="btn btn-white text-left" required />
|
||||
<small class="d-block">To {{this.format(new Date(end_time))}}</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-2">
|
||||
<select :disabled="loading" @change="chartHits" v-model="group" class="form-control">
|
||||
<option value="1m">1 Minute</option>
|
||||
<option value="5m">5 Minutes</option>
|
||||
<option value="15m">15 Minute</option>
|
||||
<option value="30m">30 Minutes</option>
|
||||
<option value="1h">1 Hour</option>
|
||||
<option value="3h">3 Hours</option>
|
||||
<option value="6h">6 Hours</option>
|
||||
<option value="12h">12 Hours</option>
|
||||
<option value="24h">1 Day</option>
|
||||
<option value="168h">7 Days</option>
|
||||
<option value="360h">15 Days</option>
|
||||
</select>
|
||||
<small class="d-block d-md-none d-block">Increment Timeframe</small>
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">Timeframe</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4 font-2">
|
||||
<flatPickr :disabled="loading" @on-change="onnn" v-model="start_time" :config="{ enableTime: true, altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date() }" type="text" class="btn btn-white text-left" required />
|
||||
<small class="d-block">From {{this.format(new Date(start_time))}}</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 font-2">
|
||||
<flatPickr :disabled="loading" @on-change="onnn" v-model="end_time" :config="{ enableTime: true, altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date()}" type="text" class="btn btn-white text-left" required />
|
||||
<small class="d-block">To {{this.format(new Date(end_time))}}</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<select :disabled="loading" @change="chartHits" v-model="group" class="form-control">
|
||||
<option value="1m">1 Minute</option>
|
||||
<option value="5m">5 Minutes</option>
|
||||
<option value="15m">15 Minute</option>
|
||||
<option value="30m">30 Minutes</option>
|
||||
<option value="1h">1 Hour</option>
|
||||
<option value="3h">3 Hours</option>
|
||||
<option value="6h">6 Hours</option>
|
||||
<option value="12h">12 Hours</option>
|
||||
<option value="24h">1 Day</option>
|
||||
<option value="168h">7 Days</option>
|
||||
<option value="360h">15 Days</option>
|
||||
</select>
|
||||
<small class="d-block d-md-none d-block">Increment Timeframe</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AdvancedChart :group="group" :updated="updated_chart" :start="start_time.toString()" :end="end_time.toString()" :service="service"/>
|
||||
|
||||
<div v-if="!loading" class="col-12">
|
||||
<div v-if="!loading" class="row">
|
||||
<apexchart width="100%" height="120" type="rangeBar" :options="timeRangeOptions" :series="uptime_data"></apexchart>
|
||||
</div>
|
||||
|
||||
<div class="service-chart-heatmap mt-5 mb-4">
|
||||
<ServiceHeatmap :service="service"/>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header text-capitalize">Service Failures</div>
|
||||
<div class="card-body">
|
||||
<div class="service-chart-heatmap mt-5 mb-4">
|
||||
<ServiceHeatmap :service="service"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -378,8 +388,7 @@ export default {
|
|||
this.loading = false
|
||||
},
|
||||
async fetchUptime() {
|
||||
const uptime = await Api.service_uptime(this.id, this.params.start, this.params.end)
|
||||
window.console.log(uptime)
|
||||
const uptime = await Api.service_uptime(this.service.id, this.params.start, this.params.end)
|
||||
this.uptime_data = this.parse_uptime(uptime)
|
||||
},
|
||||
parse_uptime(timedata) {
|
||||
|
|
|
@ -60,14 +60,14 @@
|
|||
<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 text-black-50 bg-white">
|
||||
<div class="card">
|
||||
<div class="card-header">Statping Settings</div>
|
||||
<div class="card-body">
|
||||
<CoreSettings/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">API Settings</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
|
@ -80,7 +80,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<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 <a href="#" id="regenkeys" @click="renewApiKeys">Regenerate API Keys</a> if you need to.</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>
|
||||
|
|
|
@ -251,6 +251,12 @@ func TestMainApiRoutes(t *testing.T) {
|
|||
`go_threads`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Index Page",
|
||||
URL: "/",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
|
|
|
@ -2,6 +2,7 @@ package handlers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/utils"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
@ -106,6 +107,13 @@ func (s Storage) Delete(key string) {
|
|||
func (s Storage) Set(key string, content []byte, duration time.Duration) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
u, err := url.Parse(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if u.Query().Get("v") != "" {
|
||||
return
|
||||
}
|
||||
s.items[key] = Item{
|
||||
Content: content,
|
||||
Expiration: utils.Now().Add(duration).UnixNano(),
|
||||
|
|
|
@ -24,8 +24,7 @@ func findCheckin(r *http.Request) (*checkins.Checkin, string, error) {
|
|||
}
|
||||
|
||||
func apiAllCheckinsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
chks := checkins.All()
|
||||
returnJson(chks, w, r)
|
||||
returnJson(checkins.All(), w, r)
|
||||
}
|
||||
|
||||
func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -39,8 +38,7 @@ func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var checkin *checkins.Checkin
|
||||
err := DecodeJSON(r, &checkin)
|
||||
if err != nil {
|
||||
if err := DecodeJSON(r, &checkin); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
@ -63,22 +61,27 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
log.Infof("Checking %s was requested", checkin.Name)
|
||||
|
||||
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||
|
||||
if last := checkin.LastHit(); last == nil {
|
||||
checkin.Start()
|
||||
}
|
||||
|
||||
hit := &checkins.CheckinHit{
|
||||
Checkin: checkin.Id,
|
||||
From: ip,
|
||||
CreatedAt: utils.Now(),
|
||||
}
|
||||
log.Infof("Checking %s was requested", checkin.Name)
|
||||
|
||||
err = hit.Create()
|
||||
if err != nil {
|
||||
if err := hit.Create(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
checkin.Failing = false
|
||||
checkin.LastHitTime = utils.Now()
|
||||
|
||||
sendJsonAction(hit.Id, "update", w, r)
|
||||
}
|
||||
|
||||
|
|
|
@ -45,8 +45,7 @@ func TestApiCheckinRoutes(t *testing.T) {
|
|||
Body: `{
|
||||
"name": "Example Checkin",
|
||||
"service_id": 1,
|
||||
"checkin_interval": 300,
|
||||
"grace_period": 60,
|
||||
"interval": 300,
|
||||
"api_key": "example"
|
||||
}`,
|
||||
},
|
||||
|
|
|
@ -26,7 +26,16 @@ func findIncident(r *http.Request) (*incidents.Incident, int64, error) {
|
|||
|
||||
func apiServiceIncidentsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
incids := incidents.FindByService(utils.ToInt(vars["id"]))
|
||||
id := vars["id"]
|
||||
if utils.NotNumber(id) {
|
||||
sendErrorJson(errors.NotNumber, w, r)
|
||||
return
|
||||
}
|
||||
incids := incidents.FindByService(utils.ToInt(id))
|
||||
if incids == nil {
|
||||
sendErrorJson(errors.Missing(&incidents.Incident{}, id), w, r)
|
||||
return
|
||||
}
|
||||
returnJson(incids, w, r)
|
||||
}
|
||||
|
||||
|
@ -54,8 +63,7 @@ func apiCreateIncidentUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
update.IncidentId = incid.Id
|
||||
|
||||
err = update.Create()
|
||||
if err != nil {
|
||||
if err := update.Create(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
@ -74,8 +82,7 @@ func apiCreateIncidentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
incident.ServiceId = service.Id
|
||||
err = incident.Create()
|
||||
if err != nil {
|
||||
if err := incident.Create(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
@ -103,8 +110,7 @@ func apiDeleteIncidentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
err = incident.Delete()
|
||||
if err != nil {
|
||||
if err := incident.Delete(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
@ -118,8 +124,7 @@ func apiDeleteIncidentUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
err = update.Delete()
|
||||
if err != nil {
|
||||
if err := update.Delete(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package handlers
|
|||
import (
|
||||
"github.com/statping/statping/notifiers"
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
@ -12,6 +13,8 @@ func TestAttachment(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUnAuthenticatedNotifierRoutes(t *testing.T) {
|
||||
slackWebhookUrl := utils.Params.GetString("SLACK_URL")
|
||||
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "No Authentication - View All Notifiers",
|
||||
|
@ -20,6 +23,13 @@ func TestUnAuthenticatedNotifierRoutes(t *testing.T) {
|
|||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "View All Notifiers",
|
||||
URL: "/api/notifiers",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - View Notifier",
|
||||
URL: "/api/notifier/slack",
|
||||
|
@ -28,12 +38,76 @@ func TestUnAuthenticatedNotifierRoutes(t *testing.T) {
|
|||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Update Notifier",
|
||||
Name: "View Notifier",
|
||||
URL: "/api/notifier/slack",
|
||||
Method: "POST",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Update Notifier",
|
||||
URL: "/api/notifier/slack",
|
||||
Method: "POST",
|
||||
Body: `{
|
||||
"method": "slack",
|
||||
"host": "` + slackWebhookUrl + `",
|
||||
"enabled": true,
|
||||
"limits": 55
|
||||
}`,
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "Update Notifier",
|
||||
URL: "/api/notifier/slack",
|
||||
Method: "POST",
|
||||
Body: `{
|
||||
"method": "slack",
|
||||
"host": "` + slackWebhookUrl + `",
|
||||
"enabled": true,
|
||||
"limits": 55
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "Test Notifier (OnSuccess)",
|
||||
URL: "/api/notifier/slack/test",
|
||||
Method: "POST",
|
||||
Body: `{
|
||||
"method": "success",
|
||||
"notifier": {
|
||||
"enabled": false,
|
||||
"limits": 60,
|
||||
"method": "slack",
|
||||
"host": "` + slackWebhookUrl + `",
|
||||
"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}",
|
||||
"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}"
|
||||
}
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"success":true`},
|
||||
BeforeTest: SetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "Test Notifier (OnFailure)",
|
||||
URL: "/api/notifier/slack/test",
|
||||
Method: "POST",
|
||||
Body: `{
|
||||
"method": "failure",
|
||||
"notifier": {
|
||||
"enabled": false,
|
||||
"limits": 60,
|
||||
"method": "slack",
|
||||
"host": "` + slackWebhookUrl + `",
|
||||
"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}",
|
||||
"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}"
|
||||
}
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"success":true`},
|
||||
BeforeTest: SetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOAuthRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "OAuth Save",
|
||||
URL: "/api/oauth",
|
||||
Body: `{
|
||||
"gh_client_id": "githubid",
|
||||
"gh_client_secret": "githubsecret",
|
||||
"google_client_id": "googleid",
|
||||
"google_client_secret": "googlesecret",
|
||||
"oauth_domains": "gmail.com,yahoo.com,socialeck.com",
|
||||
"oauth_providers": "local,slack,google,github",
|
||||
"slack_client_id": "example.iddd",
|
||||
"slack_client_secret": "exampleeesecret",
|
||||
"slack_team": "dev"
|
||||
}`,
|
||||
Method: "POST",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "OAuth Values",
|
||||
URL: "/api/oauth",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"slack_client_id":"example.iddd"`},
|
||||
AfterTest: UnsetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
res, t, err := RunHTTPTest(v, t)
|
||||
assert.Nil(t, err)
|
||||
t.Log(res)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -180,7 +180,6 @@ func Router() *mux.Router {
|
|||
// API Generic Routes
|
||||
r.Handle("/metrics", readOnly(promhttp.Handler(), false))
|
||||
r.Handle("/health", http.HandlerFunc(healthCheckHandler))
|
||||
r.Handle("/.well-known/", http.StripPrefix("/.well-known/", http.FileServer(http.Dir(dir+"/.well-known"))))
|
||||
r.NotFoundHandler = http.HandlerFunc(error404Handler)
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ func die(err error) {
|
|||
}
|
||||
|
||||
var packageTemplate = template.Must(template.New("").Parse(`<template>
|
||||
<div class="col-12 bg-white p-4" v-html={{printf "%q" .Compiled}}></div>
|
||||
<div class="col-12 p-4" v-html={{printf "%q" .Compiled}}></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
var (
|
||||
log = utils.Log.WithField("type", "source")
|
||||
TmplBox *rice.Box // HTML and other small files from the 'source/tmpl' directory, this will be loaded into '/assets'
|
||||
DefaultScss = []string{"scss/main.scss", "scss/base.scss", "scss/mobile.scss"}
|
||||
DefaultScss = []string{"scss/base.scss", "scss/layout.scss", "scss/main.scss", "scss/mixin.scss", "scss/mobile.scss", "scss/variables.scss"}
|
||||
)
|
||||
|
||||
// Assets will load the Rice boxes containing the CSS, SCSS, JS, and HTML files.
|
||||
|
|
|
@ -13,8 +13,7 @@ import (
|
|||
var testCheckin = &Checkin{
|
||||
ServiceId: 1,
|
||||
Name: "Test Checkin",
|
||||
Interval: 60,
|
||||
GracePeriod: 10,
|
||||
Interval: 3,
|
||||
ApiKey: "tHiSiSaTeStXXX",
|
||||
CreatedAt: utils.Now(),
|
||||
UpdatedAt: utils.Now(),
|
||||
|
@ -39,10 +38,12 @@ func TestInit(t *testing.T) {
|
|||
db, err := database.OpenTester()
|
||||
require.Nil(t, err)
|
||||
SetDB(db)
|
||||
failures.SetDB(db)
|
||||
db.AutoMigrate(&Checkin{}, &CheckinHit{}, &failures.Failure{})
|
||||
db.Create(&testCheckin)
|
||||
for _, v := range testCheckinHits {
|
||||
db.Create(&v)
|
||||
err := db.Create(&v).Error()
|
||||
require.Nil(t, err)
|
||||
}
|
||||
assert.True(t, db.HasTable(&Checkin{}))
|
||||
assert.True(t, db.HasTable(&CheckinHit{}))
|
||||
|
|
|
@ -15,6 +15,11 @@ func SetDB(database database.Database) {
|
|||
}
|
||||
|
||||
func (c *Checkin) AfterFind() {
|
||||
c.AllHits = c.Hits()
|
||||
c.AllFailures = c.Failures().LastAmount(32)
|
||||
if last := c.LastHit(); last != nil {
|
||||
c.LastHitTime = last.CreatedAt
|
||||
}
|
||||
metrics.Query("checkin", "find")
|
||||
}
|
||||
|
||||
|
@ -41,9 +46,6 @@ func (c *Checkin) Create() error {
|
|||
c.ApiKey = utils.RandomString(32)
|
||||
}
|
||||
q := db.Create(c)
|
||||
|
||||
c.Start()
|
||||
go c.checkinRoutine()
|
||||
return q.Error()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@ package checkins
|
|||
|
||||
func (c *Checkin) LastHit() *CheckinHit {
|
||||
var hit CheckinHit
|
||||
dbHits.Where("checkin = ?", c.Id).Limit(1).Find(&hit)
|
||||
dbHits.Where("checkin = ?", c.Id).Last(&hit)
|
||||
return &hit
|
||||
}
|
||||
|
||||
func (c *Checkin) Hits() []*CheckinHit {
|
||||
var hits []*CheckinHit
|
||||
dbHits.Where("checkin = ?", c.Id).Find(&hits)
|
||||
dbHits.Where("checkin = ?", c.Id).Order("DESC").Find(&hits)
|
||||
c.AllHits = hits
|
||||
return hits
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
func (c *Checkin) CreateFailure(f *failures.Failure) error {
|
||||
f.Checkin = c.Id
|
||||
c.Failing = true
|
||||
return failures.DB().Create(f).Error()
|
||||
}
|
||||
|
||||
|
|
|
@ -10,22 +10,11 @@ func (c *Checkin) Expected() time.Duration {
|
|||
last := c.LastHit()
|
||||
now := utils.Now()
|
||||
lastDir := now.Sub(last.CreatedAt)
|
||||
sub := time.Duration(c.Period() - lastDir)
|
||||
return sub
|
||||
return c.Period() - lastDir
|
||||
}
|
||||
|
||||
func (c *Checkin) Period() time.Duration {
|
||||
duration, _ := time.ParseDuration(fmt.Sprintf("%ds", c.Interval))
|
||||
if duration.Seconds() <= 15 {
|
||||
return 15 * time.Second
|
||||
}
|
||||
return duration
|
||||
}
|
||||
|
||||
// Grace will return the duration of the Checkin Grace Period (after service hasn't responded, wait a bit for a response)
|
||||
func (c *Checkin) Grace() time.Duration {
|
||||
duration, _ := time.ParseDuration(fmt.Sprintf("%vs", c.GracePeriod))
|
||||
return duration
|
||||
return time.Duration(c.Interval) * time.Minute
|
||||
}
|
||||
|
||||
// Start will create a channel for the checkin checking go routine
|
||||
|
@ -54,12 +43,3 @@ func (c *Checkin) IsRunning() bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// String will return a Checkin API string
|
||||
func (c *Checkin) String() string {
|
||||
return c.ApiKey
|
||||
}
|
||||
|
||||
func (c *Checkin) Link() string {
|
||||
return fmt.Sprintf("%v/checkin/%v", "DOMAINHERE", c.ApiKey)
|
||||
}
|
||||
|
|
|
@ -9,27 +9,10 @@ import (
|
|||
|
||||
var log = utils.Log.WithField("type", "checkin")
|
||||
|
||||
// RecheckCheckinFailure will check if a Service Checkin has been reported yet
|
||||
func (c *Checkin) RecheckCheckinFailure(guard chan struct{}) {
|
||||
between := utils.Now().Sub(utils.Now()).Seconds()
|
||||
if between > float64(c.Interval) {
|
||||
fmt.Println("rechecking every 15 seconds!")
|
||||
time.Sleep(15 * time.Second)
|
||||
guard <- struct{}{}
|
||||
c.RecheckCheckinFailure(guard)
|
||||
} else {
|
||||
fmt.Println("i recovered!!")
|
||||
}
|
||||
<-guard
|
||||
}
|
||||
|
||||
// checkinRoutine for checking if the last Checkin was within its interval
|
||||
func (c *Checkin) checkinRoutine() {
|
||||
lastHit := c.LastHit()
|
||||
if lastHit == nil {
|
||||
return
|
||||
}
|
||||
reCheck := c.Period()
|
||||
|
||||
CheckinLoop:
|
||||
for {
|
||||
select {
|
||||
|
@ -38,20 +21,25 @@ CheckinLoop:
|
|||
c.Failing = false
|
||||
break CheckinLoop
|
||||
case <-time.After(reCheck):
|
||||
log.Infoln(fmt.Sprintf("Checkin '%s' expects a request every %v", c.Name, utils.FormatDuration(c.Period())))
|
||||
if c.Expected() <= 0 {
|
||||
issue := fmt.Sprintf("Checkin '%s' is failing, no request since %v", c.Name, lastHit.CreatedAt)
|
||||
//log.Errorln(issue)
|
||||
lastHit := c.LastHit()
|
||||
ago := utils.Now().Sub(lastHit.CreatedAt)
|
||||
|
||||
log.Infoln(fmt.Sprintf("Checkin '%s' expects a request every %s last request was %s ago", c.Name, c.Period(), utils.DurationReadable(ago)))
|
||||
|
||||
if ago.Seconds() > c.Period().Seconds() {
|
||||
issue := fmt.Sprintf("Checkin expects a request every %d seconds", c.Interval)
|
||||
log.Warnln(issue)
|
||||
|
||||
fail := &failures.Failure{
|
||||
Issue: issue,
|
||||
Method: "checkin",
|
||||
Service: c.ServiceId,
|
||||
Checkin: c.Id,
|
||||
PingTime: c.Expected().Milliseconds(),
|
||||
PingTime: ago.Milliseconds(),
|
||||
}
|
||||
|
||||
c.CreateFailure(fail)
|
||||
if err := c.CreateFailure(fail); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
reCheck = c.Period()
|
||||
}
|
||||
|
|
|
@ -8,22 +8,20 @@ import (
|
|||
func Samples() error {
|
||||
log.Infoln("Inserting Sample Checkins...")
|
||||
checkin1 := &Checkin{
|
||||
Name: "Demo Checkin 1",
|
||||
ServiceId: 1,
|
||||
Interval: 300,
|
||||
GracePeriod: 300,
|
||||
ApiKey: "demoCheckin123",
|
||||
Name: "Demo Checkin 1",
|
||||
ServiceId: 1,
|
||||
Interval: 3,
|
||||
ApiKey: "demoCheckin123",
|
||||
}
|
||||
if err := checkin1.Create(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
checkin2 := &Checkin{
|
||||
Name: "Example Checkin 2",
|
||||
ServiceId: 2,
|
||||
Interval: 900,
|
||||
GracePeriod: 300,
|
||||
ApiKey: utils.RandomString(7),
|
||||
Name: "Example Checkin 2",
|
||||
ServiceId: 2,
|
||||
Interval: 1,
|
||||
ApiKey: utils.RandomString(7),
|
||||
}
|
||||
if err := checkin2.Create(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -11,7 +11,6 @@ type Checkin struct {
|
|||
ServiceId int64 `gorm:"index;column:service" json:"service_id"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
Interval int64 `gorm:"column:check_interval" json:"interval"`
|
||||
GracePeriod int64 `gorm:"column:grace_period" json:"grace"`
|
||||
ApiKey string `gorm:"column:api_key" json:"api_key"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
|
|
|
@ -38,7 +38,7 @@ func (f Failurer) List() []*Failure {
|
|||
|
||||
func (f Failurer) LastAmount(amount int) []*Failure {
|
||||
var fail []*Failure
|
||||
f.db.Order("id asc").Limit(amount).Find(&fail)
|
||||
f.db.Order("id DESC").Limit(amount).Find(&fail)
|
||||
return fail
|
||||
}
|
||||
|
||||
|
|
|
@ -79,16 +79,13 @@ func All() []*Incident {
|
|||
}
|
||||
|
||||
func (i *Incident) Create() error {
|
||||
q := db.Create(i)
|
||||
return q.Error()
|
||||
return db.Create(i).Error()
|
||||
}
|
||||
|
||||
func (i *Incident) Update() error {
|
||||
q := db.Update(i)
|
||||
return q.Error()
|
||||
return db.Update(i).Error()
|
||||
}
|
||||
|
||||
func (i *Incident) Delete() error {
|
||||
q := db.Delete(i)
|
||||
return q.Error()
|
||||
return db.Delete(i).Error()
|
||||
}
|
||||
|
|
|
@ -32,8 +32,10 @@ func TestInit(t *testing.T) {
|
|||
db, err := database.OpenTester()
|
||||
require.Nil(t, err)
|
||||
db.AutoMigrate(&Incident{}, &IncidentUpdate{})
|
||||
db.Create(&example)
|
||||
SetDB(db)
|
||||
db.Create(&example)
|
||||
db.Create(&update1)
|
||||
db.Create(&update2)
|
||||
}
|
||||
|
||||
func TestFind(t *testing.T) {
|
||||
|
|
|
@ -7,7 +7,9 @@ import (
|
|||
// CheckinProcess runs the checkin routine for each checkin attached to service
|
||||
func CheckinProcess(s *Service) {
|
||||
for _, c := range s.Checkins() {
|
||||
c.Start()
|
||||
if last := c.LastHit(); last != nil {
|
||||
c.Start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -253,10 +253,6 @@ func SelectAllServices(start bool) (map[int64]*Service, error) {
|
|||
return allServices, nil
|
||||
}
|
||||
for _, s := range all() {
|
||||
|
||||
if start {
|
||||
CheckinProcess(s)
|
||||
}
|
||||
s.Failures = s.AllFailures().LastAmount(limitedFailures)
|
||||
for _, c := range s.Checkins() {
|
||||
s.AllCheckins = append(s.AllCheckins, c)
|
||||
|
@ -264,6 +260,9 @@ func SelectAllServices(start bool) (map[int64]*Service, error) {
|
|||
// collect initial service stats
|
||||
s.UpdateStats()
|
||||
allServices[s.Id] = s
|
||||
if start {
|
||||
CheckinProcess(s)
|
||||
}
|
||||
}
|
||||
return allServices, nil
|
||||
}
|
||||
|
|
|
@ -61,11 +61,10 @@ var hit3 = &hits.Hit{
|
|||
}
|
||||
|
||||
var exmapleCheckin = &checkins.Checkin{
|
||||
ServiceId: 1,
|
||||
Name: "Example Checkin",
|
||||
Interval: 60,
|
||||
GracePeriod: 30,
|
||||
ApiKey: "wdededede",
|
||||
ServiceId: 1,
|
||||
Name: "Example Checkin",
|
||||
Interval: 3,
|
||||
ApiKey: "wdededede",
|
||||
}
|
||||
|
||||
var fail1 = &failures.Failure{
|
||||
|
@ -504,6 +503,62 @@ func TestServices(t *testing.T) {
|
|||
assert.Len(t, all, 1)
|
||||
})
|
||||
|
||||
t.Run("Test Load services.yml", func(t *testing.T) {
|
||||
|
||||
file := `x-tcpservice: &tcpservice
|
||||
type: tcp
|
||||
check_interval: 60
|
||||
timeout: 15
|
||||
allow_notifications: true
|
||||
notify_after: 0
|
||||
notify_all_changes: true
|
||||
public: true
|
||||
redirect: true
|
||||
|
||||
x-httpservice: &httpservice
|
||||
type: http
|
||||
method: GET
|
||||
check_interval: 45
|
||||
timeout: 10
|
||||
expected_status: 200
|
||||
allow_notifications: true
|
||||
notify_after: 2
|
||||
notify_all_changes: true
|
||||
public: true
|
||||
redirect: true
|
||||
|
||||
services:
|
||||
|
||||
- name: Statping Demo
|
||||
domain: https://demo.statping.com
|
||||
<<: *httpservice
|
||||
|
||||
- name: Portainer
|
||||
domain: portainer
|
||||
port: 9000
|
||||
<<: *tcpservice
|
||||
|
||||
- name: Statping Github
|
||||
domain: https://github.com/statping/statping
|
||||
<<: *httpservice`
|
||||
|
||||
err := utils.SaveFile(utils.Directory+"/services.yml", []byte(file))
|
||||
require.Nil(t, err)
|
||||
|
||||
assert.FileExists(t, utils.Directory+"/services.yml")
|
||||
|
||||
srvs, err := LoadServicesYaml()
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 3, len(srvs.Services))
|
||||
|
||||
assert.Equal(t, "Statping Demo", srvs.Services[0].Name)
|
||||
assert.Equal(t, 45, srvs.Services[0].Interval)
|
||||
assert.Equal(t, "https://demo.statping.com", srvs.Services[0].Domain)
|
||||
|
||||
err = utils.DeleteFile(utils.Directory + "/services.yml")
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Test Close", func(t *testing.T) {
|
||||
assert.Nil(t, db.Close())
|
||||
})
|
||||
|
|
|
@ -22,7 +22,3 @@ func (d Duration) Human() string {
|
|||
func FormatDuration(d time.Duration) string {
|
||||
return durafmt.ParseShort(d).LimitFirstN(3).String()
|
||||
}
|
||||
|
||||
func rev(f float64) float64 {
|
||||
return f * -1
|
||||
}
|
||||
|
|
|
@ -10,39 +10,25 @@ import (
|
|||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ararog/timeago"
|
||||
)
|
||||
|
||||
var (
|
||||
// Directory returns the current path or the STATPING_DIR environment variable
|
||||
Directory string
|
||||
disableLogs bool
|
||||
Directory string
|
||||
)
|
||||
|
||||
type env struct {
|
||||
data interface{}
|
||||
}
|
||||
|
||||
func NotNumber(val string) bool {
|
||||
_, err := strconv.ParseInt(val, 10, 64)
|
||||
return err != nil
|
||||
}
|
||||
|
||||
func (e *env) Duration() time.Duration {
|
||||
t, err := time.ParseDuration(e.data.(string))
|
||||
if err != nil {
|
||||
Log.Errorln(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// ToInt converts a int to a string
|
||||
func ToInt(s interface{}) int64 {
|
||||
switch v := s.(type) {
|
||||
|
@ -91,17 +77,6 @@ func ToString(s interface{}) string {
|
|||
}
|
||||
}
|
||||
|
||||
type Timestamp time.Time
|
||||
type Timestamper interface {
|
||||
Ago() string
|
||||
}
|
||||
|
||||
// Ago returns a human readable timestamp based on the Timestamp (time.Time) interface
|
||||
func (t Timestamp) Ago() string {
|
||||
got, _ := timeago.TimeAgoWithTime(time.Now(), time.Time(t))
|
||||
return got
|
||||
}
|
||||
|
||||
// Command will run a terminal command with 'sh -c COMMAND' and return stdout and errOut as strings
|
||||
// in, out, err := Command("sass assets/scss assets/css/base.css")
|
||||
func Command(name string, args ...string) (string, string, error) {
|
||||
|
@ -187,14 +162,14 @@ func DurationReadable(d time.Duration) string {
|
|||
// // body - The body or form data to send with HTTP request
|
||||
// // timeout - Specific duration to timeout on. time.Duration(30 * time.Seconds)
|
||||
// // You can use a HTTP Proxy if you HTTP_PROXY environment variable
|
||||
func HttpRequest(url, method string, content interface{}, headers []string, body io.Reader, timeout time.Duration, verifySSL bool, customTLS *tls.Config) ([]byte, *http.Response, error) {
|
||||
func HttpRequest(endpoint, method string, content interface{}, headers []string, body io.Reader, timeout time.Duration, verifySSL bool, customTLS *tls.Config) ([]byte, *http.Response, error) {
|
||||
var err error
|
||||
var req *http.Request
|
||||
if method == "" {
|
||||
method = "GET"
|
||||
}
|
||||
t1 := Now()
|
||||
if req, err = http.NewRequest(method, url, body); err != nil {
|
||||
if req, err = http.NewRequest(method, endpoint, body); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
req.Header.Set("User-Agent", "Statping")
|
||||
|
@ -243,6 +218,13 @@ func HttpRequest(url, method string, content interface{}, headers []string, body
|
|||
return dialer.DialContext(ctx, network, addr)
|
||||
},
|
||||
}
|
||||
if Params.IsSet("HTTP_PROXY") {
|
||||
proxyUrl, err := url.Parse(Params.GetString("HTTP_PROXY"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
transport.Proxy = http.ProxyURL(proxyUrl)
|
||||
}
|
||||
if customTLS != nil {
|
||||
transport.TLSClientConfig.RootCAs = customTLS.RootCAs
|
||||
transport.TLSClientConfig.Certificates = customTLS.Certificates
|
||||
|
@ -269,8 +251,8 @@ func HttpRequest(url, method string, content interface{}, headers []string, body
|
|||
}
|
||||
|
||||
// record HTTP metrics
|
||||
metrics.Histo("bytes", float64(len(contents)), url, method)
|
||||
metrics.Histo("duration", Now().Sub(t1).Seconds(), url, method)
|
||||
metrics.Histo("bytes", float64(len(contents)), endpoint, method)
|
||||
metrics.Histo("duration", Now().Sub(t1).Seconds(), endpoint, method)
|
||||
|
||||
return contents, resp, err
|
||||
}
|
||||
|
|
|
@ -123,13 +123,28 @@ func ExampleStringInt() {
|
|||
// Output: 42
|
||||
}
|
||||
|
||||
func TestTimestamp_Ago(t *testing.T) {
|
||||
now := Timestamp(time.Now())
|
||||
assert.Equal(t, "Just now", now.Ago())
|
||||
func TestHashPassword(t *testing.T) {
|
||||
pass := HashPassword("password123")
|
||||
assert.Equal(t, 60, len(pass))
|
||||
assert.True(t, CheckHash("password123", pass))
|
||||
assert.False(t, CheckHash("wrongpasswd", pass))
|
||||
}
|
||||
|
||||
func TestHashPassword(t *testing.T) {
|
||||
assert.Equal(t, 60, len(HashPassword("password123")))
|
||||
func TestHuman(t *testing.T) {
|
||||
assert.Equal(t, "10 seconds", Duration{10 * time.Second}.Human())
|
||||
assert.Equal(t, "1 day 12 hours", Duration{36 * time.Hour}.Human())
|
||||
assert.Equal(t, "45 minutes", Duration{45 * time.Minute}.Human())
|
||||
}
|
||||
|
||||
func TestSha256Hash(t *testing.T) {
|
||||
assert.Equal(t, "ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f", Sha256Hash("password123"))
|
||||
}
|
||||
|
||||
func TestNotNumbber(t *testing.T) {
|
||||
assert.True(t, NotNumber("notint"))
|
||||
assert.True(t, NotNumber("1293notanint922"))
|
||||
assert.False(t, NotNumber("0"))
|
||||
assert.False(t, NotNumber("5"))
|
||||
}
|
||||
|
||||
func TestNewSHA1Hash(t *testing.T) {
|
||||
|
@ -190,3 +205,12 @@ func TestConfigLoad(t *testing.T) {
|
|||
assert.True(t, b("SAMPLE_DATA"))
|
||||
assert.True(t, b("ALLOW_REPORTS"))
|
||||
}
|
||||
|
||||
func TestPerlin(t *testing.T) {
|
||||
p := NewPerlin(2, 2, 5, Now().UnixNano())
|
||||
require.NotNil(t, p)
|
||||
|
||||
for hi := 1.; hi <= 100.; hi++ {
|
||||
assert.NotZero(t, p.Noise1D(hi/500))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.90.60
|
||||
0.90.61
|
||||
|
|
Loading…
Reference in New Issue