pull/429/head
hunterlong 2020-01-24 19:28:40 -08:00
parent 4a877bd501
commit fefc8a94fe
21 changed files with 102 additions and 898 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

View File

@ -1,651 +0,0 @@
/* Index Page */
/* Status Container */
/* Button Colors */
/* Footer Settings */
/* Global Settings */
/* Mobile Settings */
/* Mobile Service Container */
HTML, BODY {
background-color: #fcfcfc;
padding-bottom: 10px;
}
.container {
padding-top: 20px;
padding-bottom: 25px;
max-width: 860px;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}
.header-title {
color: #464646;
}
.header-desc {
color: #939393;
}
.btn {
border-radius: 0.2rem;
}
.online_list .badge {
margin-top: 0.2rem;
}
.navbar {
margin-bottom: 30px;
}
.btn-sm {
line-height: 1.3;
font-size: 0.75rem;
}
.view_service_btn {
position: absolute;
bottom: -40px;
right: 40px;
}
.service_lower_info {
position: absolute;
bottom: -40px;
left: 40px;
color: #d1ffca;
font-size: 0.85rem;
}
.lg_number {
font-size: 2.3rem;
font-weight: bold;
display: block;
color: #4f4f4f;
}
.stats_area {
text-align: center;
color: #a5a5a5;
}
.lower_canvas {
height: 3.4rem;
width: 100%;
background-color: #48d338;
padding: 15px 10px;
margin-left: 0px !important;
margin-right: 0px !important;
}
.lower_canvas SPAN {
font-size: 1rem;
color: #fff;
}
.footer {
text-decoration: none;
margin-top: 20px;
}
.footer A {
color: #8d8d8d;
text-decoration: none;
}
.footer A:HOVER {
color: #6d6d6d;
}
.badge {
color: white;
border-radius: 0.2rem;
}
.btn-group {
height: 25px;
}
.btn-group A {
padding: 0.1rem 0.75rem;
font-size: 0.8rem;
}
.card-body .badge {
color: #fff;
}
.nav-pills .nav-link {
border-radius: 0.2rem;
}
.form-control {
border-radius: 0.2rem;
}
.card {
background-color: #ffffff;
border: 1px solid rgba(0, 0, 0, 0.125);
}
.card-body {
overflow: hidden;
}
.card-body H4 A {
color: #444444;
text-decoration: none;
}
.chart-container {
position: relative;
height: 190px;
width: 100%;
overflow: hidden;
}
.service-chart-container {
position: relative;
height: 400px;
width: 100%;
}
.service-chart-heatmap {
position: relative;
height: 300px;
width: 100%;
}
.inputTags-field {
border: 0;
background-color: transparent;
padding-top: 0.13rem;
}
input.inputTags-field:focus {
outline-width: 0;
}
.inputTags-list {
display: block;
width: 100%;
min-height: calc(2.25rem + 2px);
padding: 0.2rem 0.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: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.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;
}
.btn-primary {
background-color: #3e9bff;
border-color: #006fe6;
color: white;
}
.btn-primary.dyn-dark {
background-color: #32a825 !important;
border-color: #2c9320 !important;
}
.btn-primary.dyn-light {
background-color: #75de69 !important;
border-color: #88e37e !important;
}
.btn-success {
background-color: #47d337;
}
.btn-success.dyn-dark {
background-color: #32a825 !important;
border-color: #2c9320 !important;
}
.btn-success.dyn-light {
background-color: #75de69 !important;
border-color: #88e37e !important;
}
.btn-danger {
background-color: #dd3545;
}
.btn-danger.dyn-dark {
background-color: #b61f2d !important;
border-color: #a01b28 !important;
}
.btn-danger.dyn-light {
background-color: #e66975 !important;
border-color: #e97f89 !important;
}
.bg-success {
background-color: #47d337 !important;
}
.bg-danger {
background-color: #dd3545 !important;
}
.bg-success .dyn-dark {
background-color: #35b027 !important;
}
.bg-danger .dyn-dark {
background-color: #bf202f !important;
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
background-color: #13a00d;
}
.nav-pills A {
color: #424242;
}
.nav-pills I {
margin-right: 10px;
}
.CodeMirror {
/* Bootstrap Settings */
box-sizing: border-box;
margin: 0;
font: inherit;
overflow: auto;
font-family: inherit;
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, 0.075);
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
/* Code Mirror Settings */
font-family: monospace;
position: relative;
overflow: hidden;
height: 80vh;
}
.CodeMirror-focused {
/* Bootstrap Settings */
border-color: #66afe9;
outline: 0;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.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 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;
}
@keyframes pulse_animation {
0% {
transform: scale(1);
}
30% {
transform: scale(1);
}
40% {
transform: scale(1.02);
}
50% {
transform: scale(1);
}
60% {
transform: scale(1);
}
70% {
transform: scale(1.05);
}
80% {
transform: scale(1);
}
100% {
transform: scale(1);
}
}
.pulse {
animation-name: pulse_animation;
animation-duration: 1500ms;
transform-origin: 70% 70%;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
@keyframes glow-grow {
0% {
opacity: 0;
transform: scale(1);
}
80% {
opacity: 1;
}
100% {
transform: scale(2);
opacity: 0;
}
}
.pulse-glow {
animation-name: glow-grown;
animation-duration: 100ms;
transform-origin: 70% 30%;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
.pulse-glow:before,
.pulse-glow:after {
position: absolute;
content: "";
height: 0.4rem;
width: 1.7rem;
top: 1.3rem;
right: 2.15rem;
border-radius: 0;
box-shadow: 0 0 6px #47d337;
animation: glow-grow 2s ease-out 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;
display: block;
}
#start_container {
position: absolute;
z-index: 99999;
margin-top: 20px;
}
#end_container {
position: absolute;
z-index: 99999;
margin-top: 20px;
right: 0;
}
.pointer {
cursor: pointer;
}
.jumbotron {
background-color: white;
}
.toggle-service {
font-size: 18pt;
float: left;
margin: 2px 3px 0 0;
cursor: pointer;
}
@media (max-width: 767px) {
HTML, BODY {
background-color: #fcfcfc;
}
.sm-container {
margin-top: 0px !important;
padding: 0 !important;
}
.list-group-item H5 {
font-size: 0.9rem;
}
.container {
padding: 0px !important;
padding-top: 15px !important;
}
.group_header {
margin-left: 15px;
}
.navbar {
margin-left: 0px;
margin-top: 0px;
width: 100%;
margin-bottom: 0;
}
.btn-sm {
line-height: 0.9rem;
font-size: 0.65rem;
}
.full-col-12 {
padding-left: 0px;
padding-right: 0px;
}
.card {
border: 0;
border-radius: 0rem;
padding: 0;
background-color: #ffffff;
}
.card-body {
font-size: 10pt;
padding: 10px 10px;
}
.lg_number {
font-size: 7.8vw;
}
.stats_area {
margin-top: 1.5rem !important;
margin-bottom: 1.5rem !important;
}
.stats_area .col-4 {
padding-left: 0;
padding-right: 0;
font-size: 0.6rem;
}
.list-group-item {
border-top: 1px solid #e4e4e4;
border: 0px;
}
.list-group-item:first-child {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.list-group-item:last-child {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.list-group-item P {
font-size: 0.7rem;
}
.service-chart-container {
height: 200px;
}
}
/*# sourceMappingURL=base.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,147 +0,0 @@
/* flatpickr v4.5.2, @license MIT */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.rangePlugin = factory());
}(this, (function () { 'use strict';
function rangePlugin(config) {
if (config === void 0) {
config = {};
}
return function (fp) {
var dateFormat = "",
secondInput,
_secondInputFocused,
_prevDates;
var createSecondInput = function createSecondInput() {
if (config.input) {
secondInput = config.input instanceof Element ? config.input : window.document.querySelector(config.input);
} else {
secondInput = fp._input.cloneNode();
secondInput.removeAttribute("id");
secondInput._flatpickr = undefined;
}
if (secondInput.value) {
var parsedDate = fp.parseDate(secondInput.value);
if (parsedDate) fp.selectedDates.push(parsedDate);
}
secondInput.setAttribute("data-fp-omit", "");
fp._bind(secondInput, ["focus", "click"], function () {
if (fp.selectedDates[1]) {
fp.latestSelectedDateObj = fp.selectedDates[1];
fp._setHoursFromDate(fp.selectedDates[1]);
fp.jumpToDate(fp.selectedDates[1]);
}
_secondInputFocused = true;
fp.isOpen = false;
fp.open(undefined, secondInput);
});
fp._bind(fp._input, ["focus", "click"], function (e) {
e.preventDefault();
fp.isOpen = false;
fp.open();
});
if (fp.config.allowInput) fp._bind(secondInput, "keydown", function (e) {
if (e.key === "Enter") {
fp.setDate([fp.selectedDates[0], secondInput.value], true, dateFormat);
secondInput.click();
}
});
if (!config.input) fp._input.parentNode && fp._input.parentNode.insertBefore(secondInput, fp._input.nextSibling);
};
var plugin = {
onParseConfig: function onParseConfig() {
fp.config.mode = "range";
dateFormat = fp.config.altInput ? fp.config.altFormat : fp.config.dateFormat;
},
onReady: function onReady() {
createSecondInput();
fp.config.ignoredFocusElements.push(secondInput);
if (fp.config.allowInput) {
fp._input.removeAttribute("readonly");
secondInput.removeAttribute("readonly");
} else {
secondInput.setAttribute("readonly", "readonly");
}
fp._bind(fp._input, "focus", function () {
fp.latestSelectedDateObj = fp.selectedDates[0];
fp._setHoursFromDate(fp.selectedDates[0]);
_secondInputFocused = false;
fp.jumpToDate(fp.selectedDates[0]);
});
if (fp.config.allowInput) fp._bind(fp._input, "keydown", function (e) {
if (e.key === "Enter") fp.setDate([fp._input.value, fp.selectedDates[1]], true, dateFormat);
});
fp.setDate(fp.selectedDates, false);
plugin.onValueUpdate(fp.selectedDates);
},
onPreCalendarPosition: function onPreCalendarPosition() {
if (_secondInputFocused) {
fp._positionElement = secondInput;
setTimeout(function () {
fp._positionElement = fp._input;
}, 0);
}
},
onChange: function onChange() {
if (!fp.selectedDates.length) {
setTimeout(function () {
if (fp.selectedDates.length) return;
secondInput.value = "";
_prevDates = [];
}, 10);
}
if (_secondInputFocused) {
setTimeout(function () {
secondInput.focus();
}, 0);
}
},
onDestroy: function onDestroy() {
if (!config.input) secondInput.parentNode && secondInput.parentNode.removeChild(secondInput);
},
onValueUpdate: function onValueUpdate(selDates) {
if (!secondInput) return;
_prevDates = !_prevDates || selDates.length >= _prevDates.length ? selDates.concat() : _prevDates;
if (_prevDates.length > selDates.length) {
var newSelectedDate = selDates[0];
var newDates = _secondInputFocused ? [_prevDates[0], newSelectedDate] : [newSelectedDate, _prevDates[1]];
fp.setDate(newDates, false);
_prevDates = newDates.concat();
}
var _fp$selectedDates$map = fp.selectedDates.map(function (d) {
return fp.formatDate(d, dateFormat);
});
var _fp$selectedDates$map2 = _fp$selectedDates$map[0];
fp._input.value = _fp$selectedDates$map2 === void 0 ? "" : _fp$selectedDates$map2;
var _fp$selectedDates$map3 = _fp$selectedDates$map[1];
secondInput.value = _fp$selectedDates$map3 === void 0 ? "" : _fp$selectedDates$map3;
}
};
return plugin;
};
}
return rangePlugin;
})));

View File

@ -13,7 +13,7 @@ class Api {
} }
async core_save (obj) { async core_save (obj) {
return axios.post('/api', obj).then(response => (response.data)) return axios.post('/api/core', obj).then(response => (response.data))
} }
async services () { async services () {

View File

@ -34,13 +34,11 @@
<div class="col-12"> <div class="col-12">
<h1 class="text-black-50 mt-5">Create Message</h1> <h1 class="text-black-50 mt-5">Create Message</h1>
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<FormMessage/> <FormMessage/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
@ -62,7 +60,7 @@
}, },
methods: { methods: {
service (id) { service (id) {
return this.$store.getters.serviceById(id) return this.$store.getters.serviceById(id).name || ""
}, },
async deleteMessage(m) { async deleteMessage(m) {
let c = confirm(`Are you sure you want to delete message '${m.title}'?`) let c = confirm(`Are you sure you want to delete message '${m.title}'?`)

View File

@ -1,20 +1,3 @@
<!--
- Statup
- Copyright (C) 2020. Hunter Long and the project contributors
- Written by Hunter Long <info@socialeck.com> and the project contributors
-
- https://github.com/hunterlong/statup
-
- The licenses for most software and other practical works are designed
- to take away your freedom to share and change the works. By contrast,
- the GNU General Public License is intended to guarantee your freedom to
- share and change all versions of a program--to make sure it remains free
- software for all its users.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<template> <template>
<div> <div>
<div class="col-12"> <div class="col-12">

View File

@ -1,5 +1,7 @@
<template> <template>
<div>
<FormService v-if="ready" :in_service="service"/> <FormService v-if="ready" :in_service="service"/>
</div>
</template> </template>
<script> <script>

View File

@ -18,10 +18,9 @@
<div class="col-4 col-sm-3 mt-sm-1 mt-0"> <div class="col-4 col-sm-3 mt-sm-1 mt-0">
<label class="d-inline d-sm-none">Enable CDN</label> <label class="d-inline d-sm-none">Enable CDN</label>
<label class="d-none d-sm-block">Enable CDN</label> <label class="d-none d-sm-block">Enable CDN</label>
<span class="switch"> <span @click="core.using_cdn = !!core.using_cdn" class="switch">
<input @change="core.using_cdn = !core.using_cdn" type="checkbox" name="admin" class="switch" id="switch-normal"> <input type="checkbox" name="using_cdn" class="switch" id="switch-normal" v-bind:checked="core.using_cdn">
<label for="switch-normal"></label> <label for="switch-normal"></label>
<input type="hidden" name="admin" id="switch-normal-value">
</span> </span>
</div> </div>
</div> </div>
@ -34,7 +33,7 @@
<div class="form-group"> <div class="form-group">
<label for="timezone">Timezone</label><span class="mt-1 small float-right">Current: {{now}}</span> <label for="timezone">Timezone</label><span class="mt-1 small float-right">Current: {{now}}</span>
<select class="form-control" name="timezone" id="timezone"> <select v-model="core.timezone" class="form-control" name="timezone" id="timezone">
<option value="-12.0" >(GMT -12:00) Eniwetok, Kwajalein</option> <option value="-12.0" >(GMT -12:00) Eniwetok, Kwajalein</option>
<option value="-11.0" >(GMT -11:00) Midway Island, Samoa</option> <option value="-11.0" >(GMT -11:00) Midway Island, Samoa</option>
<option value="-10.0" >(GMT -10:00) Hawaii</option> <option value="-10.0" >(GMT -10:00) Hawaii</option>
@ -69,6 +68,17 @@
</select> </select>
</div> </div>
<div class="form-group">
<div class="col-12">
<label class="d-none d-sm-block">Send Updates only</label>
<span class="switch">
<input @change="core.update_notify = !core.update_notify" type="checkbox" name="update_notify-option" class="switch" id="switch-update_notify" v-bind:checked="core.update_notify">
<label for="switch-update_notify" class="mt-2 mt-sm-0"></label>
<small class="form-text text-muted">Enabling this will send only notifications when the status of a services changes.</small>
</span>
</div>
</div>
<button @click="saveSettings" type="submit" class="btn btn-primary btn-block">Save Settings</button> <button @click="saveSettings" type="submit" class="btn btn-primary btn-block">Save Settings</button>
<div class="form-group row mt-3"> <div class="form-group row mt-3">
@ -99,24 +109,28 @@
name: 'CoreSettings', name: 'CoreSettings',
data () { data () {
return { return {
core: this.$store.getters.core, core: null,
} }
}, },
async mounted () { async created() {
const core = await Api.core() const core = await Api.core()
this.core = core
this.$store.commit('setCore', core) this.$store.commit('setCore', core)
}, },
computed: { async mounted () {
now () {
return time.now()
}
}, },
methods: { methods: {
async saveSettings (e) { async saveSettings (e) {
e.preventDefault() e.preventDefault()
await Api.core_save(this.core) const c = this.core
const coreForm = {name: c.name, description: c.description, domain: c.domain,
timezone: c.timezone, using_cdn: c.using_cdn, footer: c.footer}
alert(JSON.stringify(coreForm))
await Api.core_save(coreForm)
const core = await Api.core() const core = await Api.core()
this.$store.commit('setCore', core) this.$store.commit('setCore', core)
this.core = core
}, },
async renewApiKeys () { async renewApiKeys () {
let r = confirm("Are you sure you want to reset the API keys?"); let r = confirm("Are you sure you want to reset the API keys?");
@ -133,4 +147,6 @@
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped> <style scoped>
@import "/public/css/codemirror.css";
@import "/public/css/codemirror-colorpicker.css";
</style> </style>

View File

@ -9,8 +9,8 @@
<div class="form-group row"> <div class="form-group row">
<label for="switch-group-public" class="col-sm-4 col-form-label">Public Group</label> <label for="switch-group-public" class="col-sm-4 col-form-label">Public Group</label>
<div class="col-8 mt-1"> <div class="col-8 mt-1">
<span class="switch float-left"> <span @click="group.public = !!group.public" class="switch float-left">
<input v-model="group.public" type="checkbox" name="public" class="switch" id="switch-group-public" > <input v-model="group.public" type="checkbox" name="public" class="switch" id="switch-group-public" v-bind:checked="group.public">
<label for="switch-group-public">Show group services to the public</label> <label for="switch-group-public">Show group services to the public</label>
</span> </span>
</div> </div>
@ -30,7 +30,9 @@ import Api from "../components/API";
export default { export default {
name: 'FormGroup', name: 'FormGroup',
props: { props: {
in_group: {
type: Object
}
}, },
data () { data () {
return { return {
@ -41,14 +43,15 @@ export default {
} }
}, },
mounted() { mounted() {
if (this.props.group) { if (this.props.in_group) {
this.group = this.props.group this.group = this.props.in_group
} }
}, },
methods: { methods: {
async saveGroup(e) { async saveGroup(e) {
e.preventDefault(); e.preventDefault();
const data = {name: this.group.name, public: this.group.public} const g = this.group
const data = {name: g.name, public: g.public}
await Api.group_create(data) await Api.group_create(data)
const groups = await Api.groups() const groups = await Api.groups()
this.$store.commit('setGroups', groups) this.$store.commit('setGroups', groups)

View File

@ -1,6 +1,5 @@
<template> <template>
<form @submit="login"> <form @submit="login">
{{$store.getters.token}}
<div class="form-group row"> <div class="form-group row">
<label for="username" class="col-sm-2 col-form-label">Username</label> <label for="username" class="col-sm-2 col-form-label">Username</label>
<div class="col-sm-10"> <div class="col-sm-10">

View File

@ -66,9 +66,8 @@
<label for="notify_method" class="col-sm-4 col-form-label">Notify Users</label> <label for="notify_method" class="col-sm-4 col-form-label">Notify Users</label>
<div class="col-sm-8"> <div class="col-sm-8">
<span class="switch"> <span class="switch">
<input type="checkbox" name="notify_users-value" class="switch" id="switch-normal"> <input @click="message.notify = !!message.notify" type="checkbox" class="switch" id="switch-normal">
<label for="switch-normal">Notify Users Before Scheduled Time</label> <label for="switch-normal">Notify Users Before Scheduled Time</label>
<input type="hidden" name="notify_users" id="switch-normal-value" value="false">
</span> </span>
</div> </div>
</div> </div>

View File

@ -24,10 +24,9 @@
</div> </div>
<div class="col-3 col-sm-2 mt-1"> <div class="col-3 col-sm-2 mt-1">
<span class="switch"> <span @click="notifier.enabled = !!notifier.enabled" class="switch">
<input @change="notifier.enabled = !notifier.enabled" type="checkbox" name="enabled-option" class="switch" v-model="notifier.enabled" v-bind:id="`switch-${notifier.method}`"> <input type="checkbox" name="enabled-option" class="switch" v-model="notifier.enabled" v-bind:id="`switch-${notifier.method}`" v-bind:checked="notifier.enabled">
<label v-bind:for="`switch-${notifier.method}`"></label> <label v-bind:for="`switch-${notifier.method}`"></label>
<input type="hidden" name="enabled" v-bind:id="`switch-${notifier.method}`">
</span> </span>
</div> </div>
@ -90,7 +89,6 @@
form.enabled = this.notifier.enabled form.enabled = this.notifier.enabled
form.limits = parseInt(this.notifier.limits) form.limits = parseInt(this.notifier.limits)
form.method = this.notifier.method form.method = this.notifier.method
alert(JSON.stringify(form))
await Api.notifier_save(form) await Api.notifier_save(form)
const notifiers = await Api.notifiers() const notifiers = await Api.notifiers()
this.$store.commit('setNotifiers', notifiers) this.$store.commit('setNotifiers', notifiers)

View File

@ -6,10 +6,9 @@
<input v-model="user.username" type="text" class="form-control" placeholder="Username" required autocorrect="off" autocapitalize="none"> <input v-model="user.username" type="text" class="form-control" placeholder="Username" required autocorrect="off" autocapitalize="none">
</div> </div>
<div class="col-6 col-md-4"> <div class="col-6 col-md-4">
<span class="switch"> <span @click="user.admin = !!user.admin" class="switch">
<input @change="user.admin = !user.admin" type="checkbox" name="admin" class="switch" id="switch-normal"> <input type="checkbox" name="admin" class="switch" id="switch-normal" v-bind:checked="user.admin">
<label for="switch-normal">Administrator</label> <label for="switch-normal">Administrator</label>
<input type="hidden" name="admin" id="switch-normal-value">
</span> </span>
</div> </div>
</div> </div>

View File

@ -2,7 +2,7 @@
<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 bg-light">
<div class="col-10 offset-1 col-md-8 offset-md-2 mt-md-2"> <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"> <div class="col-12 col-md-8 offset-md-2 mb-4">
<img class="col-12 mt-5 mt-md-0" src="../assets/banner.png"> <img class="col-12 mt-5 mt-md-0" src="/public/img/banner.png">
</div> </div>
<FormLogin/> <FormLogin/>
</div> </div>

View File

@ -26,7 +26,7 @@
methods: { methods: {
async loadAll () { async loadAll () {
this.auth = Api.authToken() this.auth = Api.authToken()
this.core = await Api.root() this.core = await Api.core()
this.groups = await Api.groups() this.groups = await Api.groups()
this.services = await Api.services() this.services = await Api.services()
} }

View File

@ -6,13 +6,14 @@
<h6 class="text-muted">Main Settings</h6> <h6 class="text-muted">Main Settings</h6>
<a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-home-tab')}" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true"><i class="fa fa-cogs"></i> Settings</a> <a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-home-tab')}" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true"><i class="fa fa-cogs"></i> Settings</a>
<a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-notifications-tab')}" id="v-pills-notifications-tab" data-toggle="pill" href="#v-pills-notifications" role="tab" aria-controls="v-pills-notifications" aria-selected="true"><i class="fa fa-bell"></i> Notifications</a>
<a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-style-tab')}" id="v-pills-style-tab" data-toggle="pill" href="#v-pills-style" role="tab" aria-controls="v-pills-style" aria-selected="false"><i class="fa fa-image"></i> Theme Editor</a> <a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-style-tab')}" id="v-pills-style-tab" data-toggle="pill" href="#v-pills-style" role="tab" aria-controls="v-pills-style" aria-selected="false"><i class="fa fa-image"></i> Theme Editor</a>
<a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-cache-tab')}" id="v-pills-cache-tab" data-toggle="pill" href="#v-pills-cache" role="tab" aria-controls="v-pills-cache" aria-selected="false"><i class="fa fa-paperclip"></i> Cache</a> <a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-cache-tab')}" id="v-pills-cache-tab" data-toggle="pill" href="#v-pills-cache" role="tab" aria-controls="v-pills-cache" aria-selected="false"><i class="fa fa-paperclip"></i> Cache</a>
<h6 class="mt-4 text-muted">Notifiers</h6> <h6 class="mt-4 text-muted">Notifiers</h6>
<a v-for="(notifier, index) in $store.getters.notifiers" v-bind:key="index" v-on:click="changeTab" class="nav-link text-capitalize" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" data-toggle="pill" v-bind:href="`#v-pills-${notifier.method.toLowerCase()}`" role="tab" v-bind:aria-controls="`v-pills-${notifier.method.toLowerCase()}`" aria-selected="false"><i class="fas fa-terminal"></i> {{notifier.method}}</a> <a v-for="(notifier, index) in $store.getters.notifiers" v-bind:key="index" v-on:click="changeTab" class="nav-link text-capitalize" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" data-toggle="pill" v-bind:href="`#v-pills-${notifier.method.toLowerCase()}`" role="tab" v-bind:aria-controls="`v-pills-${notifier.method.toLowerCase()}`" aria-selected="false">
<i class="fas fa-terminal"></i> {{notifier.method}}
</a>
<h6 class="mt-4 text-muted">Integrations (beta)</h6> <h6 class="mt-4 text-muted">Integrations (beta)</h6>
@ -52,7 +53,7 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="row align-content-center"> <div class="row align-content-center">
<img class="rounded text-center" width="300" height="300" src="https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=statping%3a%2f%2fsetup%3fdomain%3dhttps%3a%2f%2fdemo.statping.com%26api%3d6b05b48f4b3a1460f3864c31b26cab6a27dbaff9"> <img class="rounded text-center" width="300" height="300" :src="qrcode">
</div> </div>
<a class="btn btn-sm btn-primary" href=statping://setup?domain&#61;https://demo.statping.com&amp;api&#61;6b05b48f4b3a1460f3864c31b26cab6a27dbaff9>Open in Statping App</a> <a class="btn btn-sm btn-primary" href=statping://setup?domain&#61;https://demo.statping.com&amp;api&#61;6b05b48f4b3a1460f3864c31b26cab6a27dbaff9>Open in Statping App</a>
<a href="settings/export" class="btn btn-sm btn-secondary">Export Settings</a> <a href="settings/export" class="btn btn-sm btn-secondary">Export Settings</a>
@ -61,30 +62,6 @@
</div> </div>
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-notifications-tab'), show: liClass('v-pills-notifications-tab')}" id="v-pills-notifications" role="tabpanel" aria-labelledby="v-pills-notifications-tab">
<h3>Notifications</h3>
<form method="POST" action="settings">
<div class="form-group">
<div class="col-12">
<label class="d-inline d-sm-none">Send Updates only</label>
<label class="d-none d-sm-block">Send Updates only</label>
<span class="switch">
<input type="checkbox" name="update_notify-option" class="switch" id="switch-update_notify">
<label for="switch-update_notify" class="mt-2 mt-sm-0"></label>
<small class="form-text text-muted">Enabling this will send only notifications when the status of a services changes.</small>
</span>
<input type="hidden" name="update_notify" id="switch-update_notify-value" value="false">
</div>
</div>
<button type="submit" class="btn btn-primary btn-block">Save Settings</button>
</form>
</div>
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-style-tab'), show: liClass('v-pills-style-tab')}" id="v-pills-style" role="tabpanel" aria-labelledby="v-pills-style-tab"> <div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-style-tab'), show: liClass('v-pills-style-tab')}" id="v-pills-style" role="tabpanel" aria-labelledby="v-pills-style-tab">
@ -162,7 +139,7 @@
<button type="submit" class="btn btn-block btn-info fetch_integrator">Fetch Services</button> <button type="submit" class="btn btn-block btn-info fetch_integrator">Fetch Services</button>
<div class="alert alert-danger d-none" id="integration_alerter" role="alert"></div> <div class="alert alert-danger d-none" role="alert"></div>
</form> </form>
</div> </div>
@ -288,6 +265,7 @@
data () { data () {
return { return {
tab: "v-pills-home-tab", tab: "v-pills-home-tab",
qrcode: ""
} }
}, },
async created() { async created() {
@ -295,6 +273,8 @@
this.$store.commit('setCore', core) this.$store.commit('setCore', core)
const notifiers = await Api.notifiers() const notifiers = await Api.notifiers()
this.$store.commit('setNotifiers', notifiers) this.$store.commit('setNotifiers', notifiers)
const qrurl = `statping://setup?domain=${core.domain}&api=${core.api_secret}`
this.qrcode = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURI(qrurl)
}, },
beforeMount() { beforeMount() {

View File

@ -16,6 +16,7 @@
package handlers package handlers
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/hunterlong/statping/core" "github.com/hunterlong/statping/core"
@ -52,6 +53,39 @@ func apiRenewHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/settings", http.StatusSeeOther) http.Redirect(w, r, "/settings", http.StatusSeeOther)
} }
func apiCoreHandler(w http.ResponseWriter, r *http.Request) {
var c *core.Core
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&c)
if err != nil {
sendErrorJson(err, w, r)
return
}
app := core.CoreApp
if c.Name != "" {
app.Name = c.Name
}
if c.Description != app.Description {
app.Description = c.Description
}
if c.Style != app.Style {
app.Style = c.Style
}
if c.Footer.String != app.Footer.String {
app.Footer = c.Footer
}
if c.Domain != app.Domain {
app.Domain = c.Domain
}
if c.Timezone != app.Timezone {
app.Timezone = c.Timezone
}
app.UpdateNotify = c.UpdateNotify
app.UseCdn = types.NewNullBool(c.UseCdn.Bool)
core.CoreApp, err = core.UpdateCore(app)
returnJson(core.CoreApp, w, r)
}
func apiClearCacheHandler(w http.ResponseWriter, r *http.Request) { func apiClearCacheHandler(w http.ResponseWriter, r *http.Request) {
CacheStorage = NewStorage() CacheStorage = NewStorage()
http.Redirect(w, r, basePath, http.StatusSeeOther) http.Redirect(w, r, basePath, http.StatusSeeOther)

View File

@ -118,6 +118,7 @@ func Router() *mux.Router {
r.Handle("/api/logout", http.HandlerFunc(logoutHandler)) r.Handle("/api/logout", http.HandlerFunc(logoutHandler))
r.Handle("/api/renew", authenticated(apiRenewHandler, false)) r.Handle("/api/renew", authenticated(apiRenewHandler, false))
r.Handle("/api/clear_cache", authenticated(apiClearCacheHandler, false)) r.Handle("/api/clear_cache", authenticated(apiClearCacheHandler, false))
r.Handle("/api/core", authenticated(apiCoreHandler, false)).Methods("POST")
r.Handle("/api/integrations", authenticated(apiAllIntegrationsHandler, false)).Methods("GET") r.Handle("/api/integrations", authenticated(apiAllIntegrationsHandler, false)).Methods("GET")
r.Handle("/api/integrations/{name}", authenticated(apiIntegrationHandler, false)).Methods("GET") r.Handle("/api/integrations/{name}", authenticated(apiIntegrationHandler, false)).Methods("GET")