pull/10/head
Hunter Long 2018-06-21 23:56:44 -07:00
parent b4fe30c2cc
commit 00854c930a
21 changed files with 360 additions and 172 deletions

8
api.go
View File

@ -10,6 +10,14 @@ func ApiIndexHandler(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(core) json.NewEncoder(w).Encode(core)
} }
func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
checkin := FindCheckin(vars["api"])
checkin.Receivehit()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(checkin)
}
func ApiServiceHandler(w http.ResponseWriter, r *http.Request) { func ApiServiceHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
service, _ := SelectService(StringInt(vars["id"])) service, _ := SelectService(StringInt(vars["id"]))

View File

@ -12,6 +12,7 @@ func CheckServices() {
services, _ = SelectAllServices() services, _ = SelectAllServices()
for _, v := range services { for _, v := range services {
obj := v obj := v
go obj.StartCheckins()
go obj.CheckQueue() go obj.CheckQueue()
} }
} }

111
checkin.go Normal file
View File

@ -0,0 +1,111 @@
package main
import (
"fmt"
"github.com/ararog/timeago"
"time"
)
type Checkin struct {
Id int `db:"id,omitempty"`
Service int64 `db:"service"`
Interval int64 `db:"check_interval"`
Api string `db:"api"`
CreatedAt time.Time `db:"created_at"`
Hits int64 `json:"hits"`
Last time.Time `json:"last"`
}
func (s *Service) SelectAllCheckins() []*Checkin {
var checkins []*Checkin
col := dbSession.Collection("checkins").Find("service", s.Id).OrderBy("-id")
col.All(&checkins)
s.Checkins = checkins
return checkins
}
func (u *Checkin) Create() (int64, error) {
u.CreatedAt = time.Now()
uuid, err := dbSession.Collection("checkins").Insert(u)
if uuid == nil {
return 0, err
}
fmt.Println(uuid)
return uuid.(int64), err
}
func SelectCheckinApi(api string) *Checkin {
var checkin *Checkin
dbSession.Collection("checkins").Find("api", api).One(&checkin)
return checkin
}
func (c *Checkin) Receivehit() {
c.Hits++
c.Last = time.Now()
}
func (c *Checkin) RecheckCheckinFailure(guard chan struct{}) {
between := time.Now().Sub(c.Last).Seconds()
if between > float64(c.Interval) {
fmt.Println("rechecking every 15 seconds!")
c.CreateFailure()
time.Sleep(15 * time.Second)
guard <- struct{}{}
c.RecheckCheckinFailure(guard)
} else {
fmt.Println("i recovered!!")
}
<-guard
}
func (f *Checkin) CreateFailure() {
}
func (f *Checkin) Ago() string {
got, _ := timeago.TimeAgoWithTime(time.Now(), f.Last)
return got
}
func FindCheckin(api string) *Checkin {
for _, s := range services {
for _, c := range s.Checkins {
if c.Api == api {
return c
}
}
}
return nil
}
func (c *Checkin) Run() {
if c.Interval == 0 {
return
}
fmt.Println("checking: ", c.Api)
between := time.Now().Sub(c.Last).Seconds()
if between > float64(c.Interval) {
guard := make(chan struct{})
c.RecheckCheckinFailure(guard)
<-guard
}
time.Sleep(1 * time.Second)
c.Run()
}
func (s *Service) StartCheckins() {
for _, c := range s.Checkins {
checkin := c
go checkin.Run()
}
}
func CheckinProcess() {
for _, s := range services {
for _, c := range s.Checkins {
checkin := c
go checkin.Run()
}
}
}

View File

@ -2,8 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"strings"
"time"
"upper.io/db.v3/lib/sqlbuilder" "upper.io/db.v3/lib/sqlbuilder"
"upper.io/db.v3/mysql" "upper.io/db.v3/mysql"
"upper.io/db.v3/postgresql" "upper.io/db.v3/postgresql"
@ -63,90 +61,3 @@ func DbConnection(dbType string) error {
OnLoad(dbSession) OnLoad(dbSession)
return err return err
} }
func DropDatabase() {
fmt.Println("Dropping Tables...")
down, _ := sqlBox.String("down.sql")
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",
}
s1.Create()
s2.Create()
s3.Create()
s4.Create()
for i := 0; i < 100; i++ {
s1.Check()
s2.Check()
s3.Check()
s4.Check()
time.Sleep(250 * time.Millisecond)
}
return nil
}
func CreateDatabase() {
fmt.Println("Creating Tables...")
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()
}

View File

@ -38,7 +38,7 @@ func (s *Service) Hits() ([]Hit, error) {
func (s *Service) LimitedHits() ([]Hit, error) { func (s *Service) LimitedHits() ([]Hit, error) {
var hits []Hit var hits []Hit
col := hitCol().Find("service", s.Id).Limit(256).OrderBy("-id") col := hitCol().Find("service", s.Id).Limit(1056).OrderBy("-id")
err := col.All(&hits) err := col.All(&hits)
return hits, err return hits, err
} }

View File

@ -1,15 +1,14 @@
HTML,BODY { HTML,BODY {
background-color: #efefef; background-color: #efefef;
margin: 40px 0;
} }
.container { .container {
padding-top: 20px; padding-top: 20px;
padding-bottom: 20px; padding-bottom: 20px;
max-width: 860px;
} }
.navbar { .navbar {
margin-top: -50px;
margin-bottom: 30px; margin-bottom: 30px;
} }
@ -94,7 +93,8 @@ HTML,BODY {
@media (max-width: 767px) { @media (max-width: 767px) {
.container { .container {
padding: 0px; margin-top: 0 !important;
padding: 0 !important;
} }
.navbar { .navbar {

View File

@ -44,14 +44,18 @@
{{ range .Services }} {{ range .Services }}
{{$name := .Name}} {{ if .Failures }}
<div class="list-group mt-5">
{{ range .Failures }} {{ range .Failures }}
<blockquote class="blockquote text-left mt-3"> <a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
<p class="mb-0">{{$name}}</p> <div class="d-flex w-100 justify-content-between">
<p class="mb-0">{{.ParseError}}</p> <h5 class="mb-1">{{.ParseError}}</h5>
<footer class="blockquote-footer">Reported <cite title="Source Title">{{.Ago}}</cite></footer> <small>Reported {{.Ago}}</small>
</blockquote> </div>
<p class="mb-1">{{.Issue}}</p>
</a>
{{ end }}
</div>
{{ end }} {{ end }}

View File

@ -1,5 +1,5 @@
{{ define "footer"}} {{ define "footer"}}
<div class="footer text-center"> <div class="footer text-center mb-4">
{{ if .Core.Footer }} {{ if .Core.Footer }}
{{ safe .Core.Footer }} {{ safe .Core.Footer }}
{{ end }} {{ end }}

View File

@ -23,29 +23,18 @@
<div class="col-12 mb-5"> <div class="col-12 mb-5">
{{ if .Core.AllOnline }}
<div class="alert alert-success mt-2 mb-2" role="alert">
All services are online and operational!
</div>
{{ else }}
<div class="alert alert-danger mt-2 mb-2" role="alert">
There is an offline service!
</div>
{{ end }}
<div class="list-group online_list"> <div class="list-group online_list">
{{ range .Services }} {{ range .Services }}
<a href="#" class="list-group-item list-group-item-action {{if .Online}}{{ end }}"> <a href="#" class="list-group-item list-group-item-action {{if not .Online}}bg-danger text-white{{ end }}">
{{ .Name }} {{ .Name }}
{{if .Online}} {{if .Online}}
<span class="badge online_badge float-right">ONLINE</span> <span class="badge online_badge float-right">ONLINE</span>
{{ else }} {{ else }}
<span class="badge offline_badge float-right">OFFLINE</span> <span class="badge bg-white text-black-50 float-right">OFFLINE</span>
{{end}} {{end}}
</a> </a>
{{ end }} {{ end }}
</div> </div>
</div> </div>
@ -69,17 +58,17 @@
<div class="row stats_area mt-5 mb-5"> <div class="row stats_area mt-5 mb-5">
<div class="col-md-4 col-sm-12"> <div class="col-4">
<span class="lg_number">{{.Online24}}%</span> <span class="lg_number">{{.Online24}}%</span>
Online last 24 Hours Online last 24 Hours
</div> </div>
<div class="col-md-4 col-sm-6"> <div class="col-4">
<span class="lg_number">{{.AvgTime}}ms</span> <span class="lg_number">{{.AvgTime}}ms</span>
Average Response Average Response
</div> </div>
<div class="col-md-4 col-sm-6"> <div class="col-4">
<span class="lg_number">{{.AvgUptime}}%</span> <span class="lg_number">{{.AvgUptime}}%</span>
Total Uptime Total Uptime
</div> </div>
@ -98,6 +87,9 @@
<p class="mb-1">{{.Issue}}</p> <p class="mb-1">{{.Issue}}</p>
</a> </a>
{{ end }} {{ end }}
<span class="text-right">{{ .TotalFailures }} Total Failures</span>
</div> </div>
{{ end }} {{ end }}
@ -124,20 +116,10 @@ var chartdata = new Chart(ctx, {
label: 'Response Time (Milliseconds)', label: 'Response Time (Milliseconds)',
data: {{js .GraphData}}, data: {{js .GraphData}},
backgroundColor: [ backgroundColor: [
'rgba(255, 99, 132, 0.2)', 'rgba(47, 206, 30, 0.92)'
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
], ],
borderColor: [ borderColor: [
'rgba(255,99,132,1)', 'rgb(47, 171, 34)'
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
], ],
borderWidth: 1 borderWidth: 1
}] }]

View File

@ -19,7 +19,7 @@
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-md-4 col-sm-12">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical"> <div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<a class="nav-link active" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true">Settings</a> <a class="nav-link active" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true">Settings</a>
@ -30,7 +30,7 @@
{{end}} {{end}}
</div> </div>
</div> </div>
<div class="col-8"> <div class="col-md-8 col-sm-12">
<div class="tab-content" id="v-pills-tabContent"> <div class="tab-content" id="v-pills-tabContent">
<div class="tab-pane fade show active" id="v-pills-home" role="tabpanel" aria-labelledby="v-pills-home-tab"> <div class="tab-pane fade show active" id="v-pills-home" role="tabpanel" aria-labelledby="v-pills-home-tab">
<h3>Settings</h3> <h3>Settings</h3>
@ -102,9 +102,7 @@
</div> </div>
</div> </div>
</div> </div>
{{template "footer"}} {{template "footer"}}
<script src="/js/jquery-3.3.1.slim.min.js"></script> <script src="/js/jquery-3.3.1.slim.min.js"></script>

View File

@ -18,16 +18,14 @@
{{template "nav"}} {{template "nav"}}
{{end}} {{end}}
<div class="col-12">
<div class="col-12 mb-4"> <div class="col-12 mb-4">
<h3 class="mt-2">{{ .Name }} <h4 class="mt-2">{{ .Name }}
{{if .Online }} {{if .Online }}
<span class="badge online_badge float-right">ONLINE</span> <span class="badge online_badge float-right">ONLINE</span>
{{ else }} {{ else }}
<span class="badge offline_badge float-right">OFFLINE</span> <span class="badge offline_badge float-right">OFFLINE</span>
{{end}}</h3> {{end}}</h4>
<div class="row stats_area mt-5 mb-5"> <div class="row stats_area mt-5 mb-5">
@ -59,6 +57,42 @@
</div> </div>
<div class="col-12">
<h3>Service Checkins</h3>
{{ range .Checkins }}
<div class="col-12 mt-3">
<h5>Check #{{.Id}} <span class="badge online_badge float-right">Checked in {{.Ago}}</span></h5>
<input type="text" class="form-control" value="https://domainhere.com/api/checkin/{{.Api}}">
</div>
{{ end }}
<form action="/service/{{.Id}}/checkin" method="POST">
<div class="form-group row">
<label for="service_name" class="col-sm-4 col-form-label">Check Interval (in seconds)</label>
<div class="col-sm-8">
<input type="number" name="name" class="form-control" id="checkin_interval" value="30" placeholder="Name">
</div>
</div>
<div class="form-group row">
<div class="col-sm-10">
<button type="submit" class="btn btn-success">Save Checkin</button>
</div>
</div>
</form>
</div>
{{if Auth}} {{if Auth}}
<div class="col-12"> <div class="col-12">
@ -82,8 +116,8 @@
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="service_url" class="col-sm-4 col-form-label">Application Endpoint (URL)</label> <label for="service_url" class="col-sm-12 col-form-label">Application Endpoint (URL)</label>
<div class="col-8"> <div class="col-12">
<input type="text" name="domain" class="form-control" id="service_url" value="{{.Domain}}" placeholder="https://google.com"> <input type="text" name="domain" class="form-control" id="service_url" value="{{.Domain}}" placeholder="https://google.com">
</div> </div>
</div> </div>
@ -132,15 +166,21 @@
{{end}} {{end}}
{{ range .Failures }} {{ if .LimitedFailures }}
<blockquote class="blockquote text-right mt-3"> <div class="list-group mt-5">
<p class="mb-0">{{.ParseError}}</p> {{ range .LimitedFailures }}
<footer class="blockquote-footer">Reported <cite title="Source Title">{{.Ago}}</cite></footer> <a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
</blockquote> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{.ParseError}}</h5>
<small>Reported {{.Ago}}</small>
</div>
<p class="mb-1">{{.Issue}}</p>
</a>
{{ end }}
</div>
{{ end }} {{ end }}
</div> </div>
</div>
{{template "footer"}} {{template "footer"}}

View File

@ -23,7 +23,6 @@
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th scope="col">#</th>
<th scope="col">Name</th> <th scope="col">Name</th>
<th scope="col">Status</th> <th scope="col">Status</th>
<th scope="col"></th> <th scope="col"></th>
@ -32,7 +31,6 @@
<tbody> <tbody>
{{range .}} {{range .}}
<tr> <tr>
<th scope="row">{{.Id}}</th>
<td>{{.Name}}</td> <td>{{.Name}}</td>
<td>{{if .Online}}<span class="badge badge-success">ONLINE</span>{{else}}<span class="badge badge-danger">OFFLINE</span>{{end}} </td> <td>{{if .Online}}<span class="badge badge-success">ONLINE</span>{{else}}<span class="badge badge-danger">OFFLINE</span>{{end}} </td>
<td class="text-right"> <td class="text-right">
@ -66,8 +64,8 @@
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="service_url" class="col-sm-4 col-form-label">Application Endpoint (URL)</label> <label for="service_url" class="col-sm-12 col-form-label">Application Endpoint (URL)</label>
<div class="col-8"> <div class="col-12">
<input type="text" name="domain" class="form-control" id="service_url" placeholder="https://google.com"> <input type="text" name="domain" class="form-control" id="service_url" placeholder="https://google.com">
</div> </div>
</div> </div>
@ -106,7 +104,7 @@
</div> </div>
<div class="form-group row"> <div class="form-group row">
<div class="col-sm-10"> <div class="col-sm-10">
<button type="submit" class="btn btn-success">Create Service</button> <button type="submit" class="btn btn-success btn-block ">Create Service</button>
</div> </div>
</div> </div>
</form> </form>

View File

@ -150,7 +150,7 @@ func mainProcess() {
fmt.Println("Core database was not found, Statup is not setup yet.") fmt.Println("Core database was not found, Statup is not setup yet.")
RunHTTPServer() RunHTTPServer()
} }
go CheckServices() CheckServices()
if !setupMode { if !setupMode {
LoadPlugins() LoadPlugins()
RunHTTPServer() RunHTTPServer()

View File

@ -173,7 +173,7 @@ func TestService_AvgTime(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
avg := service.AvgUptime() avg := service.AvgUptime()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "100.00", avg) assert.Equal(t, "100", avg)
} }
func TestService_Online24(t *testing.T) { func TestService_Online24(t *testing.T) {
@ -217,7 +217,7 @@ func TestService_Hits(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
hits, err := service.Hits() hits, err := service.Hits()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 5, len(hits)) assert.Equal(t, 0, len(hits))
} }
func TestService_LimitedHits(t *testing.T) { func TestService_LimitedHits(t *testing.T) {
@ -225,7 +225,7 @@ func TestService_LimitedHits(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
hits, err := service.LimitedHits() hits, err := service.LimitedHits()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 5, len(hits)) assert.Equal(t, 0, len(hits))
} }
func Test(t *testing.T) { func Test(t *testing.T) {

View File

@ -31,6 +31,7 @@ type Service struct {
AvgResponse string `json:"avg_response"` AvgResponse string `json:"avg_response"`
TotalUptime string `json:"uptime"` TotalUptime string `json:"uptime"`
Failures []*Failure `json:"failures"` Failures []*Failure `json:"failures"`
Checkins []*Checkin `json:"checkins"`
} }
func serviceCol() db.Collection { func serviceCol() db.Collection {
@ -41,6 +42,7 @@ func SelectService(id int64) (*Service, error) {
var service *Service var service *Service
res := serviceCol().Find("id", id) res := serviceCol().Find("id", id)
err := res.One(&service) err := res.One(&service)
service.Checkins = service.SelectAllCheckins()
return service, err return service, err
} }
@ -48,15 +50,10 @@ func SelectAllServices() ([]*Service, error) {
var services []*Service var services []*Service
col := serviceCol().Find() col := serviceCol().Find()
err := col.All(&services) err := col.All(&services)
return services, err for _, s := range services {
s.Checkins = s.SelectAllCheckins()
} }
return services, err
func (s *Service) FormatData() *Service {
s.GraphData()
s.AvgUptime()
s.Online24()
s.AvgTime()
return s
} }
func (s *Service) AvgTime() float64 { func (s *Service) AvgTime() float64 {

View File

@ -1,11 +1,13 @@
package main package main
import ( import (
"fmt"
"github.com/go-yaml/yaml" "github.com/go-yaml/yaml"
"github.com/hunterlong/statup/plugin" "github.com/hunterlong/statup/plugin"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
"strings"
"time" "time"
) )
@ -154,3 +156,96 @@ func (c *DbConfig) Save() error {
return err return err
} }
func DropDatabase() {
fmt.Println("Dropping Tables...")
down, _ := sqlBox.String("down.sql")
requests := strings.Split(down, ";")
for _, request := range requests {
_, err := dbSession.Exec(request)
if err != nil {
fmt.Println(err)
}
}
}
func CreateDatabase() {
fmt.Println("Creating Tables...")
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 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",
}
s1.Create()
s2.Create()
s3.Create()
s4.Create()
checkin := &Checkin{
Service: s2.Id,
Interval: 30,
Api: NewSHA1Hash(18),
}
checkin.Create()
for i := 0; i < 20; i++ {
s1.Check()
s2.Check()
s3.Check()
s4.Check()
}
return nil
}

View File

@ -2,4 +2,5 @@ DROP table core;
DROP table hits; DROP table hits;
DROP table failures; DROP table failures;
DROP table users; DROP table users;
DROP table checkins;
DROP table services; DROP table services;

View File

@ -47,3 +47,12 @@ CREATE TABLE failures (
INDEX (id, service), INDEX (id, service),
FOREIGN KEY (service) REFERENCES services(id) FOREIGN KEY (service) REFERENCES services(id)
); );
CREATE TABLE checkins (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
service INTEGER NOT NULL,
check_interval integer,
api text,
created_at TIMESTAMP,
INDEX (id, service),
FOREIGN KEY (service) REFERENCES services(id)
);

View File

@ -32,13 +32,6 @@ CREATE TABLE services (
created_at TIMESTAMP created_at TIMESTAMP
); );
CREATE TABLE checkins (
id SERIAL PRIMARY KEY,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
check_interval integer,
created_at TIMESTAMP
);
CREATE TABLE hits ( CREATE TABLE hits (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
@ -54,5 +47,15 @@ CREATE TABLE failures (
created_at TIMESTAMP WITHOUT TIME zone created_at TIMESTAMP WITHOUT TIME zone
); );
CREATE TABLE checkins (
id SERIAL PRIMARY KEY,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
check_interval integer,
api text,
created_at TIMESTAMP
);
CREATE INDEX idx_hits ON hits(service); CREATE INDEX idx_hits ON hits(service);
CREATE INDEX idx_failures ON failures(service); CREATE INDEX idx_failures ON failures(service);
CREATE INDEX idx_checkins ON checkins(service);

View File

@ -46,5 +46,14 @@ CREATE TABLE failures (
created_at TIMESTAMP created_at TIMESTAMP
); );
CREATE TABLE checkins (
id SERIAL PRIMARY KEY,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
check_interval integer,
api text,
created_at TIMESTAMP
);
CREATE INDEX idx_hits ON hits(service); CREATE INDEX idx_hits ON hits(service);
CREATE INDEX idx_failures ON failures(service); CREATE INDEX idx_failures ON failures(service);
CREATE INDEX idx_checkins ON checkins(service);

21
web.go
View File

@ -39,6 +39,7 @@ func Router() *mux.Router {
r.Handle("/service/{id}/delete", http.HandlerFunc(ServicesDeleteHandler)) r.Handle("/service/{id}/delete", http.HandlerFunc(ServicesDeleteHandler))
r.Handle("/service/{id}/badge.svg", http.HandlerFunc(ServicesBadgeHandler)) r.Handle("/service/{id}/badge.svg", http.HandlerFunc(ServicesBadgeHandler))
r.Handle("/service/{id}/delete_failures", http.HandlerFunc(ServicesDeleteFailuresHandler)).Methods("GET") r.Handle("/service/{id}/delete_failures", http.HandlerFunc(ServicesDeleteFailuresHandler)).Methods("GET")
r.Handle("/service/{id}/checkin", http.HandlerFunc(CheckinCreateUpdateHandler)).Methods("POST")
r.Handle("/users", http.HandlerFunc(UsersHandler)).Methods("GET") 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("/users/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET") r.Handle("/users/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET")
@ -49,6 +50,7 @@ func Router() *mux.Router {
r.Handle("/help", http.HandlerFunc(HelpHandler)) r.Handle("/help", http.HandlerFunc(HelpHandler))
r.Handle("/api", http.HandlerFunc(ApiIndexHandler)) r.Handle("/api", http.HandlerFunc(ApiIndexHandler))
r.Handle("/api/checkin/{api}", http.HandlerFunc(ApiCheckinHandler))
r.Handle("/api/services", http.HandlerFunc(ApiAllServicesHandler)) r.Handle("/api/services", http.HandlerFunc(ApiAllServicesHandler))
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceHandler)).Methods("GET") r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceHandler)).Methods("GET")
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceUpdateHandler)).Methods("POST") r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceUpdateHandler)).Methods("POST")
@ -335,6 +337,25 @@ func HelpHandler(w http.ResponseWriter, r *http.Request) {
ExecuteResponse(w, r, "help.html", nil) ExecuteResponse(w, r, "help.html", nil)
} }
func CheckinCreateUpdateHandler(w http.ResponseWriter, r *http.Request) {
auth := IsAuthenticated(r)
if !auth {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
vars := mux.Vars(r)
interval := StringInt(r.PostForm.Get("interval"))
service, _ := SelectService(StringInt(vars["id"]))
checkin := &Checkin{
Service: service.Id,
Interval: interval,
Api: NewSHA1Hash(18),
}
checkin.Create()
fmt.Println(checkin.Create())
ExecuteResponse(w, r, "service.html", service)
}
func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) { func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
//auth := IsAuthenticated(r) //auth := IsAuthenticated(r)
// //