pull/10/head
Hunter Long 2018-06-13 18:19:00 -07:00
parent afd6ec52f1
commit 077d1a5900
10 changed files with 329 additions and 122 deletions

View File

@ -54,8 +54,10 @@ func (s *Service) Check() {
func (s *Service) Record(response *http.Response) {
db.QueryRow("INSERT INTO hits(service,latency,created_at) VALUES($1,$2,NOW()) returning id;", s.Id, s.Latency).Scan()
OnHit(s)
}
func (s *Service) Failure(issue string) {
db.QueryRow("INSERT INTO failures(issue,service,created_at) VALUES($1,$2,NOW()) returning id;", issue, s.Id).Scan()
OnFailure(s)
}

61
events.go Normal file
View File

@ -0,0 +1,61 @@
package main
import "github.com/hunterlong/statup/plugin"
func OnHit(s *Service) {
for _, p := range allPlugins {
p.OnHit(s.ToP())
}
}
func OnFailure(s *Service) {
for _, p := range allPlugins {
p.OnFailure(s.ToP())
}
}
func SelectPlugin(name string) plugin.PluginActions {
for _, p := range allPlugins {
if p.GetInfo().Name == name {
return p
}
}
return plugin.PluginInfo{}
}
func (s *Service) PluginFailures() []*plugin.Failure {
var failed []*plugin.Failure
for _, f := range s.Failures {
fail := &plugin.Failure{
f.Id,
f.Issue,
f.Service,
f.CreatedAt,
f.Ago,
}
failed = append(failed, fail)
}
return failed
}
func (s *Service) ToP() *plugin.Service {
out := &plugin.Service{
s.Id,
s.Name,
s.Domain,
s.Expected,
s.ExpectedStatus,
s.Interval,
s.Method,
s.Port,
s.CreatedAt,
s.Data,
s.Online,
s.Latency,
s.Online24Hours,
s.AvgResponse,
s.TotalUptime,
s.PluginFailures(),
}
return out
}

View File

@ -15,9 +15,6 @@
<li class="nav-item">
<a class="nav-link" href="/users">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/plugins">Plugins</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/settings">Settings</a>
</li>

View File

@ -49,9 +49,25 @@
{{ range .Plugins }}
<div class="tab-pane fade" id="v-pills-{{.Name}}" role="tabpanel" aria-labelledby="v-pills-{{.Name}}-tab">
<form action="/plugins/{{.Name}}/save" method="POST">
{{safe .Form }}
</form>
<h4 class="text-capitalize">{{ .Name }}</h4>
<span class="text-muted">{{ .Description }}</span>
<form class="mt-3" method="POST" action="/plugins/{{.Name}}/save">
{{ range .Form }}
<div class="form-group">
<label for="input_{{ .SQLValue }}" class="text-capitalize">{{ .Name }}</label>
<input type="text" class="form-control" name="{{ .SQLValue }}" value="{{ .Val }}" id="input_{{ .SQLValue }}" placeholder="Example input" aria-describedby="help_{{ .SQLValue }}">
{{ if .Description }}
<small id="help_{{ .SQLValue }}" class="form-text text-muted">
{{ .Description }}
</small>
{{ end }}
</div>
{{ end }}
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
{{end}}

78
main.go
View File

@ -28,7 +28,7 @@ var (
jsBox *rice.Box
tmplBox *rice.Box
setupMode bool
allPlugins []plg.Plugin
allPlugins []plugin.PluginActions
)
const (
@ -70,45 +70,36 @@ func (c *Core) FetchPluginRepo() []PluginJSON {
return pk
}
func SelectPlugin(name string) *PluginJSON {
for _, v := range core.FetchPluginRepo() {
if v.Namespace == name {
return &v
}
}
return nil
}
func DownloadPlugin(name string) {
plugin := SelectPlugin(name)
var _, err = os.Stat("plugins/" + plugin.Namespace)
if err != nil {
}
if os.IsNotExist(err) {
var file, _ = os.Create("plugins/" + plugin.Namespace)
defer file.Close()
}
resp, err := http.Get("https://raw.githubusercontent.com/hunterlong/statup/master/plugins.json")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
file, err := os.OpenFile("plugins/"+plugin.Namespace, os.O_RDWR, 0644)
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Write(body)
if err != nil {
panic(err)
}
err = file.Sync()
}
//func DownloadPlugin(name string) {
// plugin := SelectPlugin(name)
// var _, err = os.Stat("plugins/" + plugin.Namespace)
// if err != nil {
// }
// if os.IsNotExist(err) {
// var file, _ = os.Create("plugins/" + plugin.Namespace)
// defer file.Close()
// }
// resp, err := http.Get("https://raw.githubusercontent.com/hunterlong/statup/master/plugins.json")
// if err != nil {
// panic(err)
// }
// defer resp.Body.Close()
// body, err := ioutil.ReadAll(resp.Body)
// if err != nil {
// panic(err)
// }
// file, err := os.OpenFile("plugins/"+plugin.Namespace, os.O_RDWR, 0644)
// if err != nil {
// panic(err)
// }
// defer file.Close()
//
// _, err = file.Write(body)
// if err != nil {
// panic(err)
// }
// err = file.Sync()
//}
func main() {
VERSION = "1.1.1"
@ -177,14 +168,13 @@ func LoadPlugins() {
fmt.Printf("Plugin '%v' could not load correctly, error: %v\n", f.Name(), "unexpected type from module symbol")
continue
}
//plugin := plugActions.Plugin()
//
//fmt.Println(plugin.OnLoad)
plugActions.OnLoad()
allPlugins = append(allPlugins, *plug)
allPlugins = append(allPlugins, plugActions)
core.Plugins = append(core.Plugins, plugActions.GetInfo())
}
fmt.Printf("Loaded %v Plugins\n", len(allPlugins))
ForEachPlugin()

75
plugin/interface.go Normal file
View File

@ -0,0 +1,75 @@
package plugin
import (
"net/http"
"time"
)
type PluginActions interface {
GetInfo() Info
Routes() []Routing
OnSave(map[string]string)
OnInstall()
OnUninstall()
OnFailure(*Service)
OnHit(*Service)
OnSettingsSaved()
OnNewUser()
OnNewService()
OnShutdown()
OnLoad()
OnBeforeRequest()
OnAfterRequest()
}
type Service struct {
Id int64
Name string
Domain string
Expected string
ExpectedStatus int
Interval int
Method string
Port int
CreatedAt time.Time
Data string
Online bool
Latency float64
Online24Hours float32
AvgResponse string
TotalUptime string
Failures []*Failure
}
type Failure struct {
Id int
Issue string
Service int
CreatedAt time.Time
Ago string
}
type PluginInfo struct {
Info Info
PluginActions
}
type Routing struct {
URL string
Method string
Handler func(http.ResponseWriter, *http.Request)
}
type Info struct {
Name string
Description string
Form []FormElement
}
type FormElement struct {
Name string
Description string
SQLValue string
SQLType string
Value interface{}
}

View File

@ -2,37 +2,126 @@ package plugin
import (
"database/sql"
"fmt"
"html/template"
"io"
"net/http"
"os"
"strings"
)
var (
DB *sql.DB
db *sql.DB
AllPlugins []Info
)
type PluginInfo struct {
Info Info
PluginActions
Add
func CreateSettingsTable(p Info) string {
var tableValues []string
for _, v := range p.Form {
tb := fmt.Sprintf("%v %v", v.SQLValue, v.SQLType)
tableValues = append(tableValues, tb)
}
vals := strings.Join(tableValues, ", ")
out := fmt.Sprintf("CREATE TABLE settings_%v (%v);", p.Name, vals)
smtp, _ := db.Prepare(out)
_, _ = smtp.Exec()
InitalSettings(p)
return out
}
type Routing struct {
URL string
Method string
Handler func(http.ResponseWriter, *http.Request)
func InitalSettings(p Info) {
var tableValues []string
var tableInput []string
for _, v := range p.Form {
val := fmt.Sprintf("'%v'", "example data")
tableValues = append(tableValues, v.SQLValue)
tableInput = append(tableInput, val)
}
vals := strings.Join(tableValues, ",")
ins := strings.Join(tableInput, ",")
sql := fmt.Sprintf("INSERT INTO settings_%v(%v) VALUES(%v);", p.Name, vals, ins)
smtp, _ := db.Prepare(sql)
_, _ = smtp.Exec()
SelectSettings(p)
}
type Info struct {
Name string
Form string
func (f FormElement) Val() string {
var v string
fmt.Println(f.Value)
b, ok := f.Value.([]byte)
if ok {
v = string(b)
}
return v
}
func SelectSettings(p Info) []*FormElement {
var newForm []*FormElement
sql := fmt.Sprintf("SELECT * FROM settings_%v LIMIT 1", p.Name)
rows, err := db.Query(sql)
if err != nil {
panic(err)
}
count := len(p.Form)
valuePtrs := make([]interface{}, count)
values := make([]interface{}, count)
for rows.Next() {
for i, _ := range p.Form {
valuePtrs[i] = &values[i]
}
err = rows.Scan(valuePtrs...)
if err != nil {
panic(err)
}
for i, col := range p.Form {
var v interface{}
val := values[i]
b, ok := val.([]byte)
if ok {
v = string(b)
} else {
v = val
}
ll := &FormElement{
Name: col.Name,
Description: col.Description,
SQLValue: col.SQLValue,
SQLType: col.SQLType,
Value: v,
}
newForm = append(newForm, ll)
fmt.Println(col.SQLValue, v)
col.Value = v
}
}
return newForm
}
func RunSQL(query string, args ...interface{}) (*sql.Rows, error) {
rows, err := db.Query(query, args)
return rows, err
}
func (i Info) Template() *template.Template {
t := template.New("form")
temp, _ := t.Parse(i.Form)
temp, _ := t.Parse("hello nworld")
return temp
}
@ -57,27 +146,8 @@ func DownloadFile(filepath string, url string) error {
return nil
}
type Add func(p PluginInfo)
type PluginActions interface {
GetInfo() Info
Routes() []Routing
SaveForm()
OnInstall()
OnUninstall()
OnFailure()
OnHit()
OnSettingsSaved()
OnNewUser()
OnNewService()
OnShutdown()
OnLoad()
OnBeforeRequest()
OnAfterRequest()
}
func SetDatabase(db *sql.DB) {
DB = db
func SetDatabase(database *sql.DB) {
db = database
}
func (p *PluginInfo) InstallPlugin(w http.ResponseWriter, r *http.Request) {

BIN
plugins/example.so Normal file

Binary file not shown.

BIN
plugins/stack.so Normal file

Binary file not shown.

72
web.go
View File

@ -4,10 +4,10 @@ import (
"fmt"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"github.com/hunterlong/statup/plugin"
"html/template"
"net/http"
"strconv"
"strings"
"time"
)
@ -29,46 +29,28 @@ func RunHTTPServer() {
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler))
r.Handle("/login", http.HandlerFunc(LoginHandler))
r.Handle("/logout", http.HandlerFunc(LogoutHandler))
r.Handle("/services", http.HandlerFunc(ServicesHandler))
r.Handle("/services", http.HandlerFunc(CreateServiceHandler)).Methods("POST")
r.Handle("/service/{id}", http.HandlerFunc(ServicesViewHandler))
r.Handle("/service/{id}", http.HandlerFunc(ServicesUpdateHandler)).Methods("POST")
r.Handle("/service/{id}/edit", http.HandlerFunc(ServicesViewHandler))
r.Handle("/service/{id}/delete", http.HandlerFunc(ServicesDeleteHandler))
r.Handle("/users", http.HandlerFunc(UsersHandler))
r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST")
r.Handle("/settings", http.HandlerFunc(SettingsHandler))
r.Handle("/plugins", http.HandlerFunc(PluginsHandler))
r.Handle("/settings", http.HandlerFunc(PluginsHandler))
r.Handle("/plugins/download/{name}", http.HandlerFunc(PluginsDownloadHandler))
r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST")
r.Handle("/help", http.HandlerFunc(HelpHandler))
for _, p := range allPlugins {
symPlugin, _ := p.Lookup("Plugin")
var pluginObject plugin.PluginActions
pluginObject, ok := symPlugin.(plugin.PluginActions)
info := pluginObject.GetInfo()
if !ok {
fmt.Printf("Plugin '%v' could not load correctly, error: %v\n", info.Name, "unexpected type from module symbol")
continue
}
plugin.AllPlugins = append(plugin.AllPlugins, info)
for _, route := range pluginObject.Routes() {
info := p.GetInfo()
for _, route := range p.Routes() {
path := fmt.Sprintf("/plugins/%v/%v", info.Name, route.URL)
r.Handle(path, http.HandlerFunc(route.Handler)).Methods(route.Method)
fmt.Printf("Added Route %v for plugin %v\n", path, info.Name)
}
}
core.Plugins = plugin.AllPlugins
srv := &http.Server{
Addr: "0.0.0.0:8080",
WriteTimeout: time.Second * 15,
@ -235,16 +217,6 @@ func IsAuthenticated(r *http.Request) bool {
return session.Values["authenticated"].(bool)
}
func SettingsHandler(w http.ResponseWriter, r *http.Request) {
auth := IsAuthenticated(r)
if !auth {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
tmpl := Parse("settings.html")
tmpl.Execute(w, core)
}
func PluginsHandler(w http.ResponseWriter, r *http.Request) {
auth := IsAuthenticated(r)
if !auth {
@ -254,8 +226,32 @@ func PluginsHandler(w http.ResponseWriter, r *http.Request) {
tmpl := ParsePlugins("plugins.html")
core.FetchPluginRepo()
fmt.Println(core.FetchPluginRepo())
tmpl.Execute(w, &core)
for _, p := range allPlugins {
for _, f := range p.GetInfo().Form {
f.Val()
}
}
tmpl.Execute(w, core)
}
func PluginSavedHandler(w http.ResponseWriter, r *http.Request) {
auth := IsAuthenticated(r)
if !auth {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
r.ParseForm()
vars := mux.Vars(r)
plugin := SelectPlugin(vars["name"])
data := make(map[string]string)
for k, v := range r.PostForm {
data[k] = strings.Join(v, "")
}
plugin.OnSave(data)
http.Redirect(w, r, "/settings", http.StatusSeeOther)
}
func PluginsDownloadHandler(w http.ResponseWriter, r *http.Request) {
@ -264,9 +260,9 @@ func PluginsDownloadHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
vars := mux.Vars(r)
name := vars["name"]
DownloadPlugin(name)
//vars := mux.Vars(r)
//name := vars["name"]
//DownloadPlugin(name)
LoadConfig()
http.Redirect(w, r, "/plugins", http.StatusSeeOther)
}