mirror of https://github.com/statping/statping
updates
parent
e265765c75
commit
9a237defe7
|
@ -1,3 +1,5 @@
|
|||
.idea
|
||||
rice-box.go
|
||||
config.yml
|
||||
config.yml
|
||||
statup.db
|
||||
plugins/*.so
|
|
@ -12,6 +12,9 @@ sudo: required
|
|||
|
||||
services:
|
||||
- docker
|
||||
- postgresql
|
||||
- mysql
|
||||
- mongodb
|
||||
|
||||
env:
|
||||
- VERSION=0.12
|
||||
|
@ -37,14 +40,12 @@ deploy:
|
|||
- "build/statup-windows-x32.exe"
|
||||
skip_cleanup: true
|
||||
|
||||
services:
|
||||
- postgresql
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then travis_wait 30 docker pull karalabe/xgo-latest; fi
|
||||
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
|
||||
|
||||
before_script:
|
||||
- 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() {
|
||||
services = SelectAllServices()
|
||||
services, _ = SelectAllServices()
|
||||
for _, v := range services {
|
||||
obj := v
|
||||
go obj.CheckQueue()
|
||||
|
@ -18,12 +18,15 @@ func CheckServices() {
|
|||
|
||||
func (s *Service) CheckQueue() {
|
||||
s.Check()
|
||||
if s.Interval < 1 {
|
||||
s.Interval = 1
|
||||
}
|
||||
fmt.Printf(" Service: %v | Online: %v | Latency: %v\n", s.Name, s.Online, s.Latency)
|
||||
time.Sleep(time.Duration(s.Interval) * time.Second)
|
||||
s.CheckQueue()
|
||||
}
|
||||
|
||||
func (s *Service) Check() {
|
||||
func (s *Service) Check() *Service {
|
||||
t1 := time.Now()
|
||||
client := http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
|
@ -33,7 +36,7 @@ func (s *Service) Check() {
|
|||
s.Latency = t2.Sub(t1).Seconds()
|
||||
if err != nil {
|
||||
s.Failure(fmt.Sprintf("HTTP Error %v", err))
|
||||
return
|
||||
return s
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if s.Expected != "" {
|
||||
|
@ -41,23 +44,40 @@ func (s *Service) Check() {
|
|||
match, _ := regexp.MatchString(s.Expected, string(contents))
|
||||
if !match {
|
||||
s.Failure(fmt.Sprintf("HTTP Response Body did not match '%v'", s.Expected))
|
||||
return
|
||||
return s
|
||||
}
|
||||
}
|
||||
if s.ExpectedStatus != response.StatusCode {
|
||||
s.Failure(fmt.Sprintf("HTTP Status Code %v did not match %v", response.StatusCode, s.ExpectedStatus))
|
||||
return
|
||||
return s
|
||||
}
|
||||
s.Online = true
|
||||
s.Record(response)
|
||||
return s
|
||||
}
|
||||
|
||||
type HitData struct {
|
||||
Latency float64
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
type FailureData struct {
|
||||
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)
|
||||
}
|
||||
|
|
22
core.go
22
core.go
|
@ -6,29 +6,23 @@ import (
|
|||
)
|
||||
|
||||
type Core struct {
|
||||
Name string
|
||||
Config string
|
||||
Key string
|
||||
Secret string
|
||||
Version string
|
||||
Name string `db:"name"`
|
||||
Description string `db:"description"`
|
||||
Config string `db:"config"`
|
||||
ApiKey string `db:"api_key"`
|
||||
ApiSecret string `db:"api_secret"`
|
||||
Version string `db:"version"`
|
||||
Plugins []plugin.Info
|
||||
Repos []PluginJSON
|
||||
PluginFields []PluginSelect
|
||||
}
|
||||
|
||||
|
||||
func SelectCore() (*Core, error) {
|
||||
var core Core
|
||||
rows, err := db.Query("SELECT * FROM core")
|
||||
err := dbSession.Collection("core").Find().One(&core)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for rows.Next() {
|
||||
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))
|
||||
store = sessions.NewCookieStore([]byte(core.ApiSecret))
|
||||
return &core, err
|
||||
}
|
||||
|
|
170
database.go
170
database.go
|
@ -1,38 +1,59 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/plugin"
|
||||
_ "github.com/lib/pq"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"math/rand"
|
||||
"time"
|
||||
"strings"
|
||||
"upper.io/db.v3/lib/sqlbuilder"
|
||||
"upper.io/db.v3/mysql"
|
||||
"upper.io/db.v3/postgresql"
|
||||
"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 {
|
||||
var err error
|
||||
var dbInfo string
|
||||
if dbType=="sqlite3" {
|
||||
dbInfo = "./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)
|
||||
if dbType == "sqlite" {
|
||||
sqliteSettings = sqlite.ConnectionURL{
|
||||
Database: "statup.db",
|
||||
}
|
||||
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 {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//stmt, err := db.Prepare("CREATE database statup;")
|
||||
//if err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
//stmt.Exec()
|
||||
|
||||
plugin.SetDatabase(db)
|
||||
//dbSession.SetLogging(true)
|
||||
dbServer = dbType
|
||||
plugin.SetDatabase(dbSession)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -45,33 +66,104 @@ func UpgradeDatabase() {
|
|||
}
|
||||
fmt.Println("Upgrading Database...")
|
||||
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() {
|
||||
fmt.Println("Dropping Tables...")
|
||||
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() {
|
||||
fmt.Println("Creating Tables...")
|
||||
VERSION = "1.1.1"
|
||||
up, _ := sqlBox.String("up.sql")
|
||||
db.QueryRow(up).Scan()
|
||||
sql := "postgres_up.sql"
|
||||
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()
|
||||
//db.QueryRow("INSERT INTO core (secret, version) VALUES ($1, $2);", secret, VERSION).Scan()
|
||||
fmt.Println("Database Created")
|
||||
//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
|
||||
|
||||
import (
|
||||
"github.com/ararog/timeago"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Failure struct {
|
||||
Id int
|
||||
Issue string
|
||||
Service int
|
||||
CreatedAt time.Time
|
||||
Id int `db:"id,omitempty"`
|
||||
Issue string `db:"issue"`
|
||||
Service int64 `db:"service"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
Ago string
|
||||
}
|
||||
|
||||
func (s *Service) SelectAllFailures() []*Failure {
|
||||
var tks []*Failure
|
||||
rows, err := db.Query("SELECT * FROM failures WHERE service=$1 ORDER BY id DESC LIMIT 10", s.Id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
func (s *Service) CreateFailure(data FailureData) (int64, error) {
|
||||
fail := &Failure{
|
||||
Issue: data.Issue,
|
||||
Service: s.Id,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
for rows.Next() {
|
||||
var tk Failure
|
||||
err = rows.Scan(&tk.Id, &tk.Issue, &tk.Service, &tk.CreatedAt)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tk.Ago, _ = timeago.TimeAgoWithTime(time.Now(), tk.CreatedAt)
|
||||
|
||||
tks = append(tks, &tk)
|
||||
s.Failures = append(s.Failures, fail)
|
||||
col := dbSession.Collection("failures")
|
||||
uuid, err := col.Insert(fail)
|
||||
if uuid == nil {
|
||||
return 0, err
|
||||
}
|
||||
return tks
|
||||
return uuid.(int64), err
|
||||
}
|
||||
|
||||
func CountFailures() int {
|
||||
var amount int
|
||||
db.QueryRow("SELECT COUNT(id) FROM failures;").Scan(&amount)
|
||||
return amount
|
||||
func (s *Service) SelectAllFailures() ([]*Failure, error) {
|
||||
var fails []*Failure
|
||||
col := dbSession.Collection("failures").Find("session", s.Id)
|
||||
err := col.All(&fails)
|
||||
return fails, err
|
||||
}
|
||||
|
||||
func (s *Service) TotalFailures() int {
|
||||
var amount int
|
||||
db.QueryRow("SELECT COUNT(id) FROM failures WHERE service=$1;", s.Id).Scan(&amount)
|
||||
return amount
|
||||
func CountFailures() (uint64, error) {
|
||||
col := dbSession.Collection("failures").Find()
|
||||
amount, err := col.Count()
|
||||
return amount, err
|
||||
}
|
||||
|
||||
func (s *Service) TotalFailures24Hours() int {
|
||||
var amount int
|
||||
t := time.Now()
|
||||
x := t.AddDate(0, 0, -1)
|
||||
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) TotalFailures() (uint64, error) {
|
||||
col := dbSession.Collection("failures").Find("service", s.Id)
|
||||
amount, err := col.Count()
|
||||
return amount, err
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
type Hit struct {
|
||||
Id int
|
||||
Metric int
|
||||
Value float64
|
||||
CreatedAt time.Time
|
||||
Id int `db:"id,omitempty"`
|
||||
Service int64 `db:"service"`
|
||||
Latency float64 `db:"latency"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
func (s *Service) Hits() []Hit {
|
||||
var tks []Hit
|
||||
rows, err := db.Query("SELECT * FROM hits WHERE service=$1 ORDER BY id DESC LIMIT 256", s.Id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
func (s *Service) CreateHit(d HitData) (int64, error) {
|
||||
h := Hit{
|
||||
Service: s.Id,
|
||||
Latency: d.Latency,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
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)
|
||||
col := dbSession.Collection("hits")
|
||||
uuid, err := col.Insert(h)
|
||||
if uuid == nil {
|
||||
return 0, err
|
||||
}
|
||||
return tks
|
||||
return uuid.(int64), err
|
||||
}
|
||||
|
||||
func (s *Service) SelectHitsGroupBy(group string) []Hit {
|
||||
var tks []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)
|
||||
if err != nil {
|
||||
panic(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) Hits() ([]Hit, error) {
|
||||
var hits []Hit
|
||||
col := dbSession.Collection("hits").Find("service", s.Id)
|
||||
err := col.All(&hits)
|
||||
return hits, err
|
||||
}
|
||||
|
||||
func (s *Service) TotalHits() int {
|
||||
var amount int
|
||||
db.QueryRow("SELECT COUNT(id) FROM hits WHERE service=$1;", s.Id).Scan(&amount)
|
||||
return amount
|
||||
func (s *Service) SelectHitsGroupBy(group string) ([]Hit, error) {
|
||||
var hits []Hit
|
||||
col := dbSession.Collection("hits").Find("service", s.Id)
|
||||
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
|
||||
db.QueryRow("SELECT SUM(latency) FROM hits WHERE service=$1;", s.Id).Scan(&amount)
|
||||
return amount
|
||||
hits, err := s.Hits()
|
||||
for _, h := range hits {
|
||||
amount += h.Latency
|
||||
}
|
||||
return amount, err
|
||||
}
|
||||
|
|
|
@ -53,6 +53,10 @@ HTML,BODY {
|
|||
background-color: white !important;
|
||||
}
|
||||
|
||||
.online_list {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-decoration: none;
|
||||
margin-top: 20px;
|
||||
|
@ -86,12 +90,28 @@ HTML,BODY {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-body H3 A {
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
HTML,BODY {
|
||||
background-color: #efefef;
|
||||
margin: 0px 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-left: 0px;
|
||||
margin-top: 0px;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
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>
|
||||
|
||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -12,14 +12,32 @@
|
|||
</head>
|
||||
<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="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">
|
||||
|
||||
{{ range .Services }}
|
||||
<div class="col-12 mb-4">
|
||||
<div class="mb-4">
|
||||
<div class="card">
|
||||
<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="col-4">
|
||||
<span class="lg_number">{{.Online24Hours}}%</span>
|
||||
<span class="lg_number">{{.Online24}}%</span>
|
||||
Online last 24 Hours
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.AvgResponse}}ms</span>
|
||||
<span class="lg_number">{{.AvgTime}}ms</span>
|
||||
Average Response
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.TotalUptime}}%</span>
|
||||
<span class="lg_number">{{.AvgUptime}}%</span>
|
||||
Total Uptime
|
||||
</div>
|
||||
</div>
|
||||
|
@ -83,7 +101,7 @@ var chartdata = new Chart(ctx, {
|
|||
data: {
|
||||
datasets: [{
|
||||
label: 'Response Time (Milliseconds)',
|
||||
data: {{js .Data}},
|
||||
data: {{js .GraphData}},
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
|
@ -134,5 +152,6 @@ var chartdata = new Chart(ctx, {
|
|||
{{ end }}
|
||||
</script>
|
||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -16,10 +16,9 @@
|
|||
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
<form action="/login" method="POST">
|
||||
<form action="/dashboard" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="inputEmail3" class="col-sm-2 col-form-label">Username</label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -58,6 +57,6 @@
|
|||
</div>
|
||||
|
||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,6 +1,6 @@
|
|||
{{define "nav"}}
|
||||
<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">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
|
|
@ -34,17 +34,17 @@
|
|||
<div class="row stats_area mt-5 mb-5">
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.Service.Online24Hours}}%</span>
|
||||
<span class="lg_number">{{.Service.Online24}}%</span>
|
||||
Online last 24 Hours
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.Service.AvgResponse}}ms</span>
|
||||
<span class="lg_number">{{.Service.AvgTime}}ms</span>
|
||||
Average Response
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.Service.TotalUptime}}%</span>
|
||||
<span class="lg_number">{{.Service.AvgUptime}}%</span>
|
||||
Total Uptime
|
||||
</div>
|
||||
</div>
|
||||
|
@ -118,6 +118,7 @@
|
|||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
|
@ -128,7 +129,7 @@
|
|||
data: {
|
||||
datasets: [{
|
||||
label: 'Response Time (Milliseconds)',
|
||||
data: {{js .Service.Data}},
|
||||
data: {{js .Service.GraphData}},
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
|
@ -179,6 +180,6 @@
|
|||
</script>
|
||||
|
||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -108,6 +108,6 @@
|
|||
</div>
|
||||
|
||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</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">
|
||||
|
||||
<form method="POST" action="/setup/save">
|
||||
<form method="POST" action="/setup">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-6">
|
||||
|
||||
<div class="form-group">
|
||||
<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 value="sqlite">Sqlite</option>
|
||||
<option value="mysql">MySQL</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group" id="db_host">
|
||||
<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 class="form-group">
|
||||
<div class="form-group" id="db_port">
|
||||
<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 class="form-group">
|
||||
<div class="form-group" id="db_user">
|
||||
<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 class="form-group">
|
||||
<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">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<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">
|
||||
</div>
|
||||
|
@ -57,6 +58,11 @@
|
|||
<input type="text" name="project" class="form-control" 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">
|
||||
</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">
|
||||
|
@ -91,5 +97,7 @@
|
|||
</div>
|
||||
|
||||
<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>
|
||||
</html>
|
|
@ -40,32 +40,31 @@
|
|||
</table>
|
||||
|
||||
<h3>Create User</h3>
|
||||
|
||||
<form action="/users" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="inputEmail3" class="col-sm-4 col-form-label">Username</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="username" class="form-control" id="inputEmail3" placeholder="Username">
|
||||
</div>
|
||||
<form action="/users" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="inputEmail3" class="col-sm-4 col-form-label">Username</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="username" class="form-control" id="inputEmail3" placeholder="Username">
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Password</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="password" name="password" class="form-control" id="inputPassword3" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Password</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="password" name="password" class="form-control" id="inputPassword3" placeholder="Password">
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Confirm Password</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="password" name="password_confirm" class="form-control" id="inputPassword3" placeholder="ConfirmPassword">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="inputPassword3" class="col-sm-4 col-form-label">Confirm Password</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="password" name="password_confirm" class="form-control" id="inputPassword3" placeholder="ConfirmPassword">
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8">
|
||||
<button type="submit" class="btn btn-primary">Create User</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-8">
|
||||
<button type="submit" class="btn btn-primary">Create User</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -74,6 +73,6 @@
|
|||
</div>
|
||||
|
||||
<script src="/js/jquery-3.3.1.slim.min.js"></script>
|
||||
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
11
main.go
11
main.go
|
@ -15,6 +15,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
plg "plugin"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -245,6 +246,11 @@ func main() {
|
|||
mainProcess()
|
||||
}
|
||||
|
||||
func StringInt(s string) int64 {
|
||||
num, _ := strconv.Atoi(s)
|
||||
return int64(num)
|
||||
}
|
||||
|
||||
func mainProcess() {
|
||||
var err error
|
||||
err = DbConnection(configs.Connection)
|
||||
|
@ -253,7 +259,8 @@ func mainProcess() {
|
|||
}
|
||||
core, err = SelectCore()
|
||||
if err != nil {
|
||||
throw(err)
|
||||
fmt.Println("Core database was not found, Statup is not setup yet.")
|
||||
RunHTTPServer()
|
||||
}
|
||||
go CheckServices()
|
||||
if !setupMode {
|
||||
|
@ -263,6 +270,7 @@ func mainProcess() {
|
|||
}
|
||||
|
||||
func throw(err error) {
|
||||
panic(err)
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -334,6 +342,7 @@ func LoadConfig() (*Config, error) {
|
|||
return nil, err
|
||||
}
|
||||
err = yaml.Unmarshal(file, &config)
|
||||
configs = &config
|
||||
return &config, err
|
||||
}
|
||||
|
||||
|
|
222
main_test.go
222
main_test.go
|
@ -2,8 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -12,9 +10,78 @@ import (
|
|||
func TestInit(t *testing.T) {
|
||||
VERSION = "1.1.1"
|
||||
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{
|
||||
"postgres",
|
||||
os.Getenv("DB_HOST"),
|
||||
|
@ -22,40 +89,139 @@ func TestMakeConfig(t *testing.T) {
|
|||
os.Getenv("DB_PASS"),
|
||||
os.Getenv("DB_DATABASE"),
|
||||
5432,
|
||||
"Testing",
|
||||
"Testing POSTGRES",
|
||||
"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, "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
|
||||
configs, err = LoadConfig()
|
||||
assert.Nil(t, err)
|
||||
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
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
"upper.io/db.v3/lib/sqlbuilder"
|
||||
)
|
||||
|
||||
//
|
||||
|
@ -16,10 +16,10 @@ import (
|
|||
//
|
||||
|
||||
var (
|
||||
DB *sql.DB
|
||||
DB sqlbuilder.Database
|
||||
)
|
||||
|
||||
func SetDatabase(database *sql.DB) {
|
||||
func SetDatabase(database sqlbuilder.Database) {
|
||||
DB = database
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ type Service struct {
|
|||
type Failure struct {
|
||||
Id int
|
||||
Issue string
|
||||
Service int
|
||||
Service int64
|
||||
CreatedAt time.Time
|
||||
Ago string
|
||||
}
|
||||
|
|
127
services.go
127
services.go
|
@ -15,51 +15,38 @@ var (
|
|||
)
|
||||
|
||||
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
|
||||
Id int64 `db:"id,omitempty" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Domain string `db:"domain" json:"domain"`
|
||||
Expected string `db:"expected" json:"expected"`
|
||||
ExpectedStatus int `db:"expected_status" json:"expected_status"`
|
||||
Interval int `db:"check_interval" json:"check_interval"`
|
||||
Type string `db:"check_type" json:"type"`
|
||||
Method string `db:"method" json:"method"`
|
||||
Port int `db:"port" json:"port"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
Online bool `json:"online"`
|
||||
Latency float64 `json:"latency"`
|
||||
Online24Hours float32 `json:"24_hours_online"`
|
||||
AvgResponse string `json:"avg_response"`
|
||||
TotalUptime string `json:"uptime"`
|
||||
Failures []*Failure `json:"failures"`
|
||||
plugin.Service
|
||||
}
|
||||
|
||||
func SelectService(id string) *Service {
|
||||
for _, s := range services {
|
||||
if id == strconv.Itoa(int(s.Id)) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
func SelectService(id int64) (Service, error) {
|
||||
var service Service
|
||||
col := dbSession.Collection("services")
|
||||
res := col.Find("id", id)
|
||||
err := res.One(&service)
|
||||
return service, err
|
||||
}
|
||||
|
||||
func SelectAllServices() []*Service {
|
||||
var tks []*Service
|
||||
rows, err := db.Query("SELECT * FROM services ORDER BY id ASC")
|
||||
if err != nil {
|
||||
panic(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 SelectAllServices() ([]*Service, error) {
|
||||
var services []*Service
|
||||
col := dbSession.Collection("services").Find()
|
||||
err := col.All(&services)
|
||||
return services, err
|
||||
}
|
||||
|
||||
func (s *Service) FormatData() *Service {
|
||||
|
@ -71,16 +58,20 @@ func (s *Service) FormatData() *Service {
|
|||
}
|
||||
|
||||
func (s *Service) AvgTime() float64 {
|
||||
total := s.TotalHits()
|
||||
sum := s.Sum()
|
||||
total, _ := s.TotalHits()
|
||||
if total == 0 {
|
||||
return float64(0)
|
||||
}
|
||||
sum, _ := s.Sum()
|
||||
avg := sum / float64(total) * 100
|
||||
s.AvgResponse = fmt.Sprintf("%0.0f", avg*10)
|
||||
return avg
|
||||
amount := fmt.Sprintf("%0.0f", avg*10)
|
||||
val, _ := strconv.ParseFloat(amount, 10)
|
||||
return val
|
||||
}
|
||||
|
||||
func (s *Service) Online24() float32 {
|
||||
total := s.TotalHits()
|
||||
failed := s.TotalFailures24Hours()
|
||||
total, _ := s.TotalHits()
|
||||
failed, _ := s.TotalFailures24Hours()
|
||||
if failed == 0 {
|
||||
s.Online24Hours = 100.00
|
||||
return s.Online24Hours
|
||||
|
@ -105,12 +96,13 @@ type GraphJson struct {
|
|||
}
|
||||
|
||||
func (s *Service) GraphData() string {
|
||||
var d []*GraphJson
|
||||
for _, h := range s.Hits() {
|
||||
var d []GraphJson
|
||||
hits, _ := s.Hits()
|
||||
for _, h := range hits {
|
||||
val := h.CreatedAt
|
||||
o := &GraphJson{
|
||||
o := GraphJson{
|
||||
X: val.String(),
|
||||
Y: h.Value * 1000,
|
||||
Y: h.Latency * 1000,
|
||||
}
|
||||
d = append(d, o)
|
||||
}
|
||||
|
@ -120,8 +112,8 @@ func (s *Service) GraphData() string {
|
|||
}
|
||||
|
||||
func (s *Service) AvgUptime() string {
|
||||
failed := s.TotalFailures()
|
||||
total := s.TotalHits()
|
||||
failed, _ := s.TotalFailures()
|
||||
total, _ := s.TotalHits()
|
||||
if failed == 0 {
|
||||
s.TotalUptime = "100.00"
|
||||
return s.TotalUptime
|
||||
|
@ -139,27 +131,26 @@ func (s *Service) AvgUptime() string {
|
|||
return s.TotalUptime
|
||||
}
|
||||
|
||||
func (u *Service) Delete() {
|
||||
stmt, err := db.Prepare("DELETE FROM services WHERE id=$1")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
stmt.Exec(u.Id)
|
||||
func (u *Service) Delete() error {
|
||||
col := dbSession.Collection("services")
|
||||
res := col.Find("id", u.Id)
|
||||
err := res.Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *Service) Update() {
|
||||
|
||||
}
|
||||
|
||||
func (u *Service) Create() int {
|
||||
var lastInsertId int
|
||||
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)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
func (u *Service) Create() (int64, error) {
|
||||
u.CreatedAt = time.Now()
|
||||
col := dbSession.Collection("services")
|
||||
uuid, err := col.Insert(u)
|
||||
services, _ = SelectAllServices()
|
||||
if uuid == nil {
|
||||
return 0, err
|
||||
}
|
||||
services = SelectAllServices()
|
||||
go u.CheckQueue()
|
||||
return lastInsertId
|
||||
return uuid.(int64), err
|
||||
}
|
||||
|
||||
func CountOnline() int {
|
||||
|
@ -174,17 +165,13 @@ func CountOnline() int {
|
|||
|
||||
func NewSHA1Hash(n ...int) string {
|
||||
noRandomCharacters := 32
|
||||
|
||||
if len(n) > 0 {
|
||||
noRandomCharacters = n[0]
|
||||
}
|
||||
|
||||
randString := RandomString(noRandomCharacters)
|
||||
|
||||
hash := sha1.New()
|
||||
hash.Write([]byte(randString))
|
||||
bs := hash.Sum(nil)
|
||||
|
||||
return fmt.Sprintf("%x", bs)
|
||||
}
|
||||
|
||||
|
|
59
setup.go
59
setup.go
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/hunterlong/statup/plugin"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
@ -9,15 +10,16 @@ import (
|
|||
)
|
||||
|
||||
type DbConfig struct {
|
||||
DbConn string `yaml:"connection"`
|
||||
DbHost string `yaml:"host"`
|
||||
DbUser string `yaml:"user"`
|
||||
DbPass string `yaml:"password"`
|
||||
DbData string `yaml:"database"`
|
||||
DbPort int `yaml:"port"`
|
||||
Project string `yaml:"-"`
|
||||
Username string `yaml:"-"`
|
||||
Password string `yaml:"-"`
|
||||
DbConn string `yaml:"connection"`
|
||||
DbHost string `yaml:"host"`
|
||||
DbUser string `yaml:"user"`
|
||||
DbPass string `yaml:"password"`
|
||||
DbData string `yaml:"database"`
|
||||
DbPort int `yaml:"port"`
|
||||
Project string `yaml:"-"`
|
||||
Description string `yaml:"-"`
|
||||
Username string `yaml:"-"`
|
||||
Password string `yaml:"-"`
|
||||
}
|
||||
|
||||
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")
|
||||
username := r.PostForm.Get("username")
|
||||
password := r.PostForm.Get("password")
|
||||
sample := r.PostForm.Get("sample_data")
|
||||
description := r.PostForm.Get("description")
|
||||
|
||||
config := &DbConfig{
|
||||
dbConn,
|
||||
|
@ -40,20 +44,32 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
dbDatabase,
|
||||
dbPort,
|
||||
project,
|
||||
description,
|
||||
username,
|
||||
password,
|
||||
}
|
||||
|
||||
err := config.Save()
|
||||
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)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
mainProcess()
|
||||
|
||||
}
|
||||
|
||||
func (c *DbConfig) Save() error {
|
||||
|
@ -79,6 +95,21 @@ func (c *DbConfig) Save() error {
|
|||
}
|
||||
DropDatabase()
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
DROP table core;
|
||||
DROP table hits;
|
||||
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 (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int64
|
||||
Username string
|
||||
Password string
|
||||
Email string
|
||||
Id int64 `db:"id,omitempty" json:"id"`
|
||||
Username string `db:"username" json:"username"`
|
||||
Password string `db:"password" json:"-"`
|
||||
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
|
||||
rows, err := db.Query("SELECT id, username, password FROM users WHERE username=$1", username)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&user.Id, &user.Username, &user.Password)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return user
|
||||
col := dbSession.Collection("users")
|
||||
res := col.Find("id", id)
|
||||
err := res.One(&user)
|
||||
return &user, err
|
||||
}
|
||||
|
||||
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)
|
||||
var lastInsertId int
|
||||
db.QueryRow("INSERT INTO users(username,password,created_at) VALUES($1,$2,NOW()) returning id;", u.Username, password).Scan(&lastInsertId)
|
||||
return lastInsertId
|
||||
u.Password = password
|
||||
u.ApiKey = NewSHA1Hash(5)
|
||||
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
|
||||
rows, err := db.Query("SELECT id, username, password FROM users ORDER BY id ASC")
|
||||
if err != nil {
|
||||
panic(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
|
||||
col := dbSession.Collection("users").Find()
|
||||
err := col.All(&users)
|
||||
return users, err
|
||||
}
|
||||
|
||||
func AuthUser(username, password string) (User, bool) {
|
||||
var user User
|
||||
func AuthUser(username, password string) (*User, bool) {
|
||||
var auth bool
|
||||
user = SelectUser(username)
|
||||
user, err := SelectUsername(username)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if CheckHash(password, user.Password) {
|
||||
auth = true
|
||||
}
|
||||
|
|
131
web.go
131
web.go
|
@ -15,28 +15,40 @@ var (
|
|||
session *sessions.CookieStore
|
||||
)
|
||||
|
||||
const (
|
||||
cookieKey = "apizer_auth"
|
||||
)
|
||||
|
||||
func Router() *mux.Router {
|
||||
r := mux.NewRouter()
|
||||
r.Handle("/", http.HandlerFunc(IndexHandler))
|
||||
r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(cssBox.HTTPBox())))
|
||||
r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(jsBox.HTTPBox())))
|
||||
r.Handle("/setup", http.HandlerFunc(SetupHandler))
|
||||
r.Handle("/setup/save", http.HandlerFunc(ProcessSetupHandler))
|
||||
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler))
|
||||
r.Handle("/login", http.HandlerFunc(LoginHandler))
|
||||
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
|
||||
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
|
||||
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET")
|
||||
r.Handle("/dashboard", http.HandlerFunc(LoginHandler)).Methods("POST")
|
||||
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("/service/{id}", http.HandlerFunc(ServicesViewHandler))
|
||||
r.Handle("/service/{id}", http.HandlerFunc(ServicesUpdateHandler)).Methods("POST")
|
||||
r.Handle("/service/{id}/edit", http.HandlerFunc(ServicesViewHandler))
|
||||
r.Handle("/service/{id}/delete", http.HandlerFunc(ServicesDeleteHandler))
|
||||
r.Handle("/users", http.HandlerFunc(UsersHandler))
|
||||
r.Handle("/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("/settings", http.HandlerFunc(PluginsHandler))
|
||||
r.Handle("/plugins/download/{name}", http.HandlerFunc(PluginsDownloadHandler))
|
||||
r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST")
|
||||
r.Handle("/help", http.HandlerFunc(HelpHandler))
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -64,20 +76,18 @@ func RunHTTPServer() {
|
|||
}
|
||||
|
||||
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := store.Get(r, "apizer_auth")
|
||||
session, _ := store.Get(r, cookieKey)
|
||||
session.Values["authenticated"] = false
|
||||
session.Save(r, w)
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := store.Get(r, "apizer_auth")
|
||||
session, _ := store.Get(r, cookieKey)
|
||||
r.ParseForm()
|
||||
username := r.PostForm.Get("username")
|
||||
password := r.PostForm.Get("password")
|
||||
user, auth := AuthUser(username, password)
|
||||
fmt.Println(user)
|
||||
fmt.Println(auth)
|
||||
_, auth := AuthUser(username, password)
|
||||
if auth {
|
||||
session.Values["authenticated"] = true
|
||||
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) {
|
||||
fmt.Println("creating user")
|
||||
r.ParseForm()
|
||||
username := r.PostForm.Get("username")
|
||||
password := r.PostForm.Get("password")
|
||||
|
@ -114,11 +108,15 @@ func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
user.Create()
|
||||
_, err := user.Create()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
http.Redirect(w, r, "/users", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("service adding")
|
||||
r.ParseForm()
|
||||
name := r.PostForm.Get("name")
|
||||
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"))
|
||||
interval, _ := strconv.Atoi(r.PostForm.Get("interval"))
|
||||
|
||||
service := &Service{
|
||||
fmt.Println(r.PostForm)
|
||||
|
||||
service := Service{
|
||||
Name: name,
|
||||
Domain: domain,
|
||||
Method: method,
|
||||
|
@ -138,7 +138,10 @@ func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
fmt.Println(service)
|
||||
|
||||
service.Create()
|
||||
_, err := service.Create()
|
||||
if err != nil {
|
||||
go service.CheckQueue()
|
||||
}
|
||||
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
|
@ -168,29 +171,30 @@ type dashboard struct {
|
|||
Core *Core
|
||||
CountOnline int
|
||||
CountServices int
|
||||
Count24Failures int
|
||||
Count24Failures uint64
|
||||
}
|
||||
|
||||
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 {
|
||||
tmpl := Parse("login.html")
|
||||
tmpl.Execute(w, nil)
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type serviceHandler struct {
|
||||
Service *Service
|
||||
Service Service
|
||||
Auth bool
|
||||
}
|
||||
|
||||
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 {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
|
@ -200,21 +204,21 @@ func ServicesHandler(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 {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
service := SelectService(vars["id"])
|
||||
service, _ := SelectService(StringInt(vars["id"]))
|
||||
|
||||
service.Delete()
|
||||
services = SelectAllServices()
|
||||
services, _ = SelectAllServices()
|
||||
http.Redirect(w, r, "/services", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func IsAuthenticated(r *http.Request) bool {
|
||||
session, _ := store.Get(r, "apizer_auth")
|
||||
session, _ := store.Get(r, cookieKey)
|
||||
if session.Values["authenticated"] == nil {
|
||||
return false
|
||||
}
|
||||
|
@ -298,15 +302,31 @@ func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
//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) {
|
||||
auth := IsAuthenticated(r)
|
||||
vars := mux.Vars(r)
|
||||
service := SelectService(vars["id"])
|
||||
|
||||
service, _ := SelectService(StringInt(vars["id"]))
|
||||
tmpl := Parse("service.html")
|
||||
|
||||
serve := &serviceHandler{service, auth}
|
||||
|
||||
tmpl.Execute(w, serve)
|
||||
}
|
||||
|
||||
|
@ -353,28 +373,13 @@ func ParsePlugins(file string) *template.Template {
|
|||
}
|
||||
|
||||
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 {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
tmpl := Parse("users.html")
|
||||
tmpl.Execute(w, SelectAllUsers())
|
||||
}
|
||||
|
||||
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())
|
||||
users, _ := SelectAllUsers()
|
||||
tmpl.Execute(w, users)
|
||||
}
|
||||
|
|
|
@ -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