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) {
return axios.post('/api', obj).then(response => (response.data))
return axios.post('/api/core', obj).then(response => (response.data))
}
async services () {

View File

@ -34,13 +34,11 @@
<div class="col-12">
<h1 class="text-black-50 mt-5">Create Message</h1>
<div class="card">
<div class="card-body">
<FormMessage/>
</div>
</div>
</div>
</div>
</template>
@ -62,7 +60,7 @@
},
methods: {
service (id) {
return this.$store.getters.serviceById(id)
return this.$store.getters.serviceById(id).name || ""
},
async deleteMessage(m) {
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>
<div>
<div class="col-12">

View File

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

View File

@ -18,10 +18,9 @@
<div class="col-4 col-sm-3 mt-sm-1 mt-0">
<label class="d-inline d-sm-none">Enable CDN</label>
<label class="d-none d-sm-block">Enable CDN</label>
<span class="switch">
<input @change="core.using_cdn = !core.using_cdn" type="checkbox" name="admin" class="switch" id="switch-normal">
<span @click="core.using_cdn = !!core.using_cdn" class="switch">
<input type="checkbox" name="using_cdn" class="switch" id="switch-normal" v-bind:checked="core.using_cdn">
<label for="switch-normal"></label>
<input type="hidden" name="admin" id="switch-normal-value">
</span>
</div>
</div>
@ -34,7 +33,7 @@
<div class="form-group">
<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="-11.0" >(GMT -11:00) Midway Island, Samoa</option>
<option value="-10.0" >(GMT -10:00) Hawaii</option>
@ -69,6 +68,17 @@
</select>
</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>
<div class="form-group row mt-3">
@ -92,31 +102,35 @@
</template>
<script>
import time from '../components/Time'
import Api from '../components/API'
import time from '../components/Time'
import Api from '../components/API'
export default {
export default {
name: 'CoreSettings',
data () {
return {
core: this.$store.getters.core,
core: null,
}
},
async mounted () {
const core = await Api.core()
this.$store.commit('setCore', core)
},
computed: {
now () {
return time.now()
}
async created() {
const core = await Api.core()
this.core = core
this.$store.commit('setCore', core)
},
async mounted () {
},
methods: {
async saveSettings (e) {
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()
this.$store.commit('setCore', core)
this.core = core
},
async renewApiKeys () {
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 -->
<style scoped>
@import "/public/css/codemirror.css";
@import "/public/css/codemirror-colorpicker.css";
</style>

View File

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

View File

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

View File

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

View File

@ -24,10 +24,9 @@
</div>
<div class="col-3 col-sm-2 mt-1">
<span 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}`">
<span @click="notifier.enabled = !!notifier.enabled" class="switch">
<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>
<input type="hidden" name="enabled" v-bind:id="`switch-${notifier.method}`">
</span>
</div>
@ -90,7 +89,6 @@
form.enabled = this.notifier.enabled
form.limits = parseInt(this.notifier.limits)
form.method = this.notifier.method
alert(JSON.stringify(form))
await Api.notifier_save(form)
const notifiers = await Api.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">
</div>
<div class="col-6 col-md-4">
<span class="switch">
<input @change="user.admin = !user.admin" type="checkbox" name="admin" class="switch" id="switch-normal">
<span @click="user.admin = !!user.admin" class="switch">
<input type="checkbox" name="admin" class="switch" id="switch-normal" v-bind:checked="user.admin">
<label for="switch-normal">Administrator</label>
<input type="hidden" name="admin" id="switch-normal-value">
</span>
</div>
</div>

View File

@ -2,7 +2,7 @@
<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-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>
<FormLogin/>
</div>
@ -10,9 +10,9 @@
</template>
<script>
import FormLogin from "../forms/Login";
import FormLogin from "../forms/Login";
export default {
export default {
name: 'Login',
components: {
FormLogin

View File

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

View File

@ -6,13 +6,14 @@
<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-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-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>
<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>
@ -52,7 +53,7 @@
<div class="row">
<div class="col-12">
<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>
<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>
@ -61,30 +62,6 @@
</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">
@ -162,7 +139,7 @@
<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>
</div>
@ -288,6 +265,7 @@
data () {
return {
tab: "v-pills-home-tab",
qrcode: ""
}
},
async created() {
@ -295,6 +273,8 @@
this.$store.commit('setCore', core)
const notifiers = await Api.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() {

View File

@ -16,6 +16,7 @@
package handlers
import (
"encoding/json"
"errors"
"fmt"
"github.com/hunterlong/statping/core"
@ -52,6 +53,39 @@ func apiRenewHandler(w http.ResponseWriter, r *http.Request) {
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) {
CacheStorage = NewStorage()
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/renew", authenticated(apiRenewHandler, 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/{name}", authenticated(apiIntegrationHandler, false)).Methods("GET")