mirror of https://github.com/statping/statping
upgrades - plugins
parent
6bbf636bed
commit
8a6f6a2cf8
|
@ -17,7 +17,7 @@ services:
|
|||
- mongodb
|
||||
|
||||
env:
|
||||
- VERSION=0.151
|
||||
- VERSION=0.16
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
|
@ -37,7 +37,6 @@ deploy:
|
|||
- "build/statup-linux-x64"
|
||||
- "build/statup-linux-x32"
|
||||
- "build/statup-windows-x64.exe"
|
||||
- "build/statup-linux-static"
|
||||
skip_cleanup: true
|
||||
|
||||
notifications:
|
||||
|
|
10
build.sh
10
build.sh
|
@ -6,13 +6,13 @@ APP="statup"
|
|||
|
||||
rice embed-go
|
||||
|
||||
xgo --targets=darwin/amd64 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
xgo --targets=darwin/386 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
xgo -go 1.10.x --targets=darwin/amd64 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
xgo -go 1.10.x --targets=darwin/386 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
|
||||
xgo --targets=linux/amd64 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
xgo --targets=linux/386 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
xgo -go 1.10.x --targets=linux/amd64 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
xgo -go 1.10.x --targets=linux/386 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
|
||||
xgo --targets=windows/amd64 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
xgo -go 1.10.x --targets=windows/amd64 --dest=build -ldflags="-X main.VERSION=$VERSION" ./
|
||||
|
||||
cd build
|
||||
ls
|
||||
|
|
2
core.go
2
core.go
|
@ -11,6 +11,8 @@ type Core struct {
|
|||
Config string `db:"config"`
|
||||
ApiKey string `db:"api_key"`
|
||||
ApiSecret string `db:"api_secret"`
|
||||
Style string `db:"style"`
|
||||
Footer string `db:"footer"`
|
||||
Version string `db:"version"`
|
||||
Plugins []plugin.Info
|
||||
Repos []PluginJSON
|
||||
|
|
41
database.go
41
database.go
|
@ -2,8 +2,8 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/plugin"
|
||||
"strings"
|
||||
"time"
|
||||
"upper.io/db.v3/lib/sqlbuilder"
|
||||
"upper.io/db.v3/mysql"
|
||||
"upper.io/db.v3/postgresql"
|
||||
|
@ -29,6 +29,9 @@ func DbConnection(dbType string) error {
|
|||
return err
|
||||
}
|
||||
} else if dbType == "mysql" {
|
||||
if configs.Port == "" {
|
||||
configs.Port = "3306"
|
||||
}
|
||||
mysqlSettings = mysql.ConnectionURL{
|
||||
Database: configs.Database,
|
||||
Host: configs.Host,
|
||||
|
@ -40,9 +43,13 @@ func DbConnection(dbType string) error {
|
|||
return err
|
||||
}
|
||||
} else {
|
||||
if configs.Port == "" {
|
||||
configs.Port = "5432"
|
||||
}
|
||||
host := fmt.Sprintf("%v:%v", configs.Host, configs.Port)
|
||||
postgresSettings = postgresql.ConnectionURL{
|
||||
Database: configs.Database,
|
||||
Host: configs.Host,
|
||||
Host: host,
|
||||
User: configs.User,
|
||||
Password: configs.Password,
|
||||
}
|
||||
|
@ -53,28 +60,10 @@ func DbConnection(dbType string) error {
|
|||
}
|
||||
//dbSession.SetLogging(true)
|
||||
dbServer = dbType
|
||||
plugin.SetDatabase(dbSession)
|
||||
OnLoad(dbSession)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpgradeDatabase() {
|
||||
fmt.Println("New Version: ", core.Version)
|
||||
fmt.Println("Current Version: ", VERSION)
|
||||
if VERSION == core.Version {
|
||||
fmt.Println("Database already up to date")
|
||||
return
|
||||
}
|
||||
fmt.Println("Upgrading Database...")
|
||||
upgrade, _ := sqlBox.String("upgrade.sql")
|
||||
requests := strings.Split(upgrade, ";")
|
||||
for _, request := range requests {
|
||||
_, err := db.Exec(request)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DropDatabase() {
|
||||
fmt.Println("Dropping Tables...")
|
||||
down, _ := sqlBox.String("down.sql")
|
||||
|
@ -124,22 +113,17 @@ func LoadSampleData() error {
|
|||
Type: "https",
|
||||
Method: "GET",
|
||||
}
|
||||
admin := &User{
|
||||
Username: "admin",
|
||||
Password: "admin",
|
||||
Email: "admin@admin.com",
|
||||
}
|
||||
s1.Create()
|
||||
s2.Create()
|
||||
s3.Create()
|
||||
s4.Create()
|
||||
admin.Create()
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
for i := 0; i < 100; i++ {
|
||||
s1.Check()
|
||||
s2.Check()
|
||||
s3.Check()
|
||||
s4.Check()
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -147,7 +131,6 @@ func LoadSampleData() error {
|
|||
|
||||
func CreateDatabase() {
|
||||
fmt.Println("Creating Tables...")
|
||||
VERSION = "1.1.1"
|
||||
sql := "postgres_up.sql"
|
||||
if dbServer == "mysql" {
|
||||
sql = "mysql_up.sql"
|
||||
|
|
83
events.go
83
events.go
|
@ -1,16 +1,56 @@
|
|||
package main
|
||||
|
||||
import "github.com/hunterlong/statup/plugin"
|
||||
import (
|
||||
"github.com/fatih/structs"
|
||||
"github.com/hunterlong/statup/plugin"
|
||||
"upper.io/db.v3/lib/sqlbuilder"
|
||||
)
|
||||
|
||||
func OnLoad(db sqlbuilder.Database) {
|
||||
for _, p := range allPlugins {
|
||||
p.OnLoad(db)
|
||||
}
|
||||
}
|
||||
|
||||
func OnSuccess(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
p.OnSuccess(s.ToP())
|
||||
p.OnSuccess(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
func OnFailure(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
p.OnFailure(s.ToP())
|
||||
p.OnFailure(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
func OnSettingsSaved(c *Core) {
|
||||
for _, p := range allPlugins {
|
||||
p.OnSettingsSaved(structs.Map(c))
|
||||
}
|
||||
}
|
||||
|
||||
func OnNewUser(u *User) {
|
||||
for _, p := range allPlugins {
|
||||
p.OnNewUser(structs.Map(u))
|
||||
}
|
||||
}
|
||||
|
||||
func OnNewService(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
p.OnNewService(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
func OnDeletedService(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
p.OnDeletedService(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
func OnUpdateService(s *Service) {
|
||||
for _, p := range allPlugins {
|
||||
p.OnUpdatedService(structs.Map(s))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,40 +62,3 @@ func SelectPlugin(name string) plugin.PluginActions {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
15
failures.go
15
failures.go
|
@ -29,16 +29,21 @@ func (s *Service) CreateFailure(data FailureData) (int64, error) {
|
|||
|
||||
func (s *Service) SelectAllFailures() ([]*Failure, error) {
|
||||
var fails []*Failure
|
||||
col := dbSession.Collection("failures").Find("session", s.Id)
|
||||
col := dbSession.Collection("failures").Find("service", s.Id)
|
||||
err := col.All(&fails)
|
||||
return fails, err
|
||||
}
|
||||
|
||||
func (s *Service) LimitedFailures() ([]*Failure, error) {
|
||||
func (s *Service) LimitedFailures() []*Failure {
|
||||
var fails []*Failure
|
||||
col := dbSession.Collection("failures").Find("session", s.Id).Limit(10)
|
||||
err := col.All(&fails)
|
||||
return fails, err
|
||||
col := dbSession.Collection("failures").Find("service", s.Id).Limit(10)
|
||||
col.All(&fails)
|
||||
return fails
|
||||
}
|
||||
|
||||
func (f *Failure) Delete() error {
|
||||
col := dbSession.Collection("failures").Find("id", f.Id)
|
||||
return col.Delete()
|
||||
}
|
||||
|
||||
func CountFailures() (uint64, error) {
|
||||
|
|
|
@ -7,25 +7,54 @@
|
|||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/css/base.css">
|
||||
|
||||
<title>Statup | Dashboard</title>
|
||||
<title>Statup | Help</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
{{template "nav"}}
|
||||
{{if Auth}}
|
||||
{{template "nav"}}
|
||||
{{end}}
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
<h3>Help</h3>
|
||||
<h2>Statup v{{ VERSION }} Help</h2>
|
||||
Statup is an easy to use Status Page monitor for your websites and applications. Statup is developed in Go Language and you are able to create custom plugins with it!
|
||||
|
||||
<p>
|
||||
<a href="https://github.com/hunterlong/statup"><img src="https://img.shields.io/github/stars/hunterlong/statup.svg?style=social&label=Stars"></a>
|
||||
<a href="https://github.com/hunterlong/statup"><img src="https://img.shields.io/docker/build/hunterlong/statup.svg"></a>
|
||||
<a href="https://github.com/hunterlong/statup"><img src="https://img.shields.io/github/release/hunterlong/statup.svg"></a>
|
||||
</p>
|
||||
|
||||
<h2 class="mt-3">Services</h2>
|
||||
For each website and application you want to add a new Service. Each Service will require a URL endpoint to test your applications status.
|
||||
You can also add expected HTTP responses (regex allow), expected HTTP response codes, and other fields to make sure your service is online or offline.
|
||||
|
||||
<h2 class="mt-3">Users</h2>
|
||||
Users can access the Statup Dashboard to add, remove, and view services.
|
||||
|
||||
<h2 class="mt-3">Plugins</h2>
|
||||
Creating a plugin for Statup is not that difficult, if you know a little bit of Go Language you can create any type of application to be embedded into the Status framework.
|
||||
Checkout the example plugin that includes all the interfaces, information, and custom HTTP routing at <a href="https://github.com/hunterlong/statup_plugin">https://github.com/hunterlong/statup_plugin</a>.
|
||||
Anytime there is an action on your status page, all of your plugins will be notified of the change with the values that were changed or created.
|
||||
<p></p>
|
||||
Using the statup/plugin Golang package you can quickly implement the event listeners. Statup uses <a href="https://github.com/upper/db">upper.io/db.v3</a> for the database connection.
|
||||
You can use the database inside of your plugin to create, update, and destroy tables/data. <b>Please only use respectable plugins!</b>
|
||||
|
||||
<h2 class="mt-3">Custom Stlying</h2>
|
||||
On Statup Status Page server can you create your own custom stylesheet to be rendered on the index view of your status page. Go to <a href="/settings">Settings</a> and click on Custom Styling.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -8,15 +8,20 @@
|
|||
<link rel="stylesheet" href="/css/base.css">
|
||||
<script src="/js/Chart.bundle.min.js"></script>
|
||||
|
||||
<title>{{.Project}} Status</title>
|
||||
<title>{{.Core.Name}} Status</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1 class="text-center mb-4 mt-sm-3">{{.Project}}</h1>
|
||||
|
||||
<h1 class="text-center mb-4 mt-sm-3">{{.Core.Name}}</h1>
|
||||
|
||||
|
||||
{{ if .Core.Description }}
|
||||
<h5 class="text-center mb-1 mt-sm-2">{{ .Core.Description }}</h5>
|
||||
{{ end }}
|
||||
|
||||
<div class="container">
|
||||
|
||||
|
||||
<div class="col-12 mb-5">
|
||||
|
||||
<div class="list-group online_list">
|
||||
|
@ -72,7 +77,7 @@
|
|||
|
||||
<canvas id="service_{{ .Id }}" width="400" height="120"></canvas>
|
||||
|
||||
{{ range .Failures }}
|
||||
{{ range .LimitedFailures }}
|
||||
<blockquote class="blockquote text-right mt-3">
|
||||
<p class="mb-0">{{.ParseError}}</p>
|
||||
<footer class="blockquote-footer">Reported <cite title="Source Title">{{.Ago}}</cite></footer>
|
||||
|
@ -89,7 +94,10 @@
|
|||
</div>
|
||||
|
||||
<div class="footer text-center">
|
||||
<a href="https://statup.io" target="_blank">Created with Statup.io</a> | <a href="/dashboard">Dashboard</a>
|
||||
{{ if .Core.Footer }}
|
||||
{{ safe .Core.Footer }}
|
||||
{{ end }}
|
||||
<a href="https://statup.io" target="_blank">Statup made with ❤️</a> | <a href="/dashboard">Dashboard</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
@ -151,6 +159,14 @@ var chartdata = new Chart(ctx, {
|
|||
});
|
||||
{{ end }}
|
||||
</script>
|
||||
|
||||
|
||||
{{ if .Core.Style }}
|
||||
<style>
|
||||
{{ safe .Core.Style }}
|
||||
</style>
|
||||
{{ end }}
|
||||
|
||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
|
||||
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
|
||||
<a class="nav-link active" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true">Settings</a>
|
||||
<a class="nav-link" id="v-pills-browse-tab" data-toggle="pill" href="#v-pills-browse" role="tab" aria-controls="v-pills-home" aria-selected="true">Browse Plugins</a>
|
||||
<a class="nav-link" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-style" role="tab" aria-controls="v-pills-style" aria-selected="false">Styling</a>
|
||||
<a class="nav-link" id="v-pills-browse-tab" data-toggle="pill" href="#v-pills-browse" role="tab" aria-controls="v-pills-home" aria-selected="false">Browse Plugins</a>
|
||||
{{ range .Plugins }}
|
||||
<a class="nav-link text-capitalize" id="v-pills-{{.Name}}-tab" data-toggle="pill" href="#v-pills-{{.Name}}" role="tab" aria-controls="v-pills-profile" aria-selected="false">{{.Name}}</a>
|
||||
{{end}}
|
||||
|
@ -46,12 +47,31 @@
|
|||
<input type="text" name="description" class="form-control" value="{{ .Description }}" id="formGroupExampleInput" placeholder="Great Uptime">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="formGroupExampleInput">Custom Footer</label>
|
||||
<textarea rows="4" name="footer" class="form-control" id="formGroupExampleInput">{{ .Footer }}</textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block">Save Settings</button>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="v-pills-style" role="tabpanel" aria-labelledby="v-pills-style-tab">
|
||||
<h3>Custom Style</h3>
|
||||
<form method="POST" action="/settings">
|
||||
|
||||
<div class="form-group">
|
||||
<textarea rows="15" name="style" class="form-control" id="formGroupExampleInput">{{ .Style }}</textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block">Save Settings</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="v-pills-browse" role="tabpanel" aria-labelledby="v-pills-browse-tab">
|
||||
{{ range .Repos }}
|
||||
<div class="card col-6" style="width: 18rem;">
|
||||
|
@ -98,5 +118,6 @@
|
|||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -12,10 +12,9 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
||||
{{if .Auth}}
|
||||
{{if Auth}}
|
||||
{{template "nav"}}
|
||||
{{end}}
|
||||
<div class="row">
|
||||
|
@ -24,8 +23,8 @@
|
|||
|
||||
<div class="col-12 mb-4">
|
||||
|
||||
<h3 class="mt-2">{{ .Service.Name }}
|
||||
{{if .Service.Online}}
|
||||
<h3 class="mt-2">{{ .Name }}
|
||||
{{if .Online }}
|
||||
<span class="badge online_badge float-right">ONLINE</span>
|
||||
{{ else }}
|
||||
<span class="badge offline_badge float-right">OFFLINE</span>
|
||||
|
@ -34,24 +33,24 @@
|
|||
<div class="row stats_area mt-5 mb-5">
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.Service.Online24}}%</span>
|
||||
<span class="lg_number">{{.Online24}}%</span>
|
||||
Online last 24 Hours
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.Service.AvgTime}}ms</span>
|
||||
<span class="lg_number">{{.AvgTime}}ms</span>
|
||||
Average Response
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.Service.AvgUptime}}%</span>
|
||||
<span class="lg_number">{{.AvgUptime}}%</span>
|
||||
Total Uptime
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<canvas id="service" width="400" height="120"></canvas>
|
||||
|
||||
{{ range .Service.Failures }}
|
||||
{{ range .Failures }}
|
||||
<blockquote class="blockquote text-right mt-3">
|
||||
<p class="mb-0">{{.ParseError}}</p>
|
||||
<footer class="blockquote-footer">Reported <cite title="Source Title">{{.Ago}}</cite></footer>
|
||||
|
@ -63,52 +62,53 @@
|
|||
</div>
|
||||
|
||||
|
||||
{{if .Auth}}
|
||||
{{if Auth}}
|
||||
<div class="col-12">
|
||||
|
||||
<h3>Edit Service</h3>
|
||||
|
||||
<form action="/service/{{.Service.Id}}" method="POST">
|
||||
<form action="/service/{{.Id}}" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="inputEmail3" class="col-sm-4 col-form-label">Service Name</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="name" class="form-control" value="{{.Service.Name}}" id="inputEmail3">
|
||||
<input type="text" name="name" class="form-control" value="{{.Name}}" id="inputEmail3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Application Endpoint (URL)</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="domain" class="form-control" value="{{.Service.Domain}}" id="inputPassword3">
|
||||
<input type="text" name="domain" class="form-control" value="{{.Domain}}" id="inputPassword3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Expected Response (Regex)</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="expected" class="form-control" value="{{.Service.Expected}}" id="inputPassword3">
|
||||
<input type="text" name="expected" class="form-control" value="{{.Expected}}" id="inputPassword3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Expected Status Code</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="expected_status" class="form-control" value="{{.Service.ExpectedStatus}}" id="inputPassword3">
|
||||
<input type="number" name="expected_status" class="form-control" value="{{.ExpectedStatus}}" id="inputPassword3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">HTTP Method</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="method" class="form-control" value="{{.Service.Method}}" id="inputPassword3">
|
||||
<input type="text" name="method" class="form-control" value="{{.Method}}" id="inputPassword3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Check Interval (Seconds)</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="interval" class="form-control" value="{{.Service.Interval}}" id="inputPassword3">
|
||||
<input type="number" name="interval" class="form-control" value="{{.Interval}}" id="inputPassword3">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-10">
|
||||
<a class="btn btn-primary">Test</a>
|
||||
<button type="submit" class="btn btn-success">Update Service</button>
|
||||
<a href="/service/{{ .Id }}/delete_failures" class="btn btn-danger">Delete All Failures</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -117,6 +117,13 @@
|
|||
{{end}}
|
||||
|
||||
|
||||
{{ range .Failures }}
|
||||
<blockquote class="blockquote text-right mt-3">
|
||||
<p class="mb-0">{{.ParseError}}</p>
|
||||
<footer class="blockquote-footer">Reported <cite title="Source Title">{{.Ago}}</cite></footer>
|
||||
</blockquote>
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -129,7 +136,7 @@
|
|||
data: {
|
||||
datasets: [{
|
||||
label: 'Response Time (Milliseconds)',
|
||||
data: {{js .Service.GraphData}},
|
||||
data: {{js .GraphData}},
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
<td>{{.Name}}</td>
|
||||
<td>{{if .Online}}<span class="badge badge-success">ONLINE</span>{{else}}<span class="badge badge-danger">OFFLINE</span>{{end}} </td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group" data-toggle="buttons">
|
||||
<div class="btn-group">
|
||||
<a href="/service/{{.Id}}" class="btn btn-primary">View</a>
|
||||
<a href="/service/{{.Id}}/delete" class="btn btn-danger">Delete</a>
|
||||
</div>
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
|
||||
<div class="container">
|
||||
|
||||
{{ if .Error }}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{ .Error }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<form method="POST" action="/setup">
|
||||
|
||||
<div class="row">
|
||||
|
@ -30,23 +36,23 @@
|
|||
</div>
|
||||
<div class="form-group" id="db_host">
|
||||
<label for="formGroupExampleInput">Host</label>
|
||||
<input type="text" name="db_host" class="form-control" value="localhost" placeholder="localhost">
|
||||
<input type="text" name="db_host" class="form-control" value="{{.DbHost}}" placeholder="localhost">
|
||||
</div>
|
||||
<div class="form-group" id="db_port">
|
||||
<label for="formGroupExampleInput">Database Port</label>
|
||||
<input type="text" name="db_port" class="form-control" value="5555" placeholder="localhost">
|
||||
<input type="text" name="db_port" class="form-control" value="{{.DbPort}}" placeholder="localhost">
|
||||
</div>
|
||||
<div class="form-group" id="db_user">
|
||||
<label for="formGroupExampleInput2">Username</label>
|
||||
<input type="text" name="db_user" class="form-control" value="root" placeholder="root">
|
||||
<input type="text" name="db_user" class="form-control" value="{{.DbUser}}" placeholder="root">
|
||||
</div>
|
||||
<div class="form-group" id="db_password">
|
||||
<label for="formGroupExampleInput2">Password</label>
|
||||
<input type="password" name="db_password" class="form-control" id="formGroupExampleInput2" value="223352hunter" placeholder="password123">
|
||||
<input type="password" name="db_password" class="form-control" value="{{.DbPass}}" id="formGroupExampleInput2" value="" placeholder="password123">
|
||||
</div>
|
||||
<div class="form-group" id="db_database">
|
||||
<label for="formGroupExampleInput2">Database</label>
|
||||
<input type="text" name="db_database" class="form-control" id="formGroupExampleInput2" value="uptime" placeholder="uptimeapi">
|
||||
<input type="text" name="db_database" class="form-control" value="{{.DbData}}" id="formGroupExampleInput2" value="statup" placeholder="Database name">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -55,27 +61,27 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label for="formGroupExampleInput">Project Name</label>
|
||||
<input type="text" name="project" class="form-control" id="formGroupExampleInput" placeholder="Great Uptime">
|
||||
<input type="text" name="project" class="form-control" value="{{.Project}}" id="formGroupExampleInput" placeholder="Great Uptime">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="formGroupExampleInput">Project Description</label>
|
||||
<input type="text" name="description" class="form-control" id="formGroupExampleInput" placeholder="Great Uptime">
|
||||
<input type="text" name="description" class="form-control" value="{{.Description}}" id="formGroupExampleInput" placeholder="Great Uptime">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="formGroupExampleInput">Admin Username</label>
|
||||
<input type="text" name="username" class="form-control" id="formGroupExampleInput" value="admin" placeholder="admin">
|
||||
<input type="text" name="username" class="form-control" value="{{.Username}}" id="formGroupExampleInput" value="admin" placeholder="admin">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="formGroupExampleInput">Admin Password</label>
|
||||
<input type="password" name="password" class="form-control" id="formGroupExampleInput" value="admin" placeholder="admin">
|
||||
<input type="password" name="password" class="form-control" value="{{.Password}}" id="formGroupExampleInput" value="admin" placeholder="admin">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="formGroupExampleInput">Confirm Password</label>
|
||||
<input type="password" name="confirm_password" class="form-control" id="formGroupExampleInput" value="admin" placeholder="admin">
|
||||
<input type="password" name="confirm_password" class="form-control" value="{{.Password}}" id="formGroupExampleInput" value="admin" placeholder="admin">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -34,6 +35,11 @@
|
|||
<tr>
|
||||
<th scope="row">{{.Id}}</th>
|
||||
<td>{{.Username}}</td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<a href="/users/{{.Id}}/delete" class="btn btn-danger">Delete</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
|
|
119
main.go
119
main.go
|
@ -1,14 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/GeertJohan/go.rice"
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/hunterlong/statup/plugin"
|
||||
_ "github.com/lib/pq"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -20,7 +18,6 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
db *sql.DB
|
||||
configs *Config
|
||||
core *Core
|
||||
store *sessions.CookieStore
|
||||
|
@ -93,114 +90,6 @@ func DownloadFile(filepath string, url string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func SelectSettings(p plugin.Info) map[string]string {
|
||||
|
||||
data := make(map[string]string)
|
||||
var tableInput []string
|
||||
|
||||
for _, v := range p.Form {
|
||||
val := fmt.Sprintf("%v", v.InputName)
|
||||
tableInput = append(tableInput, val)
|
||||
}
|
||||
|
||||
ins := strings.Join(tableInput, ", ")
|
||||
|
||||
sql := fmt.Sprintf("SELECT %v FROM settings_%v LIMIT 1", ins, p.Name)
|
||||
rows, err := db.Query(sql)
|
||||
if err != nil {
|
||||
fmt.Println("SQL ERROR: ", err)
|
||||
return map[string]string{}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
val := values[i]
|
||||
|
||||
b, _ := val.([]byte)
|
||||
|
||||
realVal := string(b)
|
||||
|
||||
//col.ChangeVal(realVal)
|
||||
|
||||
fmt.Println(col.Value, realVal)
|
||||
|
||||
data[col.InputName] = realVal
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func CreateSettingsTable(p plugin.Info) string {
|
||||
var tableValues []string
|
||||
tableValues = append(tableValues, "plugin text")
|
||||
tableValues = append(tableValues, "enabled bool")
|
||||
for _, v := range p.Form {
|
||||
tb := fmt.Sprintf("%v %v", v.InputName, v.InputType)
|
||||
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
|
||||
}
|
||||
|
||||
func InitalSettings(p plugin.Info) {
|
||||
var tableValues []string
|
||||
var tableInput []string
|
||||
|
||||
tableValues = append(tableValues, "plugin")
|
||||
tableInput = append(tableInput, fmt.Sprintf("'%v'", p.Name))
|
||||
|
||||
tableValues = append(tableValues, "enabled")
|
||||
tableInput = append(tableInput, "false")
|
||||
|
||||
for _, v := range p.Form {
|
||||
val := fmt.Sprintf("'%v'", v.Value)
|
||||
tableValues = append(tableValues, v.InputName)
|
||||
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()
|
||||
}
|
||||
|
||||
func UpdateSettings(p plugin.Info, data map[string]string) {
|
||||
var tableInput []string
|
||||
|
||||
for _, v := range p.Form {
|
||||
newValue := data[v.InputName]
|
||||
val := fmt.Sprintf("%v='%v'", v.InputName, newValue)
|
||||
tableInput = append(tableInput, val)
|
||||
}
|
||||
|
||||
ins := strings.Join(tableInput, ", ")
|
||||
sql := fmt.Sprintf("UPDATE settings_%v SET %v WHERE plugin='%v';", p.Name, ins, p.Name)
|
||||
smtp, _ := db.Prepare(sql)
|
||||
_, _ = smtp.Exec()
|
||||
}
|
||||
|
||||
//func DownloadPlugin(name string) {
|
||||
// plugin := SelectPlugin(name)
|
||||
// var _, err = os.Stat("plugins/" + plugin.Namespace)
|
||||
|
@ -234,7 +123,6 @@ func UpdateSettings(p plugin.Info, data map[string]string) {
|
|||
|
||||
func main() {
|
||||
var err error
|
||||
VERSION = "1.1.1"
|
||||
fmt.Printf("Starting Statup v%v\n", VERSION)
|
||||
RenderBoxes()
|
||||
configs, err = LoadConfig()
|
||||
|
@ -270,8 +158,7 @@ func mainProcess() {
|
|||
}
|
||||
|
||||
func throw(err error) {
|
||||
panic(err)
|
||||
fmt.Println(err)
|
||||
fmt.Println("ERROR: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
@ -317,12 +204,12 @@ func LoadPlugins() {
|
|||
continue
|
||||
}
|
||||
|
||||
plugActions.OnLoad()
|
||||
|
||||
allPlugins = append(allPlugins, plugActions)
|
||||
core.Plugins = append(core.Plugins, plugActions.GetInfo())
|
||||
}
|
||||
|
||||
OnLoad(dbSession)
|
||||
|
||||
fmt.Printf("Loaded %v Plugins\n", len(allPlugins))
|
||||
|
||||
ForEachPlugin()
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
VERSION = "1.1.1"
|
||||
RenderBoxes()
|
||||
os.Remove("./statup.db")
|
||||
Router()
|
||||
|
@ -26,6 +25,7 @@ func TestMySQLMakeConfig(t *testing.T) {
|
|||
"This is a test of Statup.io!",
|
||||
"admin",
|
||||
"admin",
|
||||
nil,
|
||||
}
|
||||
err := config.Save()
|
||||
assert.Nil(t, err)
|
||||
|
@ -64,6 +64,7 @@ func TestSqliteMakeConfig(t *testing.T) {
|
|||
"This is a test of Statup.io!",
|
||||
"admin",
|
||||
"admin",
|
||||
nil,
|
||||
}
|
||||
err := config.Save()
|
||||
assert.Nil(t, err)
|
||||
|
@ -93,6 +94,7 @@ func TestPostgresMakeConfig(t *testing.T) {
|
|||
"This is a test of Statup.io!",
|
||||
"admin",
|
||||
"admin",
|
||||
nil,
|
||||
}
|
||||
err := config.Save()
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -3,7 +3,6 @@ package plugin
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
"upper.io/db.v3/lib/sqlbuilder"
|
||||
)
|
||||
|
||||
|
@ -14,6 +13,10 @@ import (
|
|||
//
|
||||
// https://statup.io
|
||||
//
|
||||
//
|
||||
// An expandable plugin framework that will still
|
||||
// work even if there's an update or addition.
|
||||
//
|
||||
|
||||
var (
|
||||
DB sqlbuilder.Database
|
||||
|
@ -34,56 +37,22 @@ type PluginInfo struct {
|
|||
|
||||
type PluginActions interface {
|
||||
GetInfo() Info
|
||||
SetInfo(map[string]string) Info
|
||||
SetInfo(map[string]interface{}) Info
|
||||
Routes() []Routing
|
||||
OnSave(map[string]string)
|
||||
OnFailure(*Service)
|
||||
OnSuccess(*Service)
|
||||
OnSettingsSaved(map[string]string)
|
||||
OnNewUser(*User)
|
||||
OnNewService(*Service)
|
||||
OnUpdatedService(*Service)
|
||||
OnDeletedService(*Service)
|
||||
OnInstall()
|
||||
OnUninstall()
|
||||
OnSave(map[string]interface{})
|
||||
OnFailure(map[string]interface{})
|
||||
OnSuccess(map[string]interface{})
|
||||
OnSettingsSaved(map[string]interface{})
|
||||
OnNewUser(map[string]interface{})
|
||||
OnNewService(map[string]interface{})
|
||||
OnUpdatedService(map[string]interface{})
|
||||
OnDeletedService(map[string]interface{})
|
||||
OnInstall(map[string]interface{})
|
||||
OnUninstall(map[string]interface{})
|
||||
OnBeforeRequest(map[string]interface{})
|
||||
OnAfterRequest(map[string]interface{})
|
||||
OnShutdown()
|
||||
OnLoad()
|
||||
OnBeforeRequest()
|
||||
OnAfterRequest()
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Id int64
|
||||
Username string
|
||||
Password string
|
||||
Email string
|
||||
}
|
||||
|
||||
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 int64
|
||||
CreatedAt time.Time
|
||||
Ago string
|
||||
OnLoad(sqlbuilder.Database)
|
||||
}
|
||||
|
||||
type Routing struct {
|
||||
|
@ -95,13 +64,4 @@ type Routing struct {
|
|||
type Info struct {
|
||||
Name string
|
||||
Description string
|
||||
Form []*FormElement
|
||||
}
|
||||
|
||||
type FormElement struct {
|
||||
Name string
|
||||
Description string
|
||||
InputName string
|
||||
InputType string
|
||||
Value string
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
|
||||
}
|
Binary file not shown.
21
services.go
21
services.go
|
@ -4,7 +4,6 @@ import (
|
|||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/plugin"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -31,11 +30,10 @@ type Service struct {
|
|||
AvgResponse string `json:"avg_response"`
|
||||
TotalUptime string `json:"uptime"`
|
||||
Failures []*Failure `json:"failures"`
|
||||
plugin.Service
|
||||
}
|
||||
|
||||
func SelectService(id int64) (Service, error) {
|
||||
var service Service
|
||||
func SelectService(id int64) (*Service, error) {
|
||||
var service *Service
|
||||
col := dbSession.Collection("services")
|
||||
res := col.Find("id", id)
|
||||
err := res.One(&service)
|
||||
|
@ -107,8 +105,7 @@ func (s *Service) GraphData() string {
|
|||
d = append(d, o)
|
||||
}
|
||||
data, _ := json.Marshal(d)
|
||||
s.Data = string(data)
|
||||
return s.Data
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (s *Service) AvgUptime() string {
|
||||
|
@ -135,11 +132,22 @@ func (u *Service) Delete() error {
|
|||
col := dbSession.Collection("services")
|
||||
res := col.Find("id", u.Id)
|
||||
err := res.Delete()
|
||||
OnDeletedService(u)
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *Service) DeleteFailures() {
|
||||
var fails []*Failure
|
||||
col := dbSession.Collection("failures")
|
||||
col.Find("service", u.Id).All(&fails)
|
||||
for _, fail := range fails {
|
||||
fail.Delete()
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Service) Update() {
|
||||
|
||||
OnUpdateService(u)
|
||||
}
|
||||
|
||||
func (u *Service) Create() (int64, error) {
|
||||
|
@ -150,6 +158,7 @@ func (u *Service) Create() (int64, error) {
|
|||
if uuid == nil {
|
||||
return 0, err
|
||||
}
|
||||
OnNewService(u)
|
||||
return uuid.(int64), err
|
||||
}
|
||||
|
||||
|
|
40
setup.go
40
setup.go
|
@ -20,6 +20,7 @@ type DbConfig struct {
|
|||
Description string `yaml:"-"`
|
||||
Username string `yaml:"-"`
|
||||
Password string `yaml:"-"`
|
||||
Error error
|
||||
}
|
||||
|
||||
func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -47,24 +48,38 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
description,
|
||||
username,
|
||||
password,
|
||||
nil,
|
||||
}
|
||||
err := config.Save()
|
||||
if err != nil {
|
||||
throw(err)
|
||||
config.Error = err
|
||||
SetupResponseError(w, r, config)
|
||||
return
|
||||
}
|
||||
|
||||
configs, err = LoadConfig()
|
||||
if err != nil {
|
||||
throw(err)
|
||||
config.Error = err
|
||||
SetupResponseError(w, r, config)
|
||||
return
|
||||
}
|
||||
|
||||
err = DbConnection(configs.Connection)
|
||||
if err != nil {
|
||||
throw(err)
|
||||
DeleteConfig()
|
||||
config.Error = err
|
||||
SetupResponseError(w, r, config)
|
||||
return
|
||||
}
|
||||
|
||||
admin := &User{
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
}
|
||||
admin.Create()
|
||||
|
||||
if sample == "on" {
|
||||
LoadSampleData()
|
||||
go LoadSampleData()
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
|
@ -72,6 +87,21 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
mainProcess()
|
||||
}
|
||||
|
||||
func DeleteConfig() {
|
||||
err := os.Remove("./config.yml")
|
||||
if err != nil {
|
||||
throw(err)
|
||||
}
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Error string
|
||||
}
|
||||
|
||||
func SetupResponseError(w http.ResponseWriter, r *http.Request, a interface{}) {
|
||||
ExecuteResponse(w, r, "setup.html", a)
|
||||
}
|
||||
|
||||
func (c *DbConfig) Save() error {
|
||||
var err error
|
||||
config, err := os.Create("config.yml")
|
||||
|
@ -102,6 +132,8 @@ func (c *DbConfig) Save() error {
|
|||
"config.yml",
|
||||
NewSHA1Hash(5),
|
||||
NewSHA1Hash(10),
|
||||
"",
|
||||
"",
|
||||
VERSION,
|
||||
[]plugin.Info{},
|
||||
[]PluginJSON{},
|
||||
|
|
|
@ -4,6 +4,8 @@ CREATE TABLE core (
|
|||
config VARCHAR(50),
|
||||
api_key VARCHAR(50),
|
||||
api_secret VARCHAR(50),
|
||||
style text,
|
||||
footer text,
|
||||
version VARCHAR(50)
|
||||
);
|
||||
CREATE TABLE users (
|
||||
|
|
|
@ -4,6 +4,8 @@ CREATE TABLE core (
|
|||
config text,
|
||||
api_key text,
|
||||
api_secret text,
|
||||
style text,
|
||||
footer text,
|
||||
version text
|
||||
);
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ CREATE TABLE core (
|
|||
config text,
|
||||
api_key text,
|
||||
api_secret text,
|
||||
style text,
|
||||
footer text,
|
||||
version text
|
||||
);
|
||||
|
||||
|
|
7
users.go
7
users.go
|
@ -31,6 +31,12 @@ func SelectUsername(username string) (*User, error) {
|
|||
return &user, err
|
||||
}
|
||||
|
||||
func (u *User) Delete() error {
|
||||
col := dbSession.Collection("users")
|
||||
user := col.Find("id", u.Id)
|
||||
return user.Delete()
|
||||
}
|
||||
|
||||
func (u *User) Create() (int64, error) {
|
||||
u.CreatedAt = time.Now()
|
||||
password := HashPassword(u.Password)
|
||||
|
@ -42,6 +48,7 @@ func (u *User) Create() (int64, error) {
|
|||
if uuid == nil {
|
||||
return 0, err
|
||||
}
|
||||
OnNewUser(u)
|
||||
return uuid.(int64), err
|
||||
}
|
||||
|
||||
|
|
207
web.go
207
web.go
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fatih/structs"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/sessions"
|
||||
"html/template"
|
||||
|
@ -16,7 +17,7 @@ var (
|
|||
)
|
||||
|
||||
const (
|
||||
cookieKey = "apizer_auth"
|
||||
cookieKey = "statup_auth"
|
||||
)
|
||||
|
||||
func Router() *mux.Router {
|
||||
|
@ -31,13 +32,15 @@ func Router() *mux.Router {
|
|||
r.Handle("/logout", http.HandlerFunc(LogoutHandler))
|
||||
r.Handle("/services", http.HandlerFunc(ServicesHandler)).Methods("GET")
|
||||
r.Handle("/services", http.HandlerFunc(CreateServiceHandler)).Methods("POST")
|
||||
r.Handle("/service/{id}", http.HandlerFunc(ServicesViewHandler))
|
||||
r.Handle("/service/{id}", http.HandlerFunc(ServicesViewHandler)).Methods("GET")
|
||||
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("/service/{id}/badge.svg", http.HandlerFunc(ServicesBadgeHandler))
|
||||
r.Handle("/service/{id}/delete_failures", http.HandlerFunc(ServicesDeleteFailuresHandler)).Methods("GET")
|
||||
r.Handle("/users", http.HandlerFunc(UsersHandler)).Methods("GET")
|
||||
r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST")
|
||||
r.Handle("/users/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET")
|
||||
r.Handle("/settings", http.HandlerFunc(PluginsHandler)).Methods("GET")
|
||||
r.Handle("/settings", http.HandlerFunc(SaveSettingsHandler)).Methods("POST")
|
||||
r.Handle("/plugins/download/{name}", http.HandlerFunc(PluginsDownloadHandler))
|
||||
|
@ -147,12 +150,11 @@ func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func SetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
tmpl := Parse("setup.html")
|
||||
tmpl.Execute(w, nil)
|
||||
ExecuteResponse(w, r, "setup.html", nil)
|
||||
}
|
||||
|
||||
type index struct {
|
||||
Project string
|
||||
Core Core
|
||||
Services []*Service
|
||||
}
|
||||
|
||||
|
@ -161,10 +163,8 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, "/setup", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
tmpl := Parse("index.html")
|
||||
out := index{core.Name, services}
|
||||
tmpl.Execute(w, out)
|
||||
out := index{*core, services}
|
||||
ExecuteResponse(w, r, "index.html", out)
|
||||
}
|
||||
|
||||
type dashboard struct {
|
||||
|
@ -178,13 +178,11 @@ type dashboard struct {
|
|||
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := store.Get(r, cookieKey)
|
||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||
tmpl := Parse("login.html")
|
||||
tmpl.Execute(w, nil)
|
||||
ExecuteResponse(w, r, "login.html", nil)
|
||||
} else {
|
||||
tmpl := Parse("dashboard.html")
|
||||
fails, _ := CountFailures()
|
||||
out := dashboard{services, core, CountOnline(), len(services), fails}
|
||||
tmpl.Execute(w, out)
|
||||
ExecuteResponse(w, r, "dashboard.html", out)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -195,18 +193,17 @@ type serviceHandler struct {
|
|||
}
|
||||
|
||||
func ServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := store.Get(r, cookieKey)
|
||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||
auth := IsAuthenticated(r)
|
||||
if !auth {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
tmpl := Parse("services.html")
|
||||
tmpl.Execute(w, services)
|
||||
ExecuteResponse(w, r, "services.html", services)
|
||||
}
|
||||
|
||||
func ServicesDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := store.Get(r, cookieKey)
|
||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||
auth := IsAuthenticated(r)
|
||||
if !auth {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
@ -218,8 +215,28 @@ func ServicesDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func ServicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
||||
auth := IsAuthenticated(r)
|
||||
if !auth {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
service, _ := SelectService(StringInt(vars["id"]))
|
||||
|
||||
service.DeleteFailures()
|
||||
services, _ = SelectAllServices()
|
||||
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func IsAuthenticated(r *http.Request) bool {
|
||||
session, _ := store.Get(r, cookieKey)
|
||||
if store == nil {
|
||||
return false
|
||||
}
|
||||
session, err := store.Get(r, cookieKey)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if session.Values["authenticated"] == nil {
|
||||
return false
|
||||
}
|
||||
|
@ -233,11 +250,24 @@ func SaveSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
r.ParseForm()
|
||||
name := r.PostForm.Get("name")
|
||||
name := r.PostForm.Get("project")
|
||||
if name != "" {
|
||||
core.Name = name
|
||||
}
|
||||
description := r.PostForm.Get("description")
|
||||
core.Name = name
|
||||
core.Description = description
|
||||
if description != core.Description {
|
||||
core.Description = description
|
||||
}
|
||||
style := r.PostForm.Get("style")
|
||||
if style != core.Style {
|
||||
core.Style = style
|
||||
}
|
||||
footer := r.PostForm.Get("footer")
|
||||
if footer != core.Footer {
|
||||
core.Footer = footer
|
||||
}
|
||||
core.Update()
|
||||
OnSettingsSaved(core)
|
||||
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
|
@ -247,27 +277,23 @@ func PluginsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
tmpl := ParsePlugins("plugins.html")
|
||||
core.FetchPluginRepo()
|
||||
|
||||
var pluginFields []PluginSelect
|
||||
|
||||
for _, p := range allPlugins {
|
||||
fields := SelectSettings(p.GetInfo())
|
||||
fields := structs.Map(p.GetInfo())
|
||||
|
||||
pluginFields = append(pluginFields, PluginSelect{p.GetInfo().Name, fields})
|
||||
}
|
||||
|
||||
core.PluginFields = pluginFields
|
||||
|
||||
fmt.Println(&core.PluginFields)
|
||||
|
||||
tmpl.Execute(w, core)
|
||||
ExecuteResponse(w, r, "plugins.html", core)
|
||||
}
|
||||
|
||||
type PluginSelect struct {
|
||||
Plugin string
|
||||
Params map[string]string
|
||||
Params map[string]interface{}
|
||||
}
|
||||
|
||||
func PluginSavedHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -283,8 +309,7 @@ func PluginSavedHandler(w http.ResponseWriter, r *http.Request) {
|
|||
for k, v := range r.PostForm {
|
||||
data[k] = strings.Join(v, "")
|
||||
}
|
||||
UpdateSettings(plug.GetInfo(), data)
|
||||
plug.OnSave(data)
|
||||
plug.OnSave(structs.Map(data))
|
||||
http.Redirect(w, r, "/settings", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
|
@ -307,8 +332,7 @@ func HelpHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
tmpl := Parse("help.html")
|
||||
tmpl.Execute(w, nil)
|
||||
ExecuteResponse(w, r, "help.html", nil)
|
||||
}
|
||||
|
||||
func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -338,63 +362,66 @@ func ServicesBadgeHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func ServicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||
auth := IsAuthenticated(r)
|
||||
vars := mux.Vars(r)
|
||||
service, _ := SelectService(StringInt(vars["id"]))
|
||||
tmpl := Parse("service.html")
|
||||
serve := &serviceHandler{service, auth}
|
||||
tmpl.Execute(w, serve)
|
||||
}
|
||||
|
||||
func Parse(file string) *template.Template {
|
||||
nav, _ := tmplBox.String("nav.html")
|
||||
render, err := tmplBox.String(file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t := template.New("message")
|
||||
t.Funcs(template.FuncMap{
|
||||
"js": func(html string) template.JS {
|
||||
return template.JS(html)
|
||||
},
|
||||
"safe": func(html string) template.HTML {
|
||||
return template.HTML(html)
|
||||
},
|
||||
})
|
||||
t, _ = t.Parse(nav)
|
||||
t.Parse(render)
|
||||
return t
|
||||
}
|
||||
|
||||
func ParsePlugins(file string) *template.Template {
|
||||
nav, _ := tmplBox.String("nav.html")
|
||||
slack, _ := tmplBox.String("plugins/slack.html")
|
||||
render, err := tmplBox.String(file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t := template.New("message")
|
||||
t.Funcs(template.FuncMap{
|
||||
"js": func(html string) template.JS {
|
||||
return template.JS(html)
|
||||
},
|
||||
"safe": func(html string) template.HTML {
|
||||
return template.HTML(html)
|
||||
},
|
||||
})
|
||||
t, _ = t.Parse(nav)
|
||||
t.Parse(slack)
|
||||
t.Parse(render)
|
||||
return t
|
||||
}
|
||||
|
||||
func UsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("viewing user")
|
||||
session, _ := store.Get(r, cookieKey)
|
||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||
if !auth {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
tmpl := Parse("users.html")
|
||||
users, _ := SelectAllUsers()
|
||||
tmpl.Execute(w, users)
|
||||
vars := mux.Vars(r)
|
||||
service, _ := SelectService(StringInt(vars["id"]))
|
||||
ExecuteResponse(w, r, "service.html", service)
|
||||
}
|
||||
|
||||
func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}) {
|
||||
nav, _ := tmplBox.String("nav.html")
|
||||
render, err := tmplBox.String(file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t := template.New("message")
|
||||
t.Funcs(template.FuncMap{
|
||||
"js": func(html string) template.JS {
|
||||
return template.JS(html)
|
||||
},
|
||||
"safe": func(html string) template.HTML {
|
||||
return template.HTML(html)
|
||||
},
|
||||
"Auth": func() bool {
|
||||
return IsAuthenticated(r)
|
||||
},
|
||||
"VERSION": func() string {
|
||||
return VERSION
|
||||
},
|
||||
})
|
||||
t, _ = t.Parse(nav)
|
||||
t.Parse(render)
|
||||
t.Execute(w, data)
|
||||
}
|
||||
|
||||
func UsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
auth := IsAuthenticated(r)
|
||||
if !auth {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
users, _ := SelectAllUsers()
|
||||
ExecuteResponse(w, r, "users.html", users)
|
||||
}
|
||||
|
||||
func UsersDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
auth := IsAuthenticated(r)
|
||||
if !auth {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
id, _ := strconv.Atoi(vars["id"])
|
||||
user, _ := SelectUser(int64(id))
|
||||
|
||||
users, _ := SelectAllUsers()
|
||||
if len(users) == 1 {
|
||||
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
user.Delete()
|
||||
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue