vue js public api with scopes within struct tags. neato.

pull/429/head
hunterlong 2020-01-15 00:10:59 -08:00
parent 134bd073ff
commit 30e64f688c
12 changed files with 184 additions and 106 deletions

View File

@ -1,6 +1,6 @@
<template> <template>
<div id="app"> <div id="app">
<router-view></router-view> <router-view/>
<Footer/> <Footer/>
</div> </div>
</template> </template>
@ -9,7 +9,9 @@
import Footer from "./components/Footer"; import Footer from "./components/Footer";
export default { export default {
name: 'app', name: 'app',
components: {Footer}, components: {
Footer
},
} }
</script> </script>

View File

@ -13,10 +13,18 @@ class Api {
return axios.get('/api/services').then(response => (response.data)) return axios.get('/api/services').then(response => (response.data))
} }
async service (id) {
return axios.get('/api/services/'+id).then(response => (response.data))
}
async groups () { async groups () {
return axios.get('/api/groups').then(response => (response.data)) return axios.get('/api/groups').then(response => (response.data))
} }
async group (id) {
return axios.get('/api/groups/'+id).then(response => (response.data))
}
} }
const api = new Api() const api = new Api()
export default api export default api

View File

@ -1,12 +1,50 @@
import Vue from 'vue' import Vue from 'vue'
import App from './App.vue' import App from './App.vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
const router = require("routes") import Index from "./pages/Index";
import Dashboard from "./pages/Dashboard";
import Settings from "./pages/Settings";
import Service from "./pages/Service";
import Services from "./pages/Services";
require("./assets/css/bootstrap.min.css") require("./assets/css/bootstrap.min.css")
require("./assets/css/base.css") require("./assets/css/base.css")
Vue.use(VueRouter) const routes = [
{
path: '/',
name: 'Index',
component: Index
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard
},
{
path: '/settings',
name: 'Settings',
component: Settings
},
{
path: '/services',
name: 'Services',
component: Services
},
{
path: '/service/:id',
name: 'Service',
component: Service
}
];
const router = new VueRouter
({
mode: 'history',
routes
})
Vue.use(VueRouter);
Vue.config.productionTip = false Vue.config.productionTip = false
new Vue({ new Vue({

View File

@ -1,11 +1,8 @@
<template> <template>
<div class="container col-md-7 col-sm-12 mt-2 sm-container"> <div v-show="core" class="container col-md-7 col-sm-12 mt-2 sm-container">
<Header :core="core"/> <Header :core="core"/>
<Group/>
<Group/>
<Group/>
<div v-for="(group, index) in groups" v-bind:key="index"> <div v-for="(group, index) in groups" v-bind:key="index">
<Group :group=group /> <Group :group=group />
</div> </div>
@ -29,6 +26,7 @@ import ServiceBlock from '../components/Service/ServiceBlock.vue'
import MessageBlock from "../components/Index/MessageBlock"; import MessageBlock from "../components/Index/MessageBlock";
import Group from "../components/Index/Group"; import Group from "../components/Index/Group";
import Header from "../components/Index/Header"; import Header from "../components/Index/Header";
import Api from "../components/API"
export default { export default {
name: 'Dashboard', name: 'Dashboard',
@ -46,25 +44,13 @@ export default {
} }
}, },
beforeMount() { beforeMount() {
this.getAPI() this.loadAll()
this.getGroups()
this.getServices()
}, },
methods: { methods: {
getAPI: function() { async loadAll () {
axios this.core = await Api.root()
.get('/api') this.groups = await Api.groups()
.then(response => (this.core = response.data)) this.services = await Api.services()
},
getServices: function() {
axios
.get('/api/services')
.then(response => (this.services = response.data))
},
getGroups: function() {
axios
.get('/api/groups')
.then(response => (this.groups = response.data))
} }
} }
} }

View File

@ -1,10 +1,11 @@
<template> <template>
<div class="container col-md-7 col-sm-12 mt-2 sm-container"> <div class="container col-md-7 col-sm-12 mt-2 sm-container">
{{service}}
</div> </div>
</template> </template>
<script> <script>
import Api from "../components/API"
export default { export default {
name: 'Service', name: 'Service',
@ -13,14 +14,18 @@ export default {
}, },
data () { data () {
return { return {
id: null,
service: null, service: null,
} }
}, },
beforeMount() { beforeMount() {
this.id = this.$route.params.id
this.getService()
}, },
methods: { methods: {
async getService() {
this.service = await Api.services()
}
} }
} }
</script> </script>

View File

@ -1,26 +0,0 @@
import Index from "./pages/Index";
import VueRouter from 'vue-router'
import Dashboard from "./pages/Dashboard";
import Settings from "./pages/Settings";
const router = new VueRouter({
routes: [
{
path: '/',
name: 'Index',
component: Index
},
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard
},
{
path: '/settings',
name: 'Settings',
component: Settings
}
]
})
export default router

View File

@ -4,7 +4,7 @@ module.exports = {
proxy: { proxy: {
'/api': { '/api': {
logLevel: 'debug', logLevel: 'debug',
target: 'http://0.0.0.0:8282' target: 'http://0.0.0.0:8585'
} }
} }
} }

View File

@ -294,43 +294,94 @@ func expandServices(s []types.ServiceInterface) []*types.Service {
return services return services
} }
func returnSafeJson(w http.ResponseWriter, r *http.Request, input interface{}) { func toSafeJson(input interface{}) map[string]interface{} {
allData := make([]map[string]*json.RawMessage, 0, 1) thisData := make(map[string]interface{})
s := reflect.ValueOf(input) t := reflect.TypeOf(input)
for i := 0; i < s.Len(); i++ { elem := reflect.ValueOf(input)
obj := s.Index(i)
allData = append(allData, safeJsonKeys(obj)) d, _ := json.Marshal(input)
var raw map[string]*json.RawMessage
json.Unmarshal(d, &raw)
if t.Kind() == reflect.Ptr {
input = &input
} }
returnJson(allData, w, r)
fmt.Println("Type:", t.Name())
fmt.Println("Kind:", t.Kind())
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 == "-" {
continue
}
trueValue := elem.Field(i).Interface()
trueValue = fixValue(field, trueValue)
if tag == "" {
thisData[jsonTag] = trueValue
continue
}
if isPublic(tags) {
thisData[jsonTag] = trueValue
}
fmt.Printf("%d. %v (%v), tags: '%v'\n", i, field.Name, field.Type.Name(), tags)
}
return thisData
} }
func safeJsonKeys(in reflect.Value) map[string]*json.RawMessage { func returnSafeJson(w http.ResponseWriter, r *http.Request, input interface{}) {
if reflect.ValueOf(input).Kind() == reflect.Slice {
thisObj := in.Elem().Interface() alldata := make([]map[string]interface{}, 0, 1)
nn := make(map[string]*json.RawMessage) s := reflect.ValueOf(input)
for i := 0; i < s.Len(); i++ {
v := reflect.ValueOf(thisObj) alldata = append(alldata, toSafeJson(s.Index(i).Interface()))
typeOfS := v.Type()
fmt.Println("fields: ", v.NumField())
for i := 0; i < v.NumField(); i++ {
inter := v.Field(i).Interface()
fmt.Println(v.Field(i).CanSet())
if typeOfS.Field(i).Type.String() == "types.NullString" {
ggg := inter.(types.NullString)
fmt.Println("OKKOKOK: ", ggg)
v.Field(i).SetString(ggg.String)
} }
fmt.Printf("Field: %s\tValue: %v\n", typeOfS.Field(i).Type.String(), v.Field(i).Interface()) returnJson(alldata, w, r)
return
} }
returnJson(input, w, r)
return
}
data, _ := json.Marshal(thisObj) func fixValue(field reflect.StructField, val interface{}) interface{} {
typeName := field.Type.Name()
json.Unmarshal(data, &nn) switch typeName {
removeKeys := safeTypes(thisObj) case "NullString":
for _, k := range removeKeys { nullItem := val.(types.NullString)
delete(nn, k) return nullItem.String
case "NullBool":
nullItem := val.(types.NullBool)
return nullItem.Bool
case "NullFloat64":
nullItem := val.(types.NullFloat64)
return nullItem.Float64
case "NullInt64":
nullItem := val.(types.NullInt64)
return nullItem.Int64
default:
return val
} }
return nn }
func isPublic(tags []string) bool {
for _, v := range tags {
if v == "public" {
return true
}
}
return false
} }
// error404Handler is a HTTP handler for 404 error pages // error404Handler is a HTTP handler for 404 error pages

View File

@ -15,6 +15,12 @@ var (
authPass string authPass string
) )
func scopedRoute(handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler(w, r)
})
}
// basicAuthHandler is a middleware to implement HTTP basic authentication using // basicAuthHandler is a middleware to implement HTTP basic authentication using
// AUTH_USERNAME and AUTH_PASSWORD environment variables // AUTH_USERNAME and AUTH_PASSWORD environment variables
func basicAuthHandler(next http.Handler) http.Handler { func basicAuthHandler(next http.Handler) http.Handler {

View File

@ -114,7 +114,7 @@ func Router() *mux.Router {
r.Handle("/group/{id}", http.HandlerFunc(groupViewHandler)).Methods("GET") r.Handle("/group/{id}", http.HandlerFunc(groupViewHandler)).Methods("GET")
// API Routes // API Routes
r.Handle("/api", authenticated(apiIndexHandler, false)) r.Handle("/api", scopedRoute(apiIndexHandler))
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))
@ -123,7 +123,7 @@ func Router() *mux.Router {
r.Handle("/api/integrations/{name}", authenticated(apiIntegrationHandler, false)).Methods("POST") r.Handle("/api/integrations/{name}", authenticated(apiIntegrationHandler, false)).Methods("POST")
// API GROUPS Routes // API GROUPS Routes
r.Handle("/api/groups", readOnly(apiAllGroupHandler, false)).Methods("GET") r.Handle("/api/groups", scopedRoute(apiAllGroupHandler)).Methods("GET")
r.Handle("/api/groups", authenticated(apiCreateGroupHandler, false)).Methods("POST") r.Handle("/api/groups", authenticated(apiCreateGroupHandler, false)).Methods("POST")
r.Handle("/api/groups/{id}", readOnly(apiGroupHandler, false)).Methods("GET") r.Handle("/api/groups/{id}", readOnly(apiGroupHandler, false)).Methods("GET")
r.Handle("/api/groups/{id}", authenticated(apiGroupUpdateHandler, false)).Methods("POST") r.Handle("/api/groups/{id}", authenticated(apiGroupUpdateHandler, false)).Methods("POST")
@ -131,9 +131,9 @@ func Router() *mux.Router {
r.Handle("/api/reorder/groups", authenticated(apiGroupReorderHandler, false)).Methods("POST") r.Handle("/api/reorder/groups", authenticated(apiGroupReorderHandler, false)).Methods("POST")
// API SERVICE Routes // API SERVICE Routes
r.Handle("/api/services", http.HandlerFunc(apiAllServicesHandler)).Methods("GET") r.Handle("/api/services", scopedRoute(apiAllServicesHandler)).Methods("GET")
r.Handle("/api/services", authenticated(apiCreateServiceHandler, false)).Methods("POST") r.Handle("/api/services", authenticated(apiCreateServiceHandler, false)).Methods("POST")
r.Handle("/api/services/{id}", readOnly(apiServiceHandler, false)).Methods("GET") r.Handle("/api/services/{id}", scopedRoute(apiServiceHandler)).Methods("GET")
r.Handle("/api/reorder/services", authenticated(reorderServiceHandler, false)).Methods("POST") r.Handle("/api/reorder/services", authenticated(reorderServiceHandler, false)).Methods("POST")
r.Handle("/api/services/{id}/running", authenticated(apiServiceRunningHandler, false)).Methods("POST") r.Handle("/api/services/{id}/running", authenticated(apiServiceRunningHandler, false)).Methods("POST")
r.Handle("/api/services/{id}/data", cached("30s", "application/json", apiServiceDataHandler)).Methods("GET") r.Handle("/api/services/{id}/data", cached("30s", "application/json", apiServiceDataHandler)).Methods("GET")

View File

@ -294,15 +294,23 @@ func apiServiceDeleteHandler(w http.ResponseWriter, r *http.Request) {
} }
func apiAllServicesHandler(w http.ResponseWriter, r *http.Request) { func apiAllServicesHandler(w http.ResponseWriter, r *http.Request) {
admin := IsAdmin(r) isAdmin := IsAdmin(r)
services := core.Services() services := core.Services()
if !admin { if !isAdmin {
returnSafeJson(w, r, expandServices(services)) returnSafeJson(w, r, joinServices(services))
return return
} }
returnJson(services, w, r) returnJson(services, w, r)
} }
func joinServices(srvs []types.ServiceInterface) []types.Service {
var services []types.Service
for _, v := range srvs {
services = append(services, *v.Select())
}
return services
}
func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) { func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
service := core.SelectService(utils.ToInt(vars["id"])) service := core.SelectService(utils.ToInt(vars["id"]))

View File

@ -23,21 +23,21 @@ import (
type Service struct { type Service struct {
Id int64 `gorm:"primary_key;column:id" json:"id"` Id int64 `gorm:"primary_key;column:id" json:"id"`
Name string `gorm:"column:name" json:"name"` Name string `gorm:"column:name" json:"name"`
Domain string `gorm:"column:domain" json:"domain" private:"true"` Domain string `gorm:"column:domain" json:"domain" private:"true" scope:"user,admin"`
Expected NullString `gorm:"column:expected" json:"expected"` Expected NullString `gorm:"column:expected" json:"expected" scope:"user,admin"`
ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status"` ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status" scope:"user,admin"`
Interval int `gorm:"default:30;column:check_interval" json:"check_interval"` Interval int `gorm:"default:30;column:check_interval" json:"check_interval" scope:"user,admin"`
Type string `gorm:"column:check_type" json:"type"` Type string `gorm:"column:check_type" json:"type" scope:"user,admin"`
Method string `gorm:"column:method" json:"method"` Method string `gorm:"column:method" json:"method" scope:"user,admin"`
PostData NullString `gorm:"column:post_data" json:"post_data"` PostData NullString `gorm:"column:post_data" json:"post_data" scope:"user,admin"`
Port int `gorm:"not null;column:port" json:"port"` Port int `gorm:"not null;column:port" json:"port" scope:"user,admin"`
Timeout int `gorm:"default:30;column:timeout" json:"timeout"` Timeout int `gorm:"default:30;column:timeout" json:"timeout" scope:"user,admin"`
Order int `gorm:"default:0;column:order_id" json:"order_id"` Order int `gorm:"default:0;column:order_id" json:"order_id"`
AllowNotifications NullBool `gorm:"default:true;column:allow_notifications" json:"allow_notifications"` AllowNotifications NullBool `gorm:"default:true;column:allow_notifications" json:"allow_notifications" scope:"user,admin"`
VerifySSL NullBool `gorm:"default:false;column:verify_ssl" json:"verify_ssl"` VerifySSL NullBool `gorm:"default:false;column:verify_ssl" json:"verify_ssl" scope:"user,admin"`
Public NullBool `gorm:"default:true;column:public" json:"public"` Public NullBool `gorm:"default:true;column:public" json:"public"`
GroupId int `gorm:"default:0;column:group_id" json:"group_id"` GroupId int `gorm:"default:0;column:group_id" json:"group_id"`
Headers NullString `gorm:"column:headers" json:"headers"` Headers NullString `gorm:"column:headers" json:"headers" scope:"user,admin"`
Permalink NullString `gorm:"column:permalink" json:"permalink"` Permalink NullString `gorm:"column:permalink" json:"permalink"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
@ -56,8 +56,8 @@ type Service struct {
SuccessNotified bool `gorm:"-" json:"-"` // Is 'true' if the user has already be informed that the Services now again available SuccessNotified bool `gorm:"-" json:"-"` // Is 'true' if the user has already be informed that the Services now again available
LastStatusCode int `gorm:"-" json:"status_code"` LastStatusCode int `gorm:"-" json:"status_code"`
LastOnline time.Time `gorm:"-" json:"last_success"` LastOnline time.Time `gorm:"-" json:"last_success"`
Failures []FailureInterface `gorm:"-" json:"failures,omitempty"` Failures []FailureInterface `gorm:"-" json:"failures,omitempty" scope:"user,admin"`
Checkins []CheckinInterface `gorm:"-" json:"checkins,omitempty"` Checkins []CheckinInterface `gorm:"-" json:"checkins,omitempty" scope:"user,admin"`
} }
// BeforeCreate for Service will set CreatedAt to UTC // BeforeCreate for Service will set CreatedAt to UTC