pull/429/head
hunterlong 2020-01-17 20:02:00 -08:00
parent c342b80e93
commit 8b4cf43984
15 changed files with 265 additions and 61 deletions

View File

@ -22,6 +22,7 @@
"vue": "^2.6.10",
"vue-apexcharts": "^1.5.2",
"vue-router": "~3.0",
"vuedraggable": "^2.23.2",
"vuex": "^3.1.2"
},
"devDependencies": {

View File

@ -1,5 +1,5 @@
<template>
<div id="app">
<div id="app" v-if="ready">
<router-view/>
<Footer version="DEV" />
</div>
@ -14,6 +14,11 @@ export default {
components: {
Footer
},
computed: {
ready () {
}
},
created () {
if (!this.$store.getters.hasPublicData) {
this.setAllObjects()

File diff suppressed because one or more lines are too long

1
frontend/src/assets/js/inputTags.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,147 @@
/* 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

@ -40,6 +40,10 @@ class Api {
return axios.get('/api/notifiers').then(response => (response.data))
}
async renewApiKeys () {
return axios.get('/api/renew').then(response => (response.data))
}
async login (username, password) {
const f = {username: username, password: password}
return axios.post('/api/login', qs.stringify(f))

View File

@ -26,11 +26,9 @@
</div>
</div>
<div class="form-group">
<label>Custom Footer</label>
<textarea rows="4" class="form-control">{{core.footer}}</textarea>
<textarea v-model="core.footer" rows="4" class="form-control">{{core.footer}}</textarea>
<small class="form-text text-muted">HTML is allowed inside the footer</small>
</div>
@ -71,22 +69,22 @@
</select>
</div>
<button type="submit" class="btn btn-primary btn-block">Save Settings</button>
<button v-on:submit="saveSettings" type="submit" class="btn btn-primary btn-block">Save Settings</button>
<div class="form-group row mt-3">
<label for="api_key" class="col-sm-3 col-form-label">API Key</label>
<label class="col-sm-3 col-form-label">API Key</label>
<div class="col-sm-9">
<input type="text" class="form-control select-input" value="9e657102489b63946908a084befc187e6e506eb0" id="api_key" readonly>
<input v-model="core.api_key" type="text" class="form-control select-input" readonly>
<small class="form-text text-muted">API Key can be used for read only routes</small>
</div>
</div>
<div class="form-group row">
<label for="api_secret" class="col-sm-3 col-form-label">API Secret</label>
<label class="col-sm-3 col-form-label">API Secret</label>
<div class="col-sm-9">
<input type="text" class="form-control select-input" value="6b05b48f4b3a1460f3864c31b26cab6a27dbaff9" id="api_secret" readonly>
<input v-model="core.api_secret" type="text" class="form-control select-input" readonly>
<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 class="confirm_btn" data-msg="Are you sure you want to reset the API keys?" href="api/renew">Regenerate API Keys</a> if you need to.</small>
<small class="form-text text-muted">You can <a href="#" v-on:click="renewApiKeys">Regenerate API Keys</a> if you need to.</small>
</div>
</div>
@ -95,6 +93,7 @@
<script>
import time from '../components/Time'
import Api from '../components/API'
export default {
name: 'CoreSettings',
@ -111,6 +110,15 @@ export default {
methods: {
saveSettings () {
},
async renewApiKeys () {
let r = confirm("Are you sure you want to reset the API keys?");
if (r === true) {
await Api.renewApiKeys()
const core = await Api.core()
this.$store.commit('setCore', core)
this.core = core
}
}
}
}

View File

@ -1,8 +1,18 @@
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export const HAS_ALL_DATA = 'HAS_ALL_DATA'
export const HAS_PUBLIC_DATA = 'HAS_PUBLIC_DATA'
export const GET_CORE = 'GET_CORE'
export const GET_SERVICES = 'GET_SERVICES'
export const GET_TOKEN = 'GET_TOKEN'
export const GET_GROUPS = 'GET_GROUPS'
export const GET_MESSAGES = 'GET_MESSAGES'
export const GET_NOTIFIERS = 'GET_NOTIFIERS'
export const GET_USERS = 'GET_USERS'
export default new Vuex.Store({
state: {
hasAllData: false,

View File

@ -837,45 +837,45 @@
"@fortawesome/fontawesome-common-types@^0.1.2-1":
version "0.1.7"
resolved "https://npm.fontawesome.com/@fortawesome/fontawesome-common-types/-/0.1.7/fontawesome-common-types-0.1.7.tgz#4336c4b06d0b5608ff1215464b66fcf9f4795284"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.1.7.tgz#4336c4b06d0b5608ff1215464b66fcf9f4795284"
integrity sha512-ego8jRVSHfq/iq4KRZJKQeUAdi3ZjGNrqw4oPN3fNdvTBnLCSntwVCnc37bsAJP9UB8MhrTfPnZYxkv2vpS4pg==
"@fortawesome/fontawesome-common-types@^0.2.26":
version "0.2.26"
resolved "https://npm.fontawesome.com/@fortawesome/fontawesome-common-types/-/0.2.26/fontawesome-common-types-0.2.26.tgz#6e0b13a752676036f8196f8a1500d53a27b4adc1"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.26.tgz#6e0b13a752676036f8196f8a1500d53a27b4adc1"
integrity sha512-CcM/fIFwZlRdiWG/25xE/wHbtyUuCtqoCTrr6BsWw7hH072fR++n4L56KPydAr3ANgMJMjT8v83ZFIsDc7kE+A==
"@fortawesome/fontawesome-free-solid@^5.1.0-3":
version "5.1.0-3"
resolved "https://npm.fontawesome.com/@fortawesome/fontawesome-free-solid/-/5.1.0-3/fontawesome-free-solid-5.1.0-3.tgz#60e6e1cc7588b933a570bcfc9eb78b9cadda451a"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free-solid/-/fontawesome-free-solid-5.1.0-3.tgz#60e6e1cc7588b933a570bcfc9eb78b9cadda451a"
integrity sha512-E9b+bU91/4538vBvAeaTsvt2r9wTYEoKniuhbq8+RNI08whrUs1RGeaCzyh7ON0rrbJZz/Ae3g8EuCmfsYwfzQ==
dependencies:
"@fortawesome/fontawesome-common-types" "^0.1.2-1"
"@fortawesome/fontawesome-svg-core@^1.2.26":
version "1.2.26"
resolved "https://npm.fontawesome.com/@fortawesome/fontawesome-svg-core/-/1.2.26/fontawesome-svg-core-1.2.26.tgz#671569271d6b532cdea5e3deb8ff16f8b7ac251d"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.26.tgz#671569271d6b532cdea5e3deb8ff16f8b7ac251d"
integrity sha512-3Dfd/v2IztP1TxKOxZiB5+4kaOZK9mNy0KU1vVK7nFlPWz3gzxrCWB+AloQhQUoJ8HhGqbzjliK89Vl7PExGbw==
dependencies:
"@fortawesome/fontawesome-common-types" "^0.2.26"
"@fortawesome/free-brands-svg-icons@^5.12.0":
version "5.12.0"
resolved "https://npm.fontawesome.com/@fortawesome/free-brands-svg-icons/-/5.12.0/free-brands-svg-icons-5.12.0.tgz#b0c78627f811ac030ee0ac88df376567cf74119d"
resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.12.0.tgz#b0c78627f811ac030ee0ac88df376567cf74119d"
integrity sha512-50uCFzVUki3wfmFmrMNLFhOt8dP6YZ53zwR4dK9FR7Lwq1IVHXnSBb8MtGLe3urLJ2sA+CSu7Pc7s3i6/zLxmA==
dependencies:
"@fortawesome/fontawesome-common-types" "^0.2.26"
"@fortawesome/free-solid-svg-icons@^5.12.0":
version "5.12.0"
resolved "https://npm.fontawesome.com/@fortawesome/free-solid-svg-icons/-/5.12.0/free-solid-svg-icons-5.12.0.tgz#8decac5844e60453cc0c7c51437d1461df053a35"
resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.12.0.tgz#8decac5844e60453cc0c7c51437d1461df053a35"
integrity sha512-CnpsWs6GhTs9ekNB3d8rcO5HYqRkXbYKf2YNiAlTWbj5eVlPqsd/XH1F9If8jkcR1aegryAbln/qYeKVZzpM0g==
dependencies:
"@fortawesome/fontawesome-common-types" "^0.2.26"
"@fortawesome/vue-fontawesome@^0.1.9":
version "0.1.9"
resolved "https://npm.fontawesome.com/@fortawesome/vue-fontawesome/-/0.1.9/vue-fontawesome-0.1.9.tgz#d3af6d4e50f337327de90447fe35fa1e117a2fbe"
resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-0.1.9.tgz#d3af6d4e50f337327de90447fe35fa1e117a2fbe"
integrity sha512-h/emhmZz+DfB2zOGLWawNwXq82UYhn9waTfUjLLmeaIqtnIyNt6kYlpQT/vzJjLZRDRvY2IEJAh1di5qKpKVpA==
"@hapi/address@2.x.x":
@ -2890,9 +2890,9 @@ ejs@^2.6.1:
integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==
electron-to-chromium@^1.3.322:
version "1.3.335"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.335.tgz#5fb6084a25cb1e2542df91e62b62e1931a602303"
integrity sha512-ngKsDGd/xr2lAZvilxTfdvfEiQKmavyXd6irlswaHnewmXoz6JgbM9FUNwgp3NFIUHHegh1F87H8f5BJ8zABxw==
version "1.3.336"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.336.tgz#f0e7a3e78f1c9a0385b6693a4a4b7453f0ae6aaf"
integrity sha512-FtazvnXAizSVMxQNPqUcTv2UElY5r3uRPQwiU1Tyg/Yc2UFr+/3wqDoLIV9ES6ablW3IrCcR8uEK2ppxaNPWhw==
elliptic@^6.0.0:
version "6.5.2"
@ -6472,9 +6472,9 @@ read-pkg@^2.0.0:
util-deprecate "~1.0.1"
readable-stream@^3.0.6, readable-stream@^3.1.1:
version "3.4.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
version "3.5.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.5.0.tgz#465d70e6d1087f6162d079cd0b5db7fbebfd1606"
integrity sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
@ -6836,9 +6836,9 @@ schema-utils@^1.0.0:
ajv-keywords "^3.1.0"
schema-utils@^2.0.0, schema-utils@^2.5.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.2.tgz#9205ec5978709b0d9edbccb9a316faf11617a017"
integrity sha512-sazKNMBX/jwrXRkOI7N6dtiTVYqzSckzol8SGuHt0lE/v3xSW6cUkOqzu6Bq2tW+dlUzq3CWIqHU3ZKauliqdg==
version "2.6.4"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.4.tgz#a27efbf6e4e78689d91872ee3ccfa57d7bdd0f53"
integrity sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==
dependencies:
ajv "^6.10.2"
ajv-keywords "^3.4.1"
@ -7070,6 +7070,11 @@ sockjs@0.3.19:
faye-websocket "^0.10.0"
uuid "^3.0.1"
sortablejs@^1.10.1:
version "1.10.2"
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290"
integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==
source-list-map@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
@ -8006,6 +8011,13 @@ vue@^2.6.10:
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
vuedraggable@^2.23.2:
version "2.23.2"
resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.23.2.tgz#0d95d7fdf4f02f56755a26b3c9dca5c7ca9cfa72"
integrity sha512-PgHCjUpxEAEZJq36ys49HfQmXglattf/7ofOzUrW2/rRdG7tu6fK84ir14t1jYv4kdXewTEa2ieKEAhhEMdwkQ==
dependencies:
sortablejs "^1.10.1"
vuex@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.1.2.tgz#a2863f4005aa73f2587e55c3fadf3f01f69c7d4d"

View File

@ -34,10 +34,10 @@ type apiResponse struct {
Output interface{} `json:"output,omitempty"`
}
func apiIndexHandler(w http.ResponseWriter, r *http.Request) {
func apiIndexHandler(r *http.Request) (interface{}, error) {
coreClone := *core.CoreApp
coreClone.Started = utils.Timezoner(core.CoreApp.Started, core.CoreApp.Timezone)
returnJson(coreClone, w, r)
coreClone.Started = utils.Timezoner(coreClone.Started, coreClone.Timezone)
return *coreClone.ToCore(), nil
}
func apiRenewHandler(w http.ResponseWriter, r *http.Request) {

View File

@ -40,10 +40,10 @@ func groupViewHandler(w http.ResponseWriter, r *http.Request) {
}
// apiAllGroupHandler will show all the groups
func apiAllGroupHandler(w http.ResponseWriter, r *http.Request) {
func apiAllGroupHandler(r *http.Request) (interface{}, error) {
auth, admin := IsUser(r), IsAdmin(r)
groups := core.SelectGroups(admin, auth)
returnJson(groups, w, r)
return groups, nil
}
// apiGroupHandler will show a single group

View File

@ -311,11 +311,10 @@ func expandServices(s []types.ServiceInterface) []*types.Service {
return services
}
func toSafeJson(input interface{}) map[string]interface{} {
func toSafeJson(input interface{}, onlyAdmin, onlyUsers bool) map[string]interface{} {
thisData := make(map[string]interface{})
t := reflect.TypeOf(input)
elem := reflect.ValueOf(input)
d, _ := json.Marshal(input)
var raw map[string]*json.RawMessage
@ -327,30 +326,43 @@ func toSafeJson(input interface{}) map[string]interface{} {
fmt.Println("Type:", t.Name())
fmt.Println("Kind:", t.Kind())
fmt.Println("Fields:", t.NumField())
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
// Get the field tag value
tag := field.Tag.Get("scope")
jsonTag := field.Tag.Get("json")
tags := strings.Split(tag, ",")
if jsonTag == "" || jsonTag == "-" {
jTags := field.Tag.Get("json")
jsonTag := strings.Split(jTags, ",")
fmt.Println(jsonTag, tag)
if len(jsonTag) == 0 {
continue
}
if jsonTag[0] == "" || jsonTag[0] == "-" {
continue
}
trueValue := elem.Field(i).Interface()
trueValue = fixValue(field, trueValue)
if len(jsonTag) == 2 {
if jsonTag[1] == "omitempty" && trueValue == "" {
continue
}
}
if tag == "" {
thisData[jsonTag] = trueValue
thisData[jsonTag[0]] = trueValue
continue
}
if isPublic(tags) {
thisData[jsonTag] = trueValue
if forType(tags, onlyAdmin, onlyUsers) {
thisData[jsonTag[0]] = trueValue
}
fmt.Printf("%d. %v (%v), tags: '%v'\n", i, field.Name, field.Type.Name(), tags)
@ -359,17 +371,17 @@ func toSafeJson(input interface{}) map[string]interface{} {
}
func returnSafeJson(w http.ResponseWriter, r *http.Request, input interface{}) {
admin, user := IsAdmin(r), IsUser(r)
if reflect.ValueOf(input).Kind() == reflect.Slice {
alldata := make([]map[string]interface{}, 0, 1)
s := reflect.ValueOf(input)
for i := 0; i < s.Len(); i++ {
alldata = append(alldata, toSafeJson(s.Index(i).Interface()))
alldata = append(alldata, toSafeJson(s.Index(i).Interface(), admin, user))
}
returnJson(alldata, w, r)
return
}
returnJson(input, w, r)
return
returnJson(toSafeJson(input, admin, user), w, r)
}
func fixValue(field reflect.StructField, val interface{}) interface{} {
@ -392,9 +404,12 @@ func fixValue(field reflect.StructField, val interface{}) interface{} {
}
}
func isPublic(tags []string) bool {
func forType(tags []string, onlyAdmin, onlyUsers bool) bool {
for _, v := range tags {
if v == "public" {
if v == "admin" && onlyAdmin {
return true
}
if v == "user" && onlyUsers {
return true
}
}

View File

@ -15,9 +15,14 @@ var (
authPass string
)
func scopedRoute(handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
func scopedRoute(handler func(r *http.Request) (interface{}, error)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler(w, r)
data, err := handler(r)
if err != nil {
sendErrorJson(err, w, r)
return
}
returnSafeJson(w, r, data)
})
}

View File

@ -121,14 +121,13 @@ func servicesViewHandler(w http.ResponseWriter, r *http.Request) {
ExecuteResponse(w, r, "service.gohtml", out, nil)
}
func apiServiceHandler(w http.ResponseWriter, r *http.Request) {
func apiServiceHandler(r *http.Request) (interface{}, error) {
vars := mux.Vars(r)
servicer := core.SelectService(utils.ToInt(vars["id"]))
if servicer == nil {
sendErrorJson(errors.New("service not found"), w, r)
return
return nil, errors.New("service not found")
}
returnJson(servicer, w, r)
return servicer, nil
}
func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
@ -293,14 +292,9 @@ func apiServiceDeleteHandler(w http.ResponseWriter, r *http.Request) {
sendJsonAction(service, "delete", w, r)
}
func apiAllServicesHandler(w http.ResponseWriter, r *http.Request) {
isAdmin := IsAdmin(r)
func apiAllServicesHandler(r *http.Request) (interface{}, error) {
services := core.Services()
if !isAdmin {
returnSafeJson(w, r, joinServices(services))
return
}
returnJson(services, w, r)
return joinServices(services), nil
}
func joinServices(srvs []types.ServiceInterface) []types.Service {

View File

@ -32,8 +32,8 @@ type Core struct {
Name string `gorm:"not null;column:name" json:"name"`
Description string `gorm:"not null;column:description" json:"description,omitempty"`
ConfigFile string `gorm:"column:config" json:"-"`
ApiKey string `gorm:"column:api_key" json:"-"`
ApiSecret string `gorm:"column:api_secret" json:"-"`
ApiKey string `gorm:"column:api_key" json:"api_key" scope:"admin"`
ApiSecret string `gorm:"column:api_secret" json:"api_secret" scope:"admin"`
Style string `gorm:"not null;column:style" json:"style,omitempty"`
Footer NullString `gorm:"column:footer" json:"footer"`
Domain string `gorm:"not null;column:domain" json:"domain"`
@ -50,6 +50,6 @@ type Core struct {
Repos []PluginJSON `gorm:"-" json:"-"`
AllPlugins []PluginActions `gorm:"-" json:"-"`
Notifications []AllNotifiers `gorm:"-" json:"-"`
Config *DbConfig `gorm:"-" json:"config"`
Config *DbConfig `gorm:"-" json:"-"`
Integrations []Integrator `gorm:"-" json:"-"`
}