pull/10/head
Hunter Long 2018-06-09 20:44:47 -07:00
parent 1490d11c8e
commit 6441b325ff
16 changed files with 213 additions and 99 deletions

View File

@ -7,3 +7,7 @@ Use the [Statup Docker Image](https://hub.docker.com/r/hunterlong/statup) to cre
docker run -it -p 8080:8080 hunterlong/statup
```
### Install on Linux
```
bash <(curl -s https://statup.io/install.sh)
```

View File

@ -9,7 +9,7 @@ import (
)
func CheckServices() {
services := SelectAllServices()
services = SelectAllServices()
for _, v := range services {
obj := v
go obj.CheckQueue()
@ -32,7 +32,6 @@ func (s *Service) Check() {
s.Failure(response, fmt.Sprintf("HTTP Error %v", err))
return
}
if s.Expected != "" {
contents, _ := ioutil.ReadAll(response.Body)
match, _ := regexp.MatchString(s.Expected, string(contents))
@ -45,12 +44,12 @@ func (s *Service) Check() {
s.Failure(response, fmt.Sprintf("HTTP Status Code %v did not match %v", response.StatusCode, s.ExpectedStatus))
return
}
s.Online = true
s.Record(response)
}
func (s *Service) Record(response *http.Response) {
defer response.Body.Close()
s.Online = true
db.QueryRow("INSERT INTO hits(service,latency,created_at) VALUES($1,$2,NOW()) returning id;", s.Id, s.Latency).Scan()
}

View File

@ -1,5 +1,7 @@
package main
import "github.com/gorilla/sessions"
type Core struct {
Name string
Config string
@ -20,5 +22,6 @@ func SelectCore() (*Core, error) {
return nil, err
}
}
store = sessions.NewCookieStore([]byte(core.Secret))
return &core, err
}

View File

@ -31,3 +31,11 @@ func (s *Service) TotalFailures() int {
db.QueryRow("SELECT COUNT(id) FROM failures WHERE service=$1;", s.Id).Scan(&amount)
return amount
}
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, t, x).Scan(&amount)
return amount
}

View File

@ -12,7 +12,8 @@ HTML,BODY {
.navbar {
margin-left: -50px;
width: 720px;
margin-top: -50px;
width: 790px;
margin-bottom: 30px;
}

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/base.css">
<title>Fusioner | Dashboard</title>
<title>Statup | Dashboard</title>
</head>
<body>
@ -15,7 +15,7 @@
<div class="container">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Fusioner</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>
@ -25,13 +25,13 @@
<a class="nav-link" href="/dashboard">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/tokens">Tokens</a>
<a class="nav-link" href="/services">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/users">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/permissions">Permissions</a>
<a class="nav-link" href="/plguins">Plugins</a>
</li>
</ul>
<span class="navbar-text">

View File

@ -8,7 +8,7 @@
<link rel="stylesheet" href="/css/base.css">
<script src="/js/Chart.bundle.min.js"></script>
<title>Fusioner | Dashboard</title>
<title>Statup | Dashboard</title>
</head>
<body>
@ -24,6 +24,8 @@
<div class="row stats_area mt-3 mb-3">
{{.Online}}
<div class="col-4">
<span class="lg_number">{{.Online24Hours}}%</span>
Online last 24 Hours

View File

@ -7,13 +7,11 @@
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/base.css">
<title>Hello, world!</title>
<title>Statup | Login</title>
</head>
<body>
<div class="container">
<div class="row">

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/base.css">
<title>Fusioner | Permissions</title>
<title>Statup | Permissions</title>
</head>
<body>

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/base.css">
<title>Fusioner | Tokens</title>
<title>Statup | Services</title>
</head>
<body>
@ -15,54 +15,58 @@
<div class="container">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Fusioner</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>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/dashboard">Dashboard</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/tokens">Tokens</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/users">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/permissions">Permissions</a>
</li>
</ul>
<span class="navbar-text">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<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>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/dashboard">Dashboard</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/services">Services</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/users">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/plguins">Plugins</a>
</li>
</ul>
<span class="navbar-text">
<a class="nav-link" href="/logout">Logout</a>
</span>
</div>
</nav>
</div>
</nav>
<div class="row">
<div class="col-12">
<h3>Tokens</h3>
<a href="/token/create" class="btn btn-primary">Create New Token</a>
<h3>Services</h3>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Key</th>
<th scope="col">Secret</th>
<th scope="col">Name</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{{range .}}
<tr>
<th scope="row">{{.Id}}</th>
<td>{{.Key}}</td>
<td>{{.Secret}}</td>
<td>{{.Name}}</td>
<td>
<div class="btn-group" data-toggle="buttons">
<button class="btn btn-primary" type="submit">View</button>
<button class="btn btn-primary" type="submit">Edit</button>
<button class="btn btn-primary" type="submit">Delete</button>
</div>
</td>
</tr>
{{end}}
</tbody>
@ -71,6 +75,56 @@
</div>
<div class="col-12">
<h3>Create Service</h3>
<form action="/services/create" method="POST">
<div class="form-group row">
<label for="inputEmail3" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input type="text" name="name" class="form-control" id="inputEmail3" placeholder="Name">
</div>
</div>
<div class="form-group row">
<label for="inputPassword3" class="col-sm-4 col-form-label">Application Endpoint (URL)</label>
<div class="col-sm-8">
<input type="text" name="domain" class="form-control" id="inputPassword3" placeholder="https://google.com">
</div>
</div>
<div class="form-group row">
<label for="inputPassword3" class="col-sm-4 col-form-label">Expected Response (Regex)</label>
<div class="col-sm-8">
<input type="text" name="expected" class="form-control" id="inputPassword3" placeholder="string">
</div>
</div>
<div class="form-group row">
<label for="inputPassword3" class="col-sm-4 col-form-label">Expected Status Code</label>
<div class="col-sm-8">
<input type="number" name="expected_status" class="form-control" id="inputPassword3" placeholder="200">
</div>
</div>
<div class="form-group row">
<label for="inputPassword3" class="col-sm-4 col-form-label">HTTP Method</label>
<div class="col-sm-8">
<input type="text" name="method" class="form-control" id="inputPassword3" placeholder="GET">
</div>
</div>
<div class="form-group row">
<label for="inputPassword3" class="col-sm-4 col-form-label">Check Interval</label>
<div class="col-sm-8">
<input type="number" name="interval" class="form-control" id="inputPassword3" placeholder="10">
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-primary">Create</button>
</div>
</div>
</form>
</div>
</div>

View File

@ -8,7 +8,7 @@
<link rel="stylesheet" href="/css/base.css">
<script src="/js/Chart.bundle.min.js"></script>
<title>Fusioner | Setup</title>
<title>Statup | Setup</title>
</head>
<body>

View File

@ -7,7 +7,7 @@
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/base.css">
<title>Fusioner | Users</title>
<title>Statup | Users</title>
</head>
<body>
@ -15,31 +15,31 @@
<div class="container">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">Fusioner</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>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/dashboard">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/tokens">Tokens</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/users">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/permissions">Permissions</a>
</li>
</ul>
<span class="navbar-text">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<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>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/dashboard">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/services">Services</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="/users">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/plguins">Plugins</a>
</li>
</ul>
<span class="navbar-text">
<a class="nav-link" href="/logout">Logout</a>
</span>
</div>
</nav>
</div>
</nav>
<div class="row">
@ -64,7 +64,9 @@
</tbody>
</table>
<form action="/user/create" method="POST">
<h3>Create User</h3>
<form action="/users/create" method="POST">
<div class="form-group row">
<label for="inputEmail3" class="col-sm-2 col-form-label">Username</label>
<div class="col-sm-10">

View File

@ -73,7 +73,6 @@ func LoadConfig() *Config {
return nil
}
yaml.Unmarshal(file, &config)
store = sessions.NewCookieStore([]byte(config.Secret))
return &config
}

View File

@ -8,6 +8,10 @@ import (
"time"
)
var (
services []Service
)
type Service struct {
Id int64
Name string
@ -62,6 +66,7 @@ func SelectAllServices() []Service {
func (s *Service) FormatData() *Service {
s.GraphData()
s.AvgUptime()
s.Online24()
s.AvgTime()
return s
}
@ -74,6 +79,18 @@ func (s *Service) AvgTime() float64 {
return avg
}
func (s *Service) Online24() float64 {
total := s.TotalHits()
failed := s.TotalFailures24Hours()
if failed == 0 {
s.Online24Hours = 100.00
return s.Online24Hours
}
avg := float64(failed) / float64(total) * 100
s.Online24Hours = avg
return avg
}
type GraphJson struct {
X string `json:"x"`
Y float64 `json:"y"`
@ -103,19 +120,19 @@ func (s *Service) AvgUptime() float64 {
return s.TotalUptime
}
percent := float64(failed) / float64(total) * 100
fmt.Println(failed, total, percent)
s.TotalUptime = percent
return percent
}
func (u *Service) Create() int {
var lastInsertId int
db.QueryRow("INSERT INTO services(name, domain, expected, expected_status, created_at) VALUES($1,$2,$3,$4,NOW()) returning id;", u.Name, u.Domain, u.Expected, u.ExpectedStatus).Scan(&lastInsertId)
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)
}
return lastInsertId
}
// NewSHA1Hash generates a new SHA1 hash based on
// a random number of characters.
func NewSHA1Hash(n ...int) string {
noRandomCharacters := 32

View File

@ -46,7 +46,7 @@ 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, 'Google', 'https://www.google.com', 'https', 0, '', 200, 5, 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());

73
web.go
View File

@ -4,6 +4,7 @@ import (
"fmt"
"html/template"
"net/http"
"strconv"
)
type dashboard struct {
@ -25,9 +26,9 @@ func RunHTTPServer() {
http.Handle("/login", http.HandlerFunc(LoginHandler))
http.Handle("/logout", http.HandlerFunc(LogoutHandler))
//http.Handle("/auth", http.HandlerFunc(AuthenticateHandler))
http.Handle("/user/create", http.HandlerFunc(CreateUserHandler))
http.Handle("/token/create", http.HandlerFunc(CreateServiceHandler))
http.Handle("/tokens", http.HandlerFunc(ServicesHandler))
http.Handle("/users/create", http.HandlerFunc(CreateUserHandler))
http.Handle("/services/create", http.HandlerFunc(CreateServiceHandler))
http.Handle("/services", http.HandlerFunc(ServicesHandler))
http.Handle("/users", http.HandlerFunc(UsersHandler))
http.ListenAndServe(":8080", nil)
}
@ -44,7 +45,9 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.PostForm.Get("username")
password := r.PostForm.Get("password")
_, auth := AuthUser(username, password)
user, auth := AuthUser(username, password)
fmt.Println(user)
fmt.Println(auth)
if auth {
session.Values["authenticated"] = true
session.Save(r, w)
@ -86,8 +89,26 @@ func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
}
func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
token := &Service{}
token.Create()
r.ParseForm()
name := r.PostForm.Get("name")
domain := r.PostForm.Get("domain")
method := r.PostForm.Get("method")
expected := r.PostForm.Get("expected")
status, _ := strconv.Atoi(r.PostForm.Get("expected_status"))
interval, _ := strconv.Atoi(r.PostForm.Get("interval"))
service := &Service{
Name: name,
Domain: domain,
Method: method,
Expected: expected,
ExpectedStatus: status,
Interval: interval,
}
fmt.Println(service)
service.Create()
http.Redirect(w, r, "/services", http.StatusSeeOther)
}
@ -123,29 +144,35 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
return template.JS(html)
},
}).Parse(indexFile)
out := index{SelectAllServices()}
out := index{services}
indexTmpl.Execute(w, out)
}
func DashboardHandler(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
//}
dashboardFile, err := tmplBox.String("dashboard.html")
if err != nil {
panic(err)
}
dashboardTmpl, err := template.New("message").Parse(dashboardFile)
if err != nil {
panic(err)
session, _ := store.Get(r, "apizer_auth")
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
loginFile, err := tmplBox.String("login.html")
if err != nil {
panic(err)
}
loginTmpl, err := template.New("message").Parse(loginFile)
if err != nil {
panic(err)
}
loginTmpl.Execute(w, nil)
} else {
dashboardFile, err := tmplBox.String("dashboard.html")
if err != nil {
panic(err)
}
dashboardTmpl, err := template.New("message").Parse(dashboardFile)
if err != nil {
panic(err)
}
out := dashboard{SelectAllServices(), SelectAllUsers(), core}
dashboardTmpl.Execute(w, out)
}
out := dashboard{SelectAllServices(), SelectAllUsers(), core}
dashboardTmpl.Execute(w, out)
}
func ServicesHandler(w http.ResponseWriter, r *http.Request) {