mirror of https://github.com/statping/statping
updates
parent
e265765c75
commit
9a237defe7
|
@ -1,3 +1,5 @@
|
||||||
.idea
|
.idea
|
||||||
rice-box.go
|
rice-box.go
|
||||||
config.yml
|
config.yml
|
||||||
|
statup.db
|
||||||
|
plugins/*.so
|
|
@ -12,6 +12,9 @@ sudo: required
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
|
- postgresql
|
||||||
|
- mysql
|
||||||
|
- mongodb
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- VERSION=0.12
|
- VERSION=0.12
|
||||||
|
@ -37,14 +40,12 @@ deploy:
|
||||||
- "build/statup-windows-x32.exe"
|
- "build/statup-windows-x32.exe"
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
|
|
||||||
services:
|
|
||||||
- postgresql
|
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then travis_wait 30 docker pull karalabe/xgo-latest; fi
|
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then travis_wait 30 docker pull karalabe/xgo-latest; fi
|
||||||
|
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- psql -c 'create database travis_ci_test;' -U postgres
|
- psql -c 'create database travis_ci_test;' -U postgres
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ApiIndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
json.NewEncoder(w).Encode(core)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service, _ := SelectService(StringInt(vars["id"]))
|
||||||
|
json.NewEncoder(w).Encode(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service, _ := SelectService(StringInt(vars["id"]))
|
||||||
|
|
||||||
|
var s Service
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
decoder.Decode(&s)
|
||||||
|
|
||||||
|
json.NewEncoder(w).Encode(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiAllServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
services, _ := SelectAllServices()
|
||||||
|
json.NewEncoder(w).Encode(services)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
user, _ := SelectUser(StringInt(vars["id"]))
|
||||||
|
json.NewEncoder(w).Encode(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApiAllUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
users, _ := SelectAllUsers()
|
||||||
|
json.NewEncoder(w).Encode(users)
|
||||||
|
}
|
34
checker.go
34
checker.go
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func CheckServices() {
|
func CheckServices() {
|
||||||
services = SelectAllServices()
|
services, _ = SelectAllServices()
|
||||||
for _, v := range services {
|
for _, v := range services {
|
||||||
obj := v
|
obj := v
|
||||||
go obj.CheckQueue()
|
go obj.CheckQueue()
|
||||||
|
@ -18,12 +18,15 @@ func CheckServices() {
|
||||||
|
|
||||||
func (s *Service) CheckQueue() {
|
func (s *Service) CheckQueue() {
|
||||||
s.Check()
|
s.Check()
|
||||||
|
if s.Interval < 1 {
|
||||||
|
s.Interval = 1
|
||||||
|
}
|
||||||
fmt.Printf(" Service: %v | Online: %v | Latency: %v\n", s.Name, s.Online, s.Latency)
|
fmt.Printf(" Service: %v | Online: %v | Latency: %v\n", s.Name, s.Online, s.Latency)
|
||||||
time.Sleep(time.Duration(s.Interval) * time.Second)
|
time.Sleep(time.Duration(s.Interval) * time.Second)
|
||||||
s.CheckQueue()
|
s.CheckQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Check() {
|
func (s *Service) Check() *Service {
|
||||||
t1 := time.Now()
|
t1 := time.Now()
|
||||||
client := http.Client{
|
client := http.Client{
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
|
@ -33,7 +36,7 @@ func (s *Service) Check() {
|
||||||
s.Latency = t2.Sub(t1).Seconds()
|
s.Latency = t2.Sub(t1).Seconds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Failure(fmt.Sprintf("HTTP Error %v", err))
|
s.Failure(fmt.Sprintf("HTTP Error %v", err))
|
||||||
return
|
return s
|
||||||
}
|
}
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
if s.Expected != "" {
|
if s.Expected != "" {
|
||||||
|
@ -41,23 +44,40 @@ func (s *Service) Check() {
|
||||||
match, _ := regexp.MatchString(s.Expected, string(contents))
|
match, _ := regexp.MatchString(s.Expected, string(contents))
|
||||||
if !match {
|
if !match {
|
||||||
s.Failure(fmt.Sprintf("HTTP Response Body did not match '%v'", s.Expected))
|
s.Failure(fmt.Sprintf("HTTP Response Body did not match '%v'", s.Expected))
|
||||||
return
|
return s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.ExpectedStatus != response.StatusCode {
|
if s.ExpectedStatus != response.StatusCode {
|
||||||
s.Failure(fmt.Sprintf("HTTP Status Code %v did not match %v", response.StatusCode, s.ExpectedStatus))
|
s.Failure(fmt.Sprintf("HTTP Status Code %v did not match %v", response.StatusCode, s.ExpectedStatus))
|
||||||
return
|
return s
|
||||||
}
|
}
|
||||||
s.Online = true
|
s.Online = true
|
||||||
s.Record(response)
|
s.Record(response)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type HitData struct {
|
||||||
|
Latency float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Record(response *http.Response) {
|
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()
|
s.Online = true
|
||||||
|
data := HitData{
|
||||||
|
Latency: s.Latency,
|
||||||
|
}
|
||||||
|
s.CreateHit(data)
|
||||||
OnSuccess(s)
|
OnSuccess(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FailureData struct {
|
||||||
|
Issue string
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) Failure(issue string) {
|
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()
|
s.Online = false
|
||||||
|
data := FailureData{
|
||||||
|
Issue: issue,
|
||||||
|
}
|
||||||
|
s.CreateFailure(data)
|
||||||
OnFailure(s)
|
OnFailure(s)
|
||||||
}
|
}
|
||||||
|
|
22
core.go
22
core.go
|
@ -6,29 +6,23 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Core struct {
|
type Core struct {
|
||||||
Name string
|
Name string `db:"name"`
|
||||||
Config string
|
Description string `db:"description"`
|
||||||
Key string
|
Config string `db:"config"`
|
||||||
Secret string
|
ApiKey string `db:"api_key"`
|
||||||
Version string
|
ApiSecret string `db:"api_secret"`
|
||||||
|
Version string `db:"version"`
|
||||||
Plugins []plugin.Info
|
Plugins []plugin.Info
|
||||||
Repos []PluginJSON
|
Repos []PluginJSON
|
||||||
PluginFields []PluginSelect
|
PluginFields []PluginSelect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func SelectCore() (*Core, error) {
|
func SelectCore() (*Core, error) {
|
||||||
var core Core
|
var core Core
|
||||||
rows, err := db.Query("SELECT * FROM core")
|
err := dbSession.Collection("core").Find().One(&core)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for rows.Next() {
|
store = sessions.NewCookieStore([]byte(core.ApiSecret))
|
||||||
err = rows.Scan(&core.Name, &core.Config, &core.Key, &core.Secret, &core.Version)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
store = sessions.NewCookieStore([]byte(core.Secret))
|
|
||||||
return &core, err
|
return &core, err
|
||||||
}
|
}
|
||||||
|
|
170
database.go
170
database.go
|
@ -1,38 +1,59 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statup/plugin"
|
"github.com/hunterlong/statup/plugin"
|
||||||
_ "github.com/lib/pq"
|
"strings"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
"upper.io/db.v3/lib/sqlbuilder"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
"upper.io/db.v3/mysql"
|
||||||
"math/rand"
|
"upper.io/db.v3/postgresql"
|
||||||
"time"
|
"upper.io/db.v3/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dbServer string
|
||||||
|
sqliteSettings sqlite.ConnectionURL
|
||||||
|
postgresSettings postgresql.ConnectionURL
|
||||||
|
mysqlSettings mysql.ConnectionURL
|
||||||
|
dbSession sqlbuilder.Database
|
||||||
)
|
)
|
||||||
|
|
||||||
func DbConnection(dbType string) error {
|
func DbConnection(dbType string) error {
|
||||||
var err error
|
var err error
|
||||||
var dbInfo string
|
if dbType == "sqlite" {
|
||||||
if dbType=="sqlite3" {
|
sqliteSettings = sqlite.ConnectionURL{
|
||||||
dbInfo = "./statup.db"
|
Database: "statup.db",
|
||||||
} else if dbType=="mysql" {
|
}
|
||||||
dbInfo = fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8", configs.User, configs.Password, configs.Host, configs.Port, configs.Database)
|
dbSession, err = sqlite.Open(sqliteSettings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if dbType == "mysql" {
|
||||||
|
mysqlSettings = mysql.ConnectionURL{
|
||||||
|
Database: configs.Database,
|
||||||
|
Host: configs.Host,
|
||||||
|
User: configs.User,
|
||||||
|
Password: configs.Password,
|
||||||
|
}
|
||||||
|
dbSession, err = mysql.Open(mysqlSettings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dbInfo = fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", configs.Host, configs.Port, configs.User, configs.Password, configs.Database)
|
postgresSettings = postgresql.ConnectionURL{
|
||||||
|
Database: configs.Database,
|
||||||
|
Host: configs.Host,
|
||||||
|
User: configs.User,
|
||||||
|
Password: configs.Password,
|
||||||
|
}
|
||||||
|
dbSession, err = postgresql.Open(postgresSettings)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
db, err = sql.Open(dbType, dbInfo)
|
//dbSession.SetLogging(true)
|
||||||
if err != nil {
|
dbServer = dbType
|
||||||
return err
|
plugin.SetDatabase(dbSession)
|
||||||
}
|
|
||||||
|
|
||||||
//stmt, err := db.Prepare("CREATE database statup;")
|
|
||||||
//if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
//}
|
|
||||||
//stmt.Exec()
|
|
||||||
|
|
||||||
plugin.SetDatabase(db)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,33 +66,104 @@ func UpgradeDatabase() {
|
||||||
}
|
}
|
||||||
fmt.Println("Upgrading Database...")
|
fmt.Println("Upgrading Database...")
|
||||||
upgrade, _ := sqlBox.String("upgrade.sql")
|
upgrade, _ := sqlBox.String("upgrade.sql")
|
||||||
db.QueryRow(upgrade).Scan()
|
requests := strings.Split(upgrade, ";")
|
||||||
|
for _, request := range requests {
|
||||||
|
_, err := db.Exec(request)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DropDatabase() {
|
func DropDatabase() {
|
||||||
fmt.Println("Dropping Tables...")
|
fmt.Println("Dropping Tables...")
|
||||||
down, _ := sqlBox.String("down.sql")
|
down, _ := sqlBox.String("down.sql")
|
||||||
db.QueryRow(down).Scan()
|
requests := strings.Split(down, ";")
|
||||||
|
for _, request := range requests {
|
||||||
|
_, err := dbSession.Exec(request)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadSampleData() error {
|
||||||
|
fmt.Println("Inserting Sample Data...")
|
||||||
|
s1 := &Service{
|
||||||
|
Name: "Google",
|
||||||
|
Domain: "https://google.com",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 10,
|
||||||
|
Port: 0,
|
||||||
|
Type: "https",
|
||||||
|
Method: "GET",
|
||||||
|
}
|
||||||
|
s2 := &Service{
|
||||||
|
Name: "Statup.io",
|
||||||
|
Domain: "https://statup.io",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 15,
|
||||||
|
Port: 0,
|
||||||
|
Type: "https",
|
||||||
|
Method: "GET",
|
||||||
|
}
|
||||||
|
s3 := &Service{
|
||||||
|
Name: "Statup.io SSL Check",
|
||||||
|
Domain: "https://statup.io",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 15,
|
||||||
|
Port: 443,
|
||||||
|
Type: "tcp",
|
||||||
|
}
|
||||||
|
s4 := &Service{
|
||||||
|
Name: "Github Failing Check",
|
||||||
|
Domain: "https://github.com/thisisnotausernamemaybeitis",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 15,
|
||||||
|
Port: 0,
|
||||||
|
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++ {
|
||||||
|
s1.Check()
|
||||||
|
s2.Check()
|
||||||
|
s3.Check()
|
||||||
|
s4.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateDatabase() {
|
func CreateDatabase() {
|
||||||
fmt.Println("Creating Tables...")
|
fmt.Println("Creating Tables...")
|
||||||
VERSION = "1.1.1"
|
VERSION = "1.1.1"
|
||||||
up, _ := sqlBox.String("up.sql")
|
sql := "postgres_up.sql"
|
||||||
db.QueryRow(up).Scan()
|
if dbServer == "mysql" {
|
||||||
|
sql = "mysql_up.sql"
|
||||||
|
} else if dbServer == "sqlite3" {
|
||||||
|
sql = "sqlite_up.sql"
|
||||||
|
}
|
||||||
|
up, _ := sqlBox.String(sql)
|
||||||
|
requests := strings.Split(up, ";")
|
||||||
|
for _, request := range requests {
|
||||||
|
_, err := dbSession.Exec(request)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
//secret := NewSHA1Hash()
|
//secret := NewSHA1Hash()
|
||||||
//db.QueryRow("INSERT INTO core (secret, version) VALUES ($1, $2);", secret, VERSION).Scan()
|
//db.QueryRow("INSERT INTO core (secret, version) VALUES ($1, $2);", secret, VERSION).Scan()
|
||||||
fmt.Println("Database Created")
|
fmt.Println("Database Created")
|
||||||
//SampleData()
|
//SampleData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func SampleData() {
|
|
||||||
i := 0
|
|
||||||
for i < 300 {
|
|
||||||
ran := rand.Float32()
|
|
||||||
latency := fmt.Sprintf("%0.2f", ran)
|
|
||||||
date := time.Now().AddDate(0, 0, i)
|
|
||||||
db.QueryRow("INSERT INTO hits (service, latency, created_at) VALUES (1, $1, $2);", latency, date).Scan()
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
69
failures.go
69
failures.go
|
@ -1,54 +1,53 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ararog/timeago"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Failure struct {
|
type Failure struct {
|
||||||
Id int
|
Id int `db:"id,omitempty"`
|
||||||
Issue string
|
Issue string `db:"issue"`
|
||||||
Service int
|
Service int64 `db:"service"`
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time `db:"created_at"`
|
||||||
Ago string
|
Ago string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) SelectAllFailures() []*Failure {
|
func (s *Service) CreateFailure(data FailureData) (int64, error) {
|
||||||
var tks []*Failure
|
fail := &Failure{
|
||||||
rows, err := db.Query("SELECT * FROM failures WHERE service=$1 ORDER BY id DESC LIMIT 10", s.Id)
|
Issue: data.Issue,
|
||||||
if err != nil {
|
Service: s.Id,
|
||||||
panic(err)
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
for rows.Next() {
|
s.Failures = append(s.Failures, fail)
|
||||||
var tk Failure
|
col := dbSession.Collection("failures")
|
||||||
err = rows.Scan(&tk.Id, &tk.Issue, &tk.Service, &tk.CreatedAt)
|
uuid, err := col.Insert(fail)
|
||||||
if err != nil {
|
if uuid == nil {
|
||||||
panic(err)
|
return 0, err
|
||||||
}
|
|
||||||
|
|
||||||
tk.Ago, _ = timeago.TimeAgoWithTime(time.Now(), tk.CreatedAt)
|
|
||||||
|
|
||||||
tks = append(tks, &tk)
|
|
||||||
}
|
}
|
||||||
return tks
|
return uuid.(int64), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func CountFailures() int {
|
func (s *Service) SelectAllFailures() ([]*Failure, error) {
|
||||||
var amount int
|
var fails []*Failure
|
||||||
db.QueryRow("SELECT COUNT(id) FROM failures;").Scan(&amount)
|
col := dbSession.Collection("failures").Find("session", s.Id)
|
||||||
return amount
|
err := col.All(&fails)
|
||||||
|
return fails, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) TotalFailures() int {
|
func CountFailures() (uint64, error) {
|
||||||
var amount int
|
col := dbSession.Collection("failures").Find()
|
||||||
db.QueryRow("SELECT COUNT(id) FROM failures WHERE service=$1;", s.Id).Scan(&amount)
|
amount, err := col.Count()
|
||||||
return amount
|
return amount, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) TotalFailures24Hours() int {
|
func (s *Service) TotalFailures() (uint64, error) {
|
||||||
var amount int
|
col := dbSession.Collection("failures").Find("service", s.Id)
|
||||||
t := time.Now()
|
amount, err := col.Count()
|
||||||
x := t.AddDate(0, 0, -1)
|
return amount, err
|
||||||
db.QueryRow("SELECT COUNT(id) FROM failures WHERE service=$1 AND created_at>=$2 AND created_at<$3;", s.Id, x, t).Scan(&amount)
|
}
|
||||||
return amount
|
|
||||||
|
func (s *Service) TotalFailures24Hours() (uint64, error) {
|
||||||
|
col := dbSession.Collection("failures").Find("service", s.Id)
|
||||||
|
amount, err := col.Count()
|
||||||
|
return amount, err
|
||||||
}
|
}
|
||||||
|
|
75
hits.go
75
hits.go
|
@ -3,54 +3,51 @@ package main
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Hit struct {
|
type Hit struct {
|
||||||
Id int
|
Id int `db:"id,omitempty"`
|
||||||
Metric int
|
Service int64 `db:"service"`
|
||||||
Value float64
|
Latency float64 `db:"latency"`
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time `db:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Hits() []Hit {
|
func (s *Service) CreateHit(d HitData) (int64, error) {
|
||||||
var tks []Hit
|
h := Hit{
|
||||||
rows, err := db.Query("SELECT * FROM hits WHERE service=$1 ORDER BY id DESC LIMIT 256", s.Id)
|
Service: s.Id,
|
||||||
if err != nil {
|
Latency: d.Latency,
|
||||||
panic(err)
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
for rows.Next() {
|
col := dbSession.Collection("hits")
|
||||||
var tk Hit
|
uuid, err := col.Insert(h)
|
||||||
err = rows.Scan(&tk.Id, &tk.Metric, &tk.Value, &tk.CreatedAt)
|
if uuid == nil {
|
||||||
if err != nil {
|
return 0, err
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
tks = append(tks, tk)
|
|
||||||
}
|
}
|
||||||
return tks
|
return uuid.(int64), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) SelectHitsGroupBy(group string) []Hit {
|
func (s *Service) Hits() ([]Hit, error) {
|
||||||
var tks []Hit
|
var hits []Hit
|
||||||
rows, err := db.Query("SELECT date_trunc('$1', created_at), -- or hour, day, week, month, year count(1) FROM hits WHERE service=$2 group by 1", group, s.Id)
|
col := dbSession.Collection("hits").Find("service", s.Id)
|
||||||
if err != nil {
|
err := col.All(&hits)
|
||||||
panic(err)
|
return hits, err
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var tk Hit
|
|
||||||
err = rows.Scan(&tk.Id, &tk.Metric, &tk.Value, &tk.CreatedAt)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
tks = append(tks, tk)
|
|
||||||
}
|
|
||||||
return tks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) TotalHits() int {
|
func (s *Service) SelectHitsGroupBy(group string) ([]Hit, error) {
|
||||||
var amount int
|
var hits []Hit
|
||||||
db.QueryRow("SELECT COUNT(id) FROM hits WHERE service=$1;", s.Id).Scan(&amount)
|
col := dbSession.Collection("hits").Find("service", s.Id)
|
||||||
return amount
|
err := col.All(&hits)
|
||||||
|
return hits, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Sum() float64 {
|
func (s *Service) TotalHits() (uint64, error) {
|
||||||
|
col := dbSession.Collection("hits").Find("service", s.Id)
|
||||||
|
amount, err := col.Count()
|
||||||
|
return amount, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Sum() (float64, error) {
|
||||||
var amount float64
|
var amount float64
|
||||||
db.QueryRow("SELECT SUM(latency) FROM hits WHERE service=$1;", s.Id).Scan(&amount)
|
hits, err := s.Hits()
|
||||||
return amount
|
for _, h := range hits {
|
||||||
|
amount += h.Latency
|
||||||
|
}
|
||||||
|
return amount, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,10 @@ HTML,BODY {
|
||||||
background-color: white !important;
|
background-color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.online_list {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
@ -86,12 +90,28 @@ HTML,BODY {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-body H3 A {
|
||||||
|
color: #424242;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
|
|
||||||
|
HTML,BODY {
|
||||||
|
background-color: #efefef;
|
||||||
|
margin: 0px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
margin-left: 0px;
|
||||||
|
margin-top: 0px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
font-size: 6pt;
|
font-size: 6pt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
$('select#database_type').on('change', function(){
|
||||||
|
var selected = $('#database_type option:selected').val();
|
||||||
|
if (selected=="sqlite") {
|
||||||
|
$("#db_host").hide();
|
||||||
|
$("#db_password").hide();
|
||||||
|
$("#db_port").hide();
|
||||||
|
$("#db_user").hide();
|
||||||
|
$("#db_database").hide();
|
||||||
|
} else {
|
||||||
|
$("#db_host").show();
|
||||||
|
$("#db_password").show();
|
||||||
|
$("#db_port").show();
|
||||||
|
$("#db_user").show();
|
||||||
|
$("#db_database").show();
|
||||||
|
}
|
||||||
|
});
|
|
@ -59,8 +59,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -12,14 +12,32 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h1 class="text-center mb-4">{{.Project}}</h1>
|
<h1 class="text-center mb-4 mt-sm-3">{{.Project}}</h1>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-12 mb-5">
|
||||||
|
|
||||||
|
<div class="list-group online_list">
|
||||||
|
{{ range .Services }}
|
||||||
|
<a href="#" class="list-group-item list-group-item-action {{if .Online}}{{ end }}">
|
||||||
|
{{ .Name }}
|
||||||
|
{{if .Online}}
|
||||||
|
<span class="badge online_badge float-right">ONLINE</span>
|
||||||
|
{{ else }}
|
||||||
|
<span class="badge offline_badge float-right">OFFLINE</span>
|
||||||
|
{{end}}
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|
||||||
{{ range .Services }}
|
{{ range .Services }}
|
||||||
<div class="col-12 mb-4">
|
<div class="mb-4">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body{{if .Online}}{{else}} offline_bg{{end}}">
|
<div class="card-body{{if .Online}}{{else}} offline_bg{{end}}">
|
||||||
|
|
||||||
|
@ -37,17 +55,17 @@
|
||||||
<div class="row stats_area mt-5 mb-5">
|
<div class="row stats_area mt-5 mb-5">
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">{{.Online24Hours}}%</span>
|
<span class="lg_number">{{.Online24}}%</span>
|
||||||
Online last 24 Hours
|
Online last 24 Hours
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">{{.AvgResponse}}ms</span>
|
<span class="lg_number">{{.AvgTime}}ms</span>
|
||||||
Average Response
|
Average Response
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">{{.TotalUptime}}%</span>
|
<span class="lg_number">{{.AvgUptime}}%</span>
|
||||||
Total Uptime
|
Total Uptime
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,7 +101,7 @@ var chartdata = new Chart(ctx, {
|
||||||
data: {
|
data: {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Response Time (Milliseconds)',
|
label: 'Response Time (Milliseconds)',
|
||||||
data: {{js .Data}},
|
data: {{js .GraphData}},
|
||||||
backgroundColor: [
|
backgroundColor: [
|
||||||
'rgba(255, 99, 132, 0.2)',
|
'rgba(255, 99, 132, 0.2)',
|
||||||
'rgba(54, 162, 235, 0.2)',
|
'rgba(54, 162, 235, 0.2)',
|
||||||
|
@ -134,5 +152,6 @@ var chartdata = new Chart(ctx, {
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</script>
|
</script>
|
||||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -16,10 +16,9 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|
||||||
<form action="/login" method="POST">
|
<form action="/dashboard" method="POST">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="inputEmail3" class="col-sm-2 col-form-label">Username</label>
|
<label for="inputEmail3" class="col-sm-2 col-form-label">Username</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -58,6 +57,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,6 +1,6 @@
|
||||||
{{define "nav"}}
|
{{define "nav"}}
|
||||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
<a class="navbar-brand" href="#">Statup</a>
|
<a class="navbar-brand" href="/">Statup</a>
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -34,17 +34,17 @@
|
||||||
<div class="row stats_area mt-5 mb-5">
|
<div class="row stats_area mt-5 mb-5">
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">{{.Service.Online24Hours}}%</span>
|
<span class="lg_number">{{.Service.Online24}}%</span>
|
||||||
Online last 24 Hours
|
Online last 24 Hours
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">{{.Service.AvgResponse}}ms</span>
|
<span class="lg_number">{{.Service.AvgTime}}ms</span>
|
||||||
Average Response
|
Average Response
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">{{.Service.TotalUptime}}%</span>
|
<span class="lg_number">{{.Service.AvgUptime}}%</span>
|
||||||
Total Uptime
|
Total Uptime
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,6 +118,7 @@
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -128,7 +129,7 @@
|
||||||
data: {
|
data: {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Response Time (Milliseconds)',
|
label: 'Response Time (Milliseconds)',
|
||||||
data: {{js .Service.Data}},
|
data: {{js .Service.GraphData}},
|
||||||
backgroundColor: [
|
backgroundColor: [
|
||||||
'rgba(255, 99, 132, 0.2)',
|
'rgba(255, 99, 132, 0.2)',
|
||||||
'rgba(54, 162, 235, 0.2)',
|
'rgba(54, 162, 235, 0.2)',
|
||||||
|
@ -179,6 +180,6 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -108,6 +108,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,34 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
|
||||||
<link rel="stylesheet" href="/css/base.css">
|
|
||||||
|
|
||||||
<title>Statup | Dashboard</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
|
|
||||||
{{template "nav"}}
|
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col-12">
|
|
||||||
|
|
||||||
<h3>Statup Settings</h3>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -15,35 +15,36 @@
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<form method="POST" action="/setup/save">
|
<form method="POST" action="/setup">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputState">Database Connection</label>
|
<label for="inputState">Database Connection</label>
|
||||||
<select id="inputState" name="db_connection" class="form-control">
|
<select id="database_type" name="db_connection" class="form-control">
|
||||||
<option selected value="postgres">Postgres</option>
|
<option selected value="postgres">Postgres</option>
|
||||||
|
<option value="sqlite">Sqlite</option>
|
||||||
|
<option value="mysql">MySQL</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group" id="db_host">
|
||||||
<label for="formGroupExampleInput">Host</label>
|
<label for="formGroupExampleInput">Host</label>
|
||||||
<input type="text" name="db_host" class="form-control" id="formGroupExampleInput" value="localhost" placeholder="localhost">
|
<input type="text" name="db_host" class="form-control" value="localhost" placeholder="localhost">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group" id="db_port">
|
||||||
<label for="formGroupExampleInput">Database Port</label>
|
<label for="formGroupExampleInput">Database Port</label>
|
||||||
<input type="text" name="db_port" class="form-control" id="formGroupExampleInput" value="5555" placeholder="localhost">
|
<input type="text" name="db_port" class="form-control" value="5555" placeholder="localhost">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group" id="db_user">
|
||||||
<label for="formGroupExampleInput2">Username</label>
|
<label for="formGroupExampleInput2">Username</label>
|
||||||
<input type="text" name="db_user" class="form-control" id="formGroupExampleInput2" value="root" placeholder="root">
|
<input type="text" name="db_user" class="form-control" value="root" placeholder="root">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group" id="db_password">
|
||||||
<label for="formGroupExampleInput2">Password</label>
|
<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" id="formGroupExampleInput2" value="223352hunter" placeholder="password123">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group" id="db_database">
|
||||||
<label for="formGroupExampleInput2">Database</label>
|
<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" id="formGroupExampleInput2" value="uptime" placeholder="uptimeapi">
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,6 +58,11 @@
|
||||||
<input type="text" name="project" class="form-control" id="formGroupExampleInput" placeholder="Great Uptime">
|
<input type="text" name="project" class="form-control" id="formGroupExampleInput" placeholder="Great Uptime">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="formGroupExampleInput">Project Description</label>
|
||||||
|
<input type="text" name="description" class="form-control" id="formGroupExampleInput" placeholder="Great Uptime">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="formGroupExampleInput">Admin Username</label>
|
<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" id="formGroupExampleInput" value="admin" placeholder="admin">
|
||||||
|
@ -91,5 +97,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
|
<script src="/js/setup.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -40,32 +40,31 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Create User</h3>
|
<h3>Create User</h3>
|
||||||
|
<form action="/users" method="POST">
|
||||||
<form action="/users" method="POST">
|
<div class="form-group row">
|
||||||
<div class="form-group row">
|
<label for="inputEmail3" class="col-sm-4 col-form-label">Username</label>
|
||||||
<label for="inputEmail3" class="col-sm-4 col-form-label">Username</label>
|
<div class="col-sm-8">
|
||||||
<div class="col-sm-8">
|
<input type="text" name="username" class="form-control" id="inputEmail3" placeholder="Username">
|
||||||
<input type="text" name="username" class="form-control" id="inputEmail3" placeholder="Username">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
</div>
|
||||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Password</label>
|
<div class="form-group row">
|
||||||
<div class="col-sm-8">
|
<label for="inputPassword3" class="col-sm-4 col-form-label">Password</label>
|
||||||
<input type="password" name="password" class="form-control" id="inputPassword3" placeholder="Password">
|
<div class="col-sm-8">
|
||||||
</div>
|
<input type="password" name="password" class="form-control" id="inputPassword3" placeholder="Password">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
</div>
|
||||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Confirm Password</label>
|
<div class="form-group row">
|
||||||
<div class="col-sm-8">
|
<label for="inputPassword3" class="col-sm-4 col-form-label">Confirm Password</label>
|
||||||
<input type="password" name="password_confirm" class="form-control" id="inputPassword3" placeholder="ConfirmPassword">
|
<div class="col-sm-8">
|
||||||
</div>
|
<input type="password" name="password_confirm" class="form-control" id="inputPassword3" placeholder="ConfirmPassword">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
</div>
|
||||||
<div class="col-sm-8">
|
<div class="form-group row">
|
||||||
<button type="submit" class="btn btn-primary">Create User</button>
|
<div class="col-sm-8">
|
||||||
</div>
|
<button type="submit" class="btn btn-primary">Create User</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -74,6 +73,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||||
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
11
main.go
11
main.go
|
@ -15,6 +15,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
plg "plugin"
|
plg "plugin"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -245,6 +246,11 @@ func main() {
|
||||||
mainProcess()
|
mainProcess()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringInt(s string) int64 {
|
||||||
|
num, _ := strconv.Atoi(s)
|
||||||
|
return int64(num)
|
||||||
|
}
|
||||||
|
|
||||||
func mainProcess() {
|
func mainProcess() {
|
||||||
var err error
|
var err error
|
||||||
err = DbConnection(configs.Connection)
|
err = DbConnection(configs.Connection)
|
||||||
|
@ -253,7 +259,8 @@ func mainProcess() {
|
||||||
}
|
}
|
||||||
core, err = SelectCore()
|
core, err = SelectCore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
throw(err)
|
fmt.Println("Core database was not found, Statup is not setup yet.")
|
||||||
|
RunHTTPServer()
|
||||||
}
|
}
|
||||||
go CheckServices()
|
go CheckServices()
|
||||||
if !setupMode {
|
if !setupMode {
|
||||||
|
@ -263,6 +270,7 @@ func mainProcess() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func throw(err error) {
|
func throw(err error) {
|
||||||
|
panic(err)
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -334,6 +342,7 @@ func LoadConfig() (*Config, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = yaml.Unmarshal(file, &config)
|
err = yaml.Unmarshal(file, &config)
|
||||||
|
configs = &config
|
||||||
return &config, err
|
return &config, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
222
main_test.go
222
main_test.go
|
@ -2,8 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -12,9 +10,78 @@ import (
|
||||||
func TestInit(t *testing.T) {
|
func TestInit(t *testing.T) {
|
||||||
VERSION = "1.1.1"
|
VERSION = "1.1.1"
|
||||||
RenderBoxes()
|
RenderBoxes()
|
||||||
|
os.Remove("./statup.db")
|
||||||
|
Router()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMakeConfig(t *testing.T) {
|
func TestMySQLMakeConfig(t *testing.T) {
|
||||||
|
config := &DbConfig{
|
||||||
|
"mysql",
|
||||||
|
os.Getenv("DB_HOST"),
|
||||||
|
os.Getenv("DB_USER"),
|
||||||
|
os.Getenv("DB_PASS"),
|
||||||
|
os.Getenv("DB_DATABASE"),
|
||||||
|
3306,
|
||||||
|
"Testing MYSQL",
|
||||||
|
"This is a test of Statup.io!",
|
||||||
|
"admin",
|
||||||
|
"admin",
|
||||||
|
}
|
||||||
|
err := config.Save()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = LoadConfig()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "mysql", configs.Connection)
|
||||||
|
|
||||||
|
err = DbConnection(configs.Connection)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertMysqlSample(t *testing.T) {
|
||||||
|
err := LoadSampleData()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectCoreMYQL(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
core, err = SelectCore()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Testing MYSQL", core.Name)
|
||||||
|
assert.Equal(t, VERSION, core.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSqliteMakeConfig(t *testing.T) {
|
||||||
|
config := &DbConfig{
|
||||||
|
"sqlite",
|
||||||
|
os.Getenv("DB_HOST"),
|
||||||
|
os.Getenv("DB_USER"),
|
||||||
|
os.Getenv("DB_PASS"),
|
||||||
|
os.Getenv("DB_DATABASE"),
|
||||||
|
5432,
|
||||||
|
"Testing SQLITE",
|
||||||
|
"This is a test of Statup.io!",
|
||||||
|
"admin",
|
||||||
|
"admin",
|
||||||
|
}
|
||||||
|
err := config.Save()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = LoadConfig()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "sqlite", configs.Connection)
|
||||||
|
|
||||||
|
err = DbConnection(configs.Connection)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertSqliteSample(t *testing.T) {
|
||||||
|
err := LoadSampleData()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostgresMakeConfig(t *testing.T) {
|
||||||
config := &DbConfig{
|
config := &DbConfig{
|
||||||
"postgres",
|
"postgres",
|
||||||
os.Getenv("DB_HOST"),
|
os.Getenv("DB_HOST"),
|
||||||
|
@ -22,40 +89,139 @@ func TestMakeConfig(t *testing.T) {
|
||||||
os.Getenv("DB_PASS"),
|
os.Getenv("DB_PASS"),
|
||||||
os.Getenv("DB_DATABASE"),
|
os.Getenv("DB_DATABASE"),
|
||||||
5432,
|
5432,
|
||||||
"Testing",
|
"Testing POSTGRES",
|
||||||
|
"This is a test of Statup.io!",
|
||||||
"admin",
|
"admin",
|
||||||
"admin",
|
"admin",
|
||||||
}
|
}
|
||||||
err := config.Save()
|
err := config.Save()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = LoadConfig()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "postgres", configs.Connection)
|
||||||
|
|
||||||
|
err = DbConnection(configs.Connection)
|
||||||
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetConfig(t *testing.T) {
|
func TestInsertPostgresSample(t *testing.T) {
|
||||||
|
err := LoadSampleData()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectCorePostgres(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
core, err = SelectCore()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Testing POSTGRES", core.Name)
|
||||||
|
assert.Equal(t, VERSION, core.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectCore(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
core, err = SelectCore()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Testing POSTGRES", core.Name)
|
||||||
|
assert.Equal(t, VERSION, core.Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUser_Create(t *testing.T) {
|
||||||
|
user := &User{
|
||||||
|
Username: "testuserhere",
|
||||||
|
Password: "password123",
|
||||||
|
Email: "info@testuser.com",
|
||||||
|
}
|
||||||
|
id, err := user.Create()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotZero(t, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOneService_Check(t *testing.T) {
|
||||||
|
service, err := SelectService(1)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Google", service.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService_Create(t *testing.T) {
|
||||||
|
service := &Service{
|
||||||
|
Name: "test service",
|
||||||
|
Domain: "https://google.com",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 1,
|
||||||
|
Port: 0,
|
||||||
|
Type: "https",
|
||||||
|
Method: "GET",
|
||||||
|
}
|
||||||
|
id, err := service.Create()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, int64(5), id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService_Check(t *testing.T) {
|
||||||
|
service, err := SelectService(2)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Statup.io", service.Name)
|
||||||
|
out := service.Check()
|
||||||
|
assert.Equal(t, true, out.Online)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService_Hits(t *testing.T) {
|
||||||
|
service, err := SelectService(1)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
hits, err := service.Hits()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 0, len(hits))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService_AvgTime(t *testing.T) {
|
||||||
|
service, err := SelectService(1)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
avg := service.AvgUptime()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "100.00", avg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService_Online24(t *testing.T) {
|
||||||
|
service, err := SelectService(1)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
online := service.Online24()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, float32(100), online)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService_GraphData(t *testing.T) {
|
||||||
|
t.SkipNow()
|
||||||
|
service, err := SelectService(1)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
data := service.GraphData()
|
||||||
|
assert.Equal(t, "null", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadService_Create(t *testing.T) {
|
||||||
|
service := &Service{
|
||||||
|
Name: "bad service",
|
||||||
|
Domain: "https://9839f83h72gey2g29278hd2od2d.com",
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 10,
|
||||||
|
Port: 0,
|
||||||
|
Type: "https",
|
||||||
|
Method: "GET",
|
||||||
|
}
|
||||||
|
id, err := service.Create()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, int64(6), id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadService_Check(t *testing.T) {
|
||||||
|
service, err := SelectService(4)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "Github Failing Check", service.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
configs, err = LoadConfig()
|
configs, err = LoadConfig()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRun(t *testing.T) {
|
|
||||||
go mainProcess()
|
|
||||||
time.Sleep(15 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServiceUrl(t *testing.T) {
|
|
||||||
req, err := http.NewRequest("GET", "/service/1", nil)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
Router().ServeHTTP(rr, req)
|
|
||||||
|
|
||||||
assert.Equal(t, 3305, len(rr.Body.Bytes()), "should be balance")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test(t *testing.T) {
|
|
||||||
req, err := http.NewRequest("GET", "/dashboard", nil)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
rr := httptest.NewRecorder()
|
|
||||||
Router().ServeHTTP(rr, req)
|
|
||||||
|
|
||||||
assert.Equal(t, 2048, len(rr.Body.Bytes()), "should be balance")
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
"upper.io/db.v3/lib/sqlbuilder"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -16,10 +16,10 @@ import (
|
||||||
//
|
//
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DB *sql.DB
|
DB sqlbuilder.Database
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetDatabase(database *sql.DB) {
|
func SetDatabase(database sqlbuilder.Database) {
|
||||||
DB = database
|
DB = database
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ type Service struct {
|
||||||
type Failure struct {
|
type Failure struct {
|
||||||
Id int
|
Id int
|
||||||
Issue string
|
Issue string
|
||||||
Service int
|
Service int64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
Ago string
|
Ago string
|
||||||
}
|
}
|
||||||
|
|
127
services.go
127
services.go
|
@ -15,51 +15,38 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Id int64
|
Id int64 `db:"id,omitempty" json:"id"`
|
||||||
Name string
|
Name string `db:"name" json:"name"`
|
||||||
Domain string
|
Domain string `db:"domain" json:"domain"`
|
||||||
Expected string
|
Expected string `db:"expected" json:"expected"`
|
||||||
ExpectedStatus int
|
ExpectedStatus int `db:"expected_status" json:"expected_status"`
|
||||||
Interval int
|
Interval int `db:"check_interval" json:"check_interval"`
|
||||||
Method string
|
Type string `db:"check_type" json:"type"`
|
||||||
Port int
|
Method string `db:"method" json:"method"`
|
||||||
CreatedAt time.Time
|
Port int `db:"port" json:"port"`
|
||||||
Data string
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
Online bool
|
Online bool `json:"online"`
|
||||||
Latency float64
|
Latency float64 `json:"latency"`
|
||||||
Online24Hours float32
|
Online24Hours float32 `json:"24_hours_online"`
|
||||||
AvgResponse string
|
AvgResponse string `json:"avg_response"`
|
||||||
TotalUptime string
|
TotalUptime string `json:"uptime"`
|
||||||
Failures []*Failure
|
Failures []*Failure `json:"failures"`
|
||||||
plugin.Service
|
plugin.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectService(id string) *Service {
|
func SelectService(id int64) (Service, error) {
|
||||||
for _, s := range services {
|
var service Service
|
||||||
if id == strconv.Itoa(int(s.Id)) {
|
col := dbSession.Collection("services")
|
||||||
return s
|
res := col.Find("id", id)
|
||||||
}
|
err := res.One(&service)
|
||||||
}
|
return service, err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectAllServices() []*Service {
|
func SelectAllServices() ([]*Service, error) {
|
||||||
var tks []*Service
|
var services []*Service
|
||||||
rows, err := db.Query("SELECT * FROM services ORDER BY id ASC")
|
col := dbSession.Collection("services").Find()
|
||||||
if err != nil {
|
err := col.All(&services)
|
||||||
panic(err)
|
return services, err
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var tk Service
|
|
||||||
err = rows.Scan(&tk.Id, &tk.Name, &tk.Domain, &tk.Method, &tk.Port, &tk.Expected, &tk.ExpectedStatus, &tk.Interval, &tk.CreatedAt)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
tk.Failures = tk.SelectAllFailures()
|
|
||||||
tk.FormatData()
|
|
||||||
tks = append(tks, &tk)
|
|
||||||
}
|
|
||||||
return tks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) FormatData() *Service {
|
func (s *Service) FormatData() *Service {
|
||||||
|
@ -71,16 +58,20 @@ func (s *Service) FormatData() *Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) AvgTime() float64 {
|
func (s *Service) AvgTime() float64 {
|
||||||
total := s.TotalHits()
|
total, _ := s.TotalHits()
|
||||||
sum := s.Sum()
|
if total == 0 {
|
||||||
|
return float64(0)
|
||||||
|
}
|
||||||
|
sum, _ := s.Sum()
|
||||||
avg := sum / float64(total) * 100
|
avg := sum / float64(total) * 100
|
||||||
s.AvgResponse = fmt.Sprintf("%0.0f", avg*10)
|
amount := fmt.Sprintf("%0.0f", avg*10)
|
||||||
return avg
|
val, _ := strconv.ParseFloat(amount, 10)
|
||||||
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Online24() float32 {
|
func (s *Service) Online24() float32 {
|
||||||
total := s.TotalHits()
|
total, _ := s.TotalHits()
|
||||||
failed := s.TotalFailures24Hours()
|
failed, _ := s.TotalFailures24Hours()
|
||||||
if failed == 0 {
|
if failed == 0 {
|
||||||
s.Online24Hours = 100.00
|
s.Online24Hours = 100.00
|
||||||
return s.Online24Hours
|
return s.Online24Hours
|
||||||
|
@ -105,12 +96,13 @@ type GraphJson struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) GraphData() string {
|
func (s *Service) GraphData() string {
|
||||||
var d []*GraphJson
|
var d []GraphJson
|
||||||
for _, h := range s.Hits() {
|
hits, _ := s.Hits()
|
||||||
|
for _, h := range hits {
|
||||||
val := h.CreatedAt
|
val := h.CreatedAt
|
||||||
o := &GraphJson{
|
o := GraphJson{
|
||||||
X: val.String(),
|
X: val.String(),
|
||||||
Y: h.Value * 1000,
|
Y: h.Latency * 1000,
|
||||||
}
|
}
|
||||||
d = append(d, o)
|
d = append(d, o)
|
||||||
}
|
}
|
||||||
|
@ -120,8 +112,8 @@ func (s *Service) GraphData() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) AvgUptime() string {
|
func (s *Service) AvgUptime() string {
|
||||||
failed := s.TotalFailures()
|
failed, _ := s.TotalFailures()
|
||||||
total := s.TotalHits()
|
total, _ := s.TotalHits()
|
||||||
if failed == 0 {
|
if failed == 0 {
|
||||||
s.TotalUptime = "100.00"
|
s.TotalUptime = "100.00"
|
||||||
return s.TotalUptime
|
return s.TotalUptime
|
||||||
|
@ -139,27 +131,26 @@ func (s *Service) AvgUptime() string {
|
||||||
return s.TotalUptime
|
return s.TotalUptime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Service) Delete() {
|
func (u *Service) Delete() error {
|
||||||
stmt, err := db.Prepare("DELETE FROM services WHERE id=$1")
|
col := dbSession.Collection("services")
|
||||||
if err != nil {
|
res := col.Find("id", u.Id)
|
||||||
panic(err)
|
err := res.Delete()
|
||||||
}
|
return err
|
||||||
stmt.Exec(u.Id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Service) Update() {
|
func (u *Service) Update() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Service) Create() int {
|
func (u *Service) Create() (int64, error) {
|
||||||
var lastInsertId int
|
u.CreatedAt = time.Now()
|
||||||
err := db.QueryRow("INSERT INTO services(name, domain, method, port, expected, expected_status, interval, created_at) VALUES($1,$2,$3,$4,$5,$6,$7,NOW()) returning id;", u.Name, u.Domain, u.Method, u.Port, u.Expected, u.ExpectedStatus, u.Interval).Scan(&lastInsertId)
|
col := dbSession.Collection("services")
|
||||||
if err != nil {
|
uuid, err := col.Insert(u)
|
||||||
panic(err)
|
services, _ = SelectAllServices()
|
||||||
|
if uuid == nil {
|
||||||
|
return 0, err
|
||||||
}
|
}
|
||||||
services = SelectAllServices()
|
return uuid.(int64), err
|
||||||
go u.CheckQueue()
|
|
||||||
return lastInsertId
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CountOnline() int {
|
func CountOnline() int {
|
||||||
|
@ -174,17 +165,13 @@ func CountOnline() int {
|
||||||
|
|
||||||
func NewSHA1Hash(n ...int) string {
|
func NewSHA1Hash(n ...int) string {
|
||||||
noRandomCharacters := 32
|
noRandomCharacters := 32
|
||||||
|
|
||||||
if len(n) > 0 {
|
if len(n) > 0 {
|
||||||
noRandomCharacters = n[0]
|
noRandomCharacters = n[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
randString := RandomString(noRandomCharacters)
|
randString := RandomString(noRandomCharacters)
|
||||||
|
|
||||||
hash := sha1.New()
|
hash := sha1.New()
|
||||||
hash.Write([]byte(randString))
|
hash.Write([]byte(randString))
|
||||||
bs := hash.Sum(nil)
|
bs := hash.Sum(nil)
|
||||||
|
|
||||||
return fmt.Sprintf("%x", bs)
|
return fmt.Sprintf("%x", bs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
59
setup.go
59
setup.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-yaml/yaml"
|
"github.com/go-yaml/yaml"
|
||||||
|
"github.com/hunterlong/statup/plugin"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -9,15 +10,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type DbConfig struct {
|
type DbConfig struct {
|
||||||
DbConn string `yaml:"connection"`
|
DbConn string `yaml:"connection"`
|
||||||
DbHost string `yaml:"host"`
|
DbHost string `yaml:"host"`
|
||||||
DbUser string `yaml:"user"`
|
DbUser string `yaml:"user"`
|
||||||
DbPass string `yaml:"password"`
|
DbPass string `yaml:"password"`
|
||||||
DbData string `yaml:"database"`
|
DbData string `yaml:"database"`
|
||||||
DbPort int `yaml:"port"`
|
DbPort int `yaml:"port"`
|
||||||
Project string `yaml:"-"`
|
Project string `yaml:"-"`
|
||||||
Username string `yaml:"-"`
|
Description string `yaml:"-"`
|
||||||
Password string `yaml:"-"`
|
Username string `yaml:"-"`
|
||||||
|
Password string `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -31,6 +33,8 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
project := r.PostForm.Get("project")
|
project := r.PostForm.Get("project")
|
||||||
username := r.PostForm.Get("username")
|
username := r.PostForm.Get("username")
|
||||||
password := r.PostForm.Get("password")
|
password := r.PostForm.Get("password")
|
||||||
|
sample := r.PostForm.Get("sample_data")
|
||||||
|
description := r.PostForm.Get("description")
|
||||||
|
|
||||||
config := &DbConfig{
|
config := &DbConfig{
|
||||||
dbConn,
|
dbConn,
|
||||||
|
@ -40,20 +44,32 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
dbDatabase,
|
dbDatabase,
|
||||||
dbPort,
|
dbPort,
|
||||||
project,
|
project,
|
||||||
|
description,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := config.Save()
|
err := config.Save()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
throw(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configs, err = LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
throw(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = DbConnection(configs.Connection)
|
||||||
|
if err != nil {
|
||||||
|
throw(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sample == "on" {
|
||||||
|
LoadSampleData()
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
mainProcess()
|
mainProcess()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DbConfig) Save() error {
|
func (c *DbConfig) Save() error {
|
||||||
|
@ -79,6 +95,21 @@ func (c *DbConfig) Save() error {
|
||||||
}
|
}
|
||||||
DropDatabase()
|
DropDatabase()
|
||||||
CreateDatabase()
|
CreateDatabase()
|
||||||
db.QueryRow("INSERT INTO core (name, config, api_key, api_secret, version) VALUES($1,$2,$3,$4,$5);", c.Project, "config.yml", NewSHA1Hash(5), NewSHA1Hash(10), VERSION).Scan()
|
|
||||||
|
newCore := Core{
|
||||||
|
c.Project,
|
||||||
|
c.Description,
|
||||||
|
"config.yml",
|
||||||
|
NewSHA1Hash(5),
|
||||||
|
NewSHA1Hash(10),
|
||||||
|
VERSION,
|
||||||
|
[]plugin.Info{},
|
||||||
|
[]PluginJSON{},
|
||||||
|
[]PluginSelect{},
|
||||||
|
}
|
||||||
|
|
||||||
|
col := dbSession.Collection("core")
|
||||||
|
_, err = col.Insert(newCore)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
DROP table core;
|
DROP table core;
|
||||||
DROP table hits;
|
DROP table hits;
|
||||||
DROP table failures;
|
DROP table failures;
|
||||||
DROP table services;
|
DROP table users;
|
||||||
DROP table users;
|
DROP table services;
|
|
@ -0,0 +1,47 @@
|
||||||
|
CREATE TABLE core (
|
||||||
|
name VARCHAR(50),
|
||||||
|
description text,
|
||||||
|
config VARCHAR(50),
|
||||||
|
api_key VARCHAR(50),
|
||||||
|
api_secret VARCHAR(50),
|
||||||
|
version VARCHAR(50)
|
||||||
|
);
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
username VARCHAR(50),
|
||||||
|
password text,
|
||||||
|
email text,
|
||||||
|
api_key VARCHAR(50),
|
||||||
|
api_secret VARCHAR(50),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
INDEX (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE services (
|
||||||
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
name VARCHAR(50),
|
||||||
|
domain text,
|
||||||
|
check_type text,
|
||||||
|
method VARCHAR(50),
|
||||||
|
port INT(6),
|
||||||
|
expected text,
|
||||||
|
expected_status INT(6),
|
||||||
|
check_interval int(11),
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
INDEX (id)
|
||||||
|
);
|
||||||
|
CREATE TABLE hits (
|
||||||
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
service INTEGER NOT NULL,
|
||||||
|
latency float,
|
||||||
|
created_at TIMESTAMP,
|
||||||
|
INDEX (id, service),
|
||||||
|
FOREIGN KEY (service) REFERENCES services(id)
|
||||||
|
);
|
||||||
|
CREATE TABLE failures (
|
||||||
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
issue text,
|
||||||
|
service INTEGER NOT NULL,
|
||||||
|
created_at TIMESTAMP,
|
||||||
|
INDEX (id, service),
|
||||||
|
FOREIGN KEY (service) REFERENCES services(id)
|
||||||
|
);
|
|
@ -0,0 +1,48 @@
|
||||||
|
CREATE TABLE core (
|
||||||
|
name text,
|
||||||
|
description text,
|
||||||
|
config text,
|
||||||
|
api_key text,
|
||||||
|
api_secret text,
|
||||||
|
version text
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE users (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
username text,
|
||||||
|
password text,
|
||||||
|
email text,
|
||||||
|
api_key text,
|
||||||
|
api_secret text,
|
||||||
|
created_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE services (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name text,
|
||||||
|
domain text,
|
||||||
|
check_type text,
|
||||||
|
method text,
|
||||||
|
port integer,
|
||||||
|
expected text,
|
||||||
|
expected_status integer,
|
||||||
|
check_interval integer,
|
||||||
|
created_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE hits (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
latency float,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME zone
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE failures (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
issue text,
|
||||||
|
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME zone
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_hits ON hits(service);
|
||||||
|
CREATE INDEX idx_failures ON failures(service);
|
|
@ -0,0 +1,48 @@
|
||||||
|
CREATE TABLE core (
|
||||||
|
name text,
|
||||||
|
description text,
|
||||||
|
config text,
|
||||||
|
api_key text,
|
||||||
|
api_secret text,
|
||||||
|
version text
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE users (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
username text,
|
||||||
|
password text,
|
||||||
|
email text,
|
||||||
|
api_key text,
|
||||||
|
api_secret text,
|
||||||
|
created_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE services (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name text,
|
||||||
|
domain text,
|
||||||
|
check_type text,
|
||||||
|
method text,
|
||||||
|
port integer,
|
||||||
|
expected text,
|
||||||
|
expected_status integer,
|
||||||
|
check_interval integer,
|
||||||
|
created_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE hits (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
latency float,
|
||||||
|
created_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE failures (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
issue text,
|
||||||
|
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
created_at TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_hits ON hits(service);
|
||||||
|
CREATE INDEX idx_failures ON failures(service);
|
60
sql/up.sql
60
sql/up.sql
|
@ -1,60 +0,0 @@
|
||||||
CREATE TABLE core (
|
|
||||||
name text,
|
|
||||||
config text,
|
|
||||||
api_key text,
|
|
||||||
api_secret text,
|
|
||||||
version text,
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE users (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
username text,
|
|
||||||
password text,
|
|
||||||
key text,
|
|
||||||
secret text,
|
|
||||||
created_at TIMESTAMP WITHOUT TIME zone
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE services (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name text,
|
|
||||||
domain text,
|
|
||||||
method text,
|
|
||||||
port integer,
|
|
||||||
expected text,
|
|
||||||
expected_status integer,
|
|
||||||
interval integer,
|
|
||||||
created_at TIMESTAMP WITHOUT TIME zone
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE hits (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
latency float,
|
|
||||||
created_at TIMESTAMP WITHOUT TIME zone
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE failures (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
issue text,
|
|
||||||
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
created_at TIMESTAMP WITHOUT TIME zone
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_hits ON hits(service);
|
|
||||||
CREATE INDEX idx_failures ON failures(service);
|
|
||||||
|
|
||||||
INSERT INTO users (id, username, password, created_at) VALUES (1, 'admin', '$2a$14$sBO5VDKiGPNUa3IUSMRX.OJNIbw/VM5dXOzTjlsjvG6qA987Lfzga', NOW());
|
|
||||||
|
|
||||||
INSERT INTO services (id, name, domain, method, port, expected, expected_status, interval, created_at) VALUES (1, 'Statup Demo', 'https://demo.statup.io', 'https', 0, '', 200, 5, NOW());
|
|
||||||
|
|
||||||
INSERT INTO services (id, name, domain, method, port, expected, expected_status, interval, created_at) VALUES (2, 'Github', 'https://github.com', 'https', 0, '', 200, 10, NOW());
|
|
||||||
|
|
||||||
INSERT INTO services (id, name, domain, method, port, expected, expected_status, interval, created_at) VALUES (3, 'Santa Monica', 'https://www.santamonica.com', 'https', 0, '', 200, 30, NOW());
|
|
||||||
|
|
||||||
INSERT INTO services (id, name, domain, method, port, expected, expected_status, interval, created_at) VALUES (4, 'Example JSON', 'https://jsonplaceholder.typicode.com/posts/42', 'https', 0, 'userId', 200, 5, NOW());
|
|
||||||
|
|
||||||
INSERT INTO services (id, name, domain, method, port, expected, expected_status, interval, created_at) VALUES (5, 'Token Balance API', 'https://api.tokenbalance.com/token/0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0/0x5c98dc37c0b3ef75476eb6b8ef0d564f7c6af6ae', 'https', 0, 'broken', 200, 8, NOW());
|
|
||||||
|
|
||||||
INSERT INTO services (id, name, domain, method, port, expected, expected_status, interval, created_at) VALUES (6, 'Example JSON 2', 'https://jsonplaceholder.typicode.com/posts/42', 'https', 0, 'commodi ullam sint et excepturi error explicabo praesentium voluptas', 200, 13, NOW());
|
|
||||||
|
|
78
users.go
78
users.go
|
@ -2,58 +2,62 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Id int64
|
Id int64 `db:"id,omitempty" json:"id"`
|
||||||
Username string
|
Username string `db:"username" json:"username"`
|
||||||
Password string
|
Password string `db:"password" json:"-"`
|
||||||
Email string
|
Email string `db:"email" json:"-"`
|
||||||
|
ApiKey string `db:"api_key" json:"api_key"`
|
||||||
|
ApiSecret string `db:"api_secret" json:"-"`
|
||||||
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectUser(username string) User {
|
func SelectUser(id int64) (*User, error) {
|
||||||
var user User
|
var user User
|
||||||
rows, err := db.Query("SELECT id, username, password FROM users WHERE username=$1", username)
|
col := dbSession.Collection("users")
|
||||||
if err != nil {
|
res := col.Find("id", id)
|
||||||
panic(err)
|
err := res.One(&user)
|
||||||
}
|
return &user, err
|
||||||
for rows.Next() {
|
|
||||||
err = rows.Scan(&user.Id, &user.Username, &user.Password)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return user
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) Create() int {
|
func SelectUsername(username string) (*User, error) {
|
||||||
|
var user User
|
||||||
|
col := dbSession.Collection("users")
|
||||||
|
res := col.Find("username", username)
|
||||||
|
err := res.One(&user)
|
||||||
|
return &user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) Create() (int64, error) {
|
||||||
|
u.CreatedAt = time.Now()
|
||||||
password := HashPassword(u.Password)
|
password := HashPassword(u.Password)
|
||||||
var lastInsertId int
|
u.Password = password
|
||||||
db.QueryRow("INSERT INTO users(username,password,created_at) VALUES($1,$2,NOW()) returning id;", u.Username, password).Scan(&lastInsertId)
|
u.ApiKey = NewSHA1Hash(5)
|
||||||
return lastInsertId
|
u.ApiSecret = NewSHA1Hash(10)
|
||||||
|
col := dbSession.Collection("users")
|
||||||
|
uuid, err := col.Insert(u)
|
||||||
|
if uuid == nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uuid.(int64), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectAllUsers() []User {
|
func SelectAllUsers() ([]User, error) {
|
||||||
var users []User
|
var users []User
|
||||||
rows, err := db.Query("SELECT id, username, password FROM users ORDER BY id ASC")
|
col := dbSession.Collection("users").Find()
|
||||||
if err != nil {
|
err := col.All(&users)
|
||||||
panic(err)
|
return users, err
|
||||||
}
|
|
||||||
for rows.Next() {
|
|
||||||
var user User
|
|
||||||
err = rows.Scan(&user.Id, &user.Username, &user.Password)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
users = append(users, user)
|
|
||||||
}
|
|
||||||
return users
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AuthUser(username, password string) (User, bool) {
|
func AuthUser(username, password string) (*User, bool) {
|
||||||
var user User
|
|
||||||
var auth bool
|
var auth bool
|
||||||
user = SelectUser(username)
|
user, err := SelectUsername(username)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
if CheckHash(password, user.Password) {
|
if CheckHash(password, user.Password) {
|
||||||
auth = true
|
auth = true
|
||||||
}
|
}
|
||||||
|
|
131
web.go
131
web.go
|
@ -15,28 +15,40 @@ var (
|
||||||
session *sessions.CookieStore
|
session *sessions.CookieStore
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cookieKey = "apizer_auth"
|
||||||
|
)
|
||||||
|
|
||||||
func Router() *mux.Router {
|
func Router() *mux.Router {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.Handle("/", http.HandlerFunc(IndexHandler))
|
r.Handle("/", http.HandlerFunc(IndexHandler))
|
||||||
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(cssBox.HTTPBox())))
|
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(cssBox.HTTPBox())))
|
||||||
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(jsBox.HTTPBox())))
|
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(jsBox.HTTPBox())))
|
||||||
r.Handle("/setup", http.HandlerFunc(SetupHandler))
|
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
|
||||||
r.Handle("/setup/save", http.HandlerFunc(ProcessSetupHandler))
|
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
|
||||||
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler))
|
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET")
|
||||||
r.Handle("/login", http.HandlerFunc(LoginHandler))
|
r.Handle("/dashboard", http.HandlerFunc(LoginHandler)).Methods("POST")
|
||||||
r.Handle("/logout", http.HandlerFunc(LogoutHandler))
|
r.Handle("/logout", http.HandlerFunc(LogoutHandler))
|
||||||
r.Handle("/services", http.HandlerFunc(ServicesHandler))
|
r.Handle("/services", http.HandlerFunc(ServicesHandler)).Methods("GET")
|
||||||
r.Handle("/services", http.HandlerFunc(CreateServiceHandler)).Methods("POST")
|
r.Handle("/services", http.HandlerFunc(CreateServiceHandler)).Methods("POST")
|
||||||
r.Handle("/service/{id}", http.HandlerFunc(ServicesViewHandler))
|
r.Handle("/service/{id}", http.HandlerFunc(ServicesViewHandler))
|
||||||
r.Handle("/service/{id}", http.HandlerFunc(ServicesUpdateHandler)).Methods("POST")
|
r.Handle("/service/{id}", http.HandlerFunc(ServicesUpdateHandler)).Methods("POST")
|
||||||
r.Handle("/service/{id}/edit", http.HandlerFunc(ServicesViewHandler))
|
r.Handle("/service/{id}/edit", http.HandlerFunc(ServicesViewHandler))
|
||||||
r.Handle("/service/{id}/delete", http.HandlerFunc(ServicesDeleteHandler))
|
r.Handle("/service/{id}/delete", http.HandlerFunc(ServicesDeleteHandler))
|
||||||
r.Handle("/users", http.HandlerFunc(UsersHandler))
|
r.Handle("/service/{id}/badge.svg", http.HandlerFunc(ServicesBadgeHandler))
|
||||||
|
r.Handle("/users", http.HandlerFunc(UsersHandler)).Methods("GET")
|
||||||
r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST")
|
r.Handle("/users", http.HandlerFunc(CreateUserHandler)).Methods("POST")
|
||||||
r.Handle("/settings", http.HandlerFunc(PluginsHandler))
|
r.Handle("/settings", http.HandlerFunc(PluginsHandler))
|
||||||
r.Handle("/plugins/download/{name}", http.HandlerFunc(PluginsDownloadHandler))
|
r.Handle("/plugins/download/{name}", http.HandlerFunc(PluginsDownloadHandler))
|
||||||
r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST")
|
r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST")
|
||||||
r.Handle("/help", http.HandlerFunc(HelpHandler))
|
r.Handle("/help", http.HandlerFunc(HelpHandler))
|
||||||
|
|
||||||
|
r.Handle("/api", http.HandlerFunc(ApiIndexHandler))
|
||||||
|
r.Handle("/api/services", http.HandlerFunc(ApiAllServicesHandler))
|
||||||
|
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceHandler)).Methods("GET")
|
||||||
|
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceUpdateHandler)).Methods("POST")
|
||||||
|
r.Handle("/api/users", http.HandlerFunc(ApiAllUsersHandler))
|
||||||
|
r.Handle("/api/users/{id}", http.HandlerFunc(ApiUserHandler))
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,20 +76,18 @@ func RunHTTPServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := store.Get(r, "apizer_auth")
|
session, _ := store.Get(r, cookieKey)
|
||||||
session.Values["authenticated"] = false
|
session.Values["authenticated"] = false
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := store.Get(r, "apizer_auth")
|
session, _ := store.Get(r, cookieKey)
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
username := r.PostForm.Get("username")
|
username := r.PostForm.Get("username")
|
||||||
password := r.PostForm.Get("password")
|
password := r.PostForm.Get("password")
|
||||||
user, auth := AuthUser(username, password)
|
_, auth := AuthUser(username, password)
|
||||||
fmt.Println(user)
|
|
||||||
fmt.Println(auth)
|
|
||||||
if auth {
|
if auth {
|
||||||
session.Values["authenticated"] = true
|
session.Values["authenticated"] = true
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
|
@ -89,24 +99,8 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//func AuthenticateHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// r.ParseForm()
|
|
||||||
// key := r.PostForm.Get("key")
|
|
||||||
// secret := r.PostForm.Get("secret")
|
|
||||||
// token := SelectToken(key, secret)
|
|
||||||
// if token.Id != 0 {
|
|
||||||
// go token.Hit(r)
|
|
||||||
// w.WriteHeader(200)
|
|
||||||
// w.Header().Set("Content-Type", "plain/text")
|
|
||||||
// fmt.Fprintln(w, token.Id)
|
|
||||||
// } else {
|
|
||||||
// w.WriteHeader(502)
|
|
||||||
// w.Header().Set("Content-Type", "plain/text")
|
|
||||||
// fmt.Fprintln(w, "bad")
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
|
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Println("creating user")
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
username := r.PostForm.Get("username")
|
username := r.PostForm.Get("username")
|
||||||
password := r.PostForm.Get("password")
|
password := r.PostForm.Get("password")
|
||||||
|
@ -114,11 +108,15 @@ func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
Username: username,
|
Username: username,
|
||||||
Password: password,
|
Password: password,
|
||||||
}
|
}
|
||||||
user.Create()
|
_, err := user.Create()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Println("service adding")
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
name := r.PostForm.Get("name")
|
name := r.PostForm.Get("name")
|
||||||
domain := r.PostForm.Get("domain")
|
domain := r.PostForm.Get("domain")
|
||||||
|
@ -127,7 +125,9 @@ func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
status, _ := strconv.Atoi(r.PostForm.Get("expected_status"))
|
status, _ := strconv.Atoi(r.PostForm.Get("expected_status"))
|
||||||
interval, _ := strconv.Atoi(r.PostForm.Get("interval"))
|
interval, _ := strconv.Atoi(r.PostForm.Get("interval"))
|
||||||
|
|
||||||
service := &Service{
|
fmt.Println(r.PostForm)
|
||||||
|
|
||||||
|
service := Service{
|
||||||
Name: name,
|
Name: name,
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
Method: method,
|
Method: method,
|
||||||
|
@ -138,7 +138,10 @@ func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
fmt.Println(service)
|
fmt.Println(service)
|
||||||
|
|
||||||
service.Create()
|
_, err := service.Create()
|
||||||
|
if err != nil {
|
||||||
|
go service.CheckQueue()
|
||||||
|
}
|
||||||
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,29 +171,30 @@ type dashboard struct {
|
||||||
Core *Core
|
Core *Core
|
||||||
CountOnline int
|
CountOnline int
|
||||||
CountServices int
|
CountServices int
|
||||||
Count24Failures int
|
Count24Failures uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := store.Get(r, "apizer_auth")
|
session, _ := store.Get(r, cookieKey)
|
||||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||||
tmpl := Parse("login.html")
|
tmpl := Parse("login.html")
|
||||||
tmpl.Execute(w, nil)
|
tmpl.Execute(w, nil)
|
||||||
} else {
|
} else {
|
||||||
tmpl := Parse("dashboard.html")
|
tmpl := Parse("dashboard.html")
|
||||||
out := dashboard{services, core, CountOnline(), len(services), CountFailures()}
|
fails, _ := CountFailures()
|
||||||
|
out := dashboard{services, core, CountOnline(), len(services), fails}
|
||||||
tmpl.Execute(w, out)
|
tmpl.Execute(w, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type serviceHandler struct {
|
type serviceHandler struct {
|
||||||
Service *Service
|
Service Service
|
||||||
Auth bool
|
Auth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServicesHandler(w http.ResponseWriter, r *http.Request) {
|
func ServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := store.Get(r, "apizer_auth")
|
session, _ := store.Get(r, cookieKey)
|
||||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
|
@ -200,21 +204,21 @@ func ServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ServicesDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
func ServicesDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := store.Get(r, "apizer_auth")
|
session, _ := store.Get(r, cookieKey)
|
||||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
service := SelectService(vars["id"])
|
service, _ := SelectService(StringInt(vars["id"]))
|
||||||
|
|
||||||
service.Delete()
|
service.Delete()
|
||||||
services = SelectAllServices()
|
services, _ = SelectAllServices()
|
||||||
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsAuthenticated(r *http.Request) bool {
|
func IsAuthenticated(r *http.Request) bool {
|
||||||
session, _ := store.Get(r, "apizer_auth")
|
session, _ := store.Get(r, cookieKey)
|
||||||
if session.Values["authenticated"] == nil {
|
if session.Values["authenticated"] == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -298,15 +302,31 @@ func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
//service := SelectService(vars["id"])
|
//service := SelectService(vars["id"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ServicesBadgeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service, _ := SelectService(StringInt(vars["id"]))
|
||||||
|
|
||||||
|
var badge []byte
|
||||||
|
if service.Online {
|
||||||
|
badge = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="104" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="104" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#4c1" d="M54 0h50v20H54z"/><path fill="url(#b)" d="M0 0h104v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">`+service.Name+`</text><text x="28" y="14">`+service.Name+`</text><text x="78" y="15" fill="#010101" fill-opacity=".3">online</text><text x="78" y="14">online</text></g></svg>`)
|
||||||
|
} else {
|
||||||
|
badge = []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="99" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h54v20H0z"/><path fill="#e05d44" d="M54 0h45v20H54z"/><path fill="url(#b)" d="M0 0h99v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="28" y="15" fill="#010101" fill-opacity=".3">`+service.Name+`</text><text x="28" y="14">`+service.Name+`</text><text x="75.5" y="15" fill="#010101" fill-opacity=".3">offline</text><text x="75.5" y="14">offline</text></g></svg>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "image/svg+xml")
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
|
||||||
|
w.Write(badge)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func ServicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
func ServicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
auth := IsAuthenticated(r)
|
auth := IsAuthenticated(r)
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
service := SelectService(vars["id"])
|
service, _ := SelectService(StringInt(vars["id"]))
|
||||||
|
|
||||||
tmpl := Parse("service.html")
|
tmpl := Parse("service.html")
|
||||||
|
|
||||||
serve := &serviceHandler{service, auth}
|
serve := &serviceHandler{service, auth}
|
||||||
|
|
||||||
tmpl.Execute(w, serve)
|
tmpl.Execute(w, serve)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,28 +373,13 @@ func ParsePlugins(file string) *template.Template {
|
||||||
}
|
}
|
||||||
|
|
||||||
func UsersHandler(w http.ResponseWriter, r *http.Request) {
|
func UsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := store.Get(r, "apizer_auth")
|
fmt.Println("viewing user")
|
||||||
|
session, _ := store.Get(r, cookieKey)
|
||||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tmpl := Parse("users.html")
|
tmpl := Parse("users.html")
|
||||||
tmpl.Execute(w, SelectAllUsers())
|
users, _ := SelectAllUsers()
|
||||||
}
|
tmpl.Execute(w, users)
|
||||||
|
|
||||||
func PermissionsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
session, _ := store.Get(r, "apizer_auth")
|
|
||||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
permsFile, err := tmplBox.String("permissions.html")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
permsTmpl, err := template.New("message").Parse(permsFile)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
permsTmpl.Execute(w, SelectAllUsers())
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServiceUrl(t *testing.T) {
|
||||||
|
req, err := http.NewRequest("GET", "/service/1", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
Router().ServeHTTP(rr, req)
|
||||||
|
assert.Equal(t, 3355, len(rr.Body.Bytes()), "should be balance")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiAllServiceUrl(t *testing.T) {
|
||||||
|
req, err := http.NewRequest("GET", "/api/services", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
Router().ServeHTTP(rr, req)
|
||||||
|
var data []Service
|
||||||
|
json.Unmarshal(rr.Body.Bytes(), &data)
|
||||||
|
assert.Equal(t, "Google", data[0].Name, "should be balance")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiServiceUrl(t *testing.T) {
|
||||||
|
req, err := http.NewRequest("GET", "/api/services/1", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
Router().ServeHTTP(rr, req)
|
||||||
|
var data Service
|
||||||
|
json.Unmarshal(rr.Body.Bytes(), &data)
|
||||||
|
assert.Equal(t, "Google", data.Name, "should be balance")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiServiceUpdateUrl(t *testing.T) {
|
||||||
|
payload := []byte(`{"name":"test product - updated name","price":11.22}`)
|
||||||
|
req, err := http.NewRequest("POST", "/api/services/1", bytes.NewBuffer(payload))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
Router().ServeHTTP(rr, req)
|
||||||
|
var data Service
|
||||||
|
json.Unmarshal(rr.Body.Bytes(), &data)
|
||||||
|
assert.Equal(t, "Google", data.Name, "should be balance")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiUserUrl(t *testing.T) {
|
||||||
|
req, err := http.NewRequest("GET", "/api/users/1", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
Router().ServeHTTP(rr, req)
|
||||||
|
var data User
|
||||||
|
json.Unmarshal(rr.Body.Bytes(), &data)
|
||||||
|
assert.Equal(t, "admin", data.Username, "should be balance")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiAllUsersUrl(t *testing.T) {
|
||||||
|
req, err := http.NewRequest("GET", "/api/users", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
Router().ServeHTTP(rr, req)
|
||||||
|
var data []User
|
||||||
|
json.Unmarshal(rr.Body.Bytes(), &data)
|
||||||
|
assert.Equal(t, "admin", data[0].Username, "should be balance")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDashboardHandler(t *testing.T) {
|
||||||
|
req, err := http.NewRequest("GET", "/dashboard", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
Router().ServeHTTP(rr, req)
|
||||||
|
assert.Equal(t, 2095, len(rr.Body.Bytes()), "should be balance")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoginHandler(t *testing.T) {
|
||||||
|
form := url.Values{}
|
||||||
|
form.Add("username", "admin")
|
||||||
|
form.Add("password", "admin")
|
||||||
|
req, err := http.NewRequest("POST", "/dashboard", strings.NewReader(form.Encode()))
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
Router().ServeHTTP(rr, req)
|
||||||
|
assert.Equal(t, 303, rr.Result().StatusCode, "should be balance")
|
||||||
|
}
|
Loading…
Reference in New Issue