add switch tracking support
This commit is contained in:
parent
9f0f1dbbc5
commit
9add4289cb
|
@ -11,9 +11,11 @@ import (
|
||||||
|
|
||||||
"github.com/facebookarchive/flagenv"
|
"github.com/facebookarchive/flagenv"
|
||||||
_ "github.com/joho/godotenv/autoload"
|
_ "github.com/joho/godotenv/autoload"
|
||||||
|
r "gopkg.in/rethinkdb/rethinkdb-go.v6"
|
||||||
"within.website/ln"
|
"within.website/ln"
|
||||||
"within.website/ln/ex"
|
"within.website/ln/ex"
|
||||||
"within.website/mi/rethink"
|
"within.website/mi/rethink"
|
||||||
|
"within.website/mi/switchcounter"
|
||||||
"within.website/x/web/useragent"
|
"within.website/x/web/useragent"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,6 +38,9 @@ var (
|
||||||
|
|
||||||
// meta
|
// meta
|
||||||
noPush = flag.Bool("no-push", false, "if set, don't push content")
|
noPush = flag.Bool("no-push", false, "if set, don't push content")
|
||||||
|
|
||||||
|
// switchcounter
|
||||||
|
switchFile = flag.String("switch-file", "", "if set, import switches from CSV")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -89,6 +94,28 @@ func main() {
|
||||||
mux: mux,
|
mux: mux,
|
||||||
}
|
}
|
||||||
mi.RegisterRoutes()
|
mi.RegisterRoutes()
|
||||||
|
sc := switchcounter.New(session, mux)
|
||||||
|
|
||||||
|
if *switchFile != "" {
|
||||||
|
fin, err := os.Open(*switchFile)
|
||||||
|
if err != nil {
|
||||||
|
ln.FatalErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switches, err := sc.ImportCSV(fin)
|
||||||
|
if err != nil {
|
||||||
|
ln.FatalErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(switches) == 0 {
|
||||||
|
panic("what")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.Table("switches").Insert(switches).Exec(session)
|
||||||
|
if err != nil {
|
||||||
|
ln.FatalErr(ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
go func(cf func()) {
|
go func(cf func()) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
@ -75,6 +76,10 @@ func (pm PasetoMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
goto ok
|
goto ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(r.URL.EscapedPath(), "/webhooks/") {
|
||||||
|
goto ok
|
||||||
|
}
|
||||||
|
|
||||||
err = pm.v2.Verify(tok, pm.pubKey, &newJsonToken, &newFooter)
|
err = pm.v2.Verify(tok, pm.pubKey, &newJsonToken, &newFooter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ln.Error(r.Context(), err)
|
ln.Error(r.Context(), err)
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -6,6 +6,7 @@ require (
|
||||||
christine.website v1.3.1
|
christine.website v1.3.1
|
||||||
github.com/McKael/madon v2.3.0+incompatible
|
github.com/McKael/madon v2.3.0+incompatible
|
||||||
github.com/McKael/madon/v2 v2.0.0-20180929094633-c679abc985d6
|
github.com/McKael/madon/v2 v2.0.0-20180929094633-c679abc985d6
|
||||||
|
github.com/celrenheit/sandflake v0.0.0-20190410195419-50a943690bc2
|
||||||
github.com/dghubble/go-twitter v0.0.0-20190719072343-39e5462e111f
|
github.com/dghubble/go-twitter v0.0.0-20190719072343-39e5462e111f
|
||||||
github.com/dghubble/oauth1 v0.6.0
|
github.com/dghubble/oauth1 v0.6.0
|
||||||
github.com/facebookarchive/flagenv v0.0.0-20160425205200-fcd59fca7456
|
github.com/facebookarchive/flagenv v0.0.0-20160425205200-fcd59fca7456
|
||||||
|
|
1
go.sum
1
go.sum
|
@ -26,6 +26,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/birkelund/boltdbcache v1.0.0/go.mod h1:WgJWF40tV+4K0Q7MxAPbWEIkgs4AVUB7EyKVds0EgfQ=
|
github.com/birkelund/boltdbcache v1.0.0/go.mod h1:WgJWF40tV+4K0Q7MxAPbWEIkgs4AVUB7EyKVds0EgfQ=
|
||||||
github.com/bwmarrin/discordgo v0.20.1/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
|
github.com/bwmarrin/discordgo v0.20.1/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
|
||||||
|
github.com/celrenheit/sandflake v0.0.0-20190410195419-50a943690bc2 h1:/BpnZPo/sk1vPlt62dLya5KCn7PN9ZBDrpTGlQzgUZI=
|
||||||
github.com/celrenheit/sandflake v0.0.0-20190410195419-50a943690bc2/go.mod h1:7L8gY0+4GYeBc9TvqVuDUq7tXuM6Sj7llnt7HkVwWlQ=
|
github.com/celrenheit/sandflake v0.0.0-20190410195419-50a943690bc2/go.mod h1:7L8gY0+4GYeBc9TvqVuDUq7tXuM6Sj7llnt7HkVwWlQ=
|
||||||
github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=
|
github.com/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=
|
||||||
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package switchcounter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/celrenheit/sandflake"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Switch struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Who string `json:"who"`
|
||||||
|
StartedAt time.Time `json:"started_at"`
|
||||||
|
EndedAt *time.Time `json:"ended_at"`
|
||||||
|
Duration time.Duration `json:"duration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2020-01-04 13:42:13 UTC
|
||||||
|
const timeFormat = `2006-01-02 15:04:05 MST`
|
||||||
|
|
||||||
|
func (s *Switches) ImportCSV(r io.Reader) ([]Switch, error) {
|
||||||
|
var result []Switch
|
||||||
|
rd := csv.NewReader(r)
|
||||||
|
rows, err := rd.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows[1:] {
|
||||||
|
log.Printf("%v", row)
|
||||||
|
startedAtVal := row[1]
|
||||||
|
endedAtVal := row[2]
|
||||||
|
|
||||||
|
startedAt, err := time.Parse(timeFormat, startedAtVal)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
endedAt, err := time.Parse(timeFormat, endedAtVal)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lenSeconds, err := strconv.ParseInt(row[3], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
g := sandflake.NewFixedTimeGenerator(startedAt)
|
||||||
|
id := g.Next()
|
||||||
|
sw := Switch{
|
||||||
|
ID: id.String(),
|
||||||
|
Who: row[0],
|
||||||
|
StartedAt: startedAt,
|
||||||
|
EndedAt: &endedAt,
|
||||||
|
Duration: time.Duration(lenSeconds) * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, sw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package switchcounter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
r "gopkg.in/rethinkdb/rethinkdb-go.v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Switches struct {
|
||||||
|
session *r.Session
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(session *r.Session, mux *http.ServeMux) *Switches {
|
||||||
|
result := &Switches{
|
||||||
|
session: session,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/switches/", result.GetSwitches)
|
||||||
|
mux.HandleFunc("/switches/current", result.Current)
|
||||||
|
mux.HandleFunc("/switches/switch", result.RegisterSwitch)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
package switchcounter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/celrenheit/sandflake"
|
||||||
|
r "gopkg.in/rethinkdb/rethinkdb-go.v6"
|
||||||
|
"within.website/ln"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Switches) Get(ctx context.Context, limit, page int) ([]Switch, error) {
|
||||||
|
var result []Switch
|
||||||
|
res, err := r.Table("switches").
|
||||||
|
OrderBy(r.Desc("started_at")).
|
||||||
|
Limit(limit).
|
||||||
|
Skip(page * limit).
|
||||||
|
Run(s.session)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = res.All(&result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Switches) Switch(ctx context.Context, who string) (Switch, Switch, error) {
|
||||||
|
var lastSw Switch
|
||||||
|
var currentSw Switch
|
||||||
|
|
||||||
|
res, err := r.Table("switches").
|
||||||
|
OrderBy(r.Desc("started_at")).
|
||||||
|
Limit(1).
|
||||||
|
Run(s.session)
|
||||||
|
if err != nil {
|
||||||
|
return lastSw, currentSw, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = res.One(&lastSw)
|
||||||
|
if err != nil {
|
||||||
|
return lastSw, currentSw, err
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().UTC()
|
||||||
|
lastSw.EndedAt = &now
|
||||||
|
lastSw.Duration = now.Sub(lastSw.StartedAt).Round(time.Second)
|
||||||
|
err = r.Table("switches").
|
||||||
|
Update(lastSw).
|
||||||
|
Exec(s.session)
|
||||||
|
if err != nil {
|
||||||
|
return lastSw, currentSw, err
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSw = Switch{
|
||||||
|
ID: sandflake.Next().String(),
|
||||||
|
Who: who,
|
||||||
|
StartedAt: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.Table("switches").
|
||||||
|
Insert(currentSw).
|
||||||
|
Exec(s.session)
|
||||||
|
if err != nil {
|
||||||
|
return lastSw, currentSw, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastSw, currentSw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Switches) Current(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
var result Switch
|
||||||
|
res, err := r.Table("switches").
|
||||||
|
OrderBy(r.Desc("started_at")).
|
||||||
|
Limit(1).
|
||||||
|
Run(s.session)
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(req.Context(), err)
|
||||||
|
http.Error(rw, "can't get data", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = res.One(&result)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch req.Header.Get("Accept") {
|
||||||
|
case "text/plain":
|
||||||
|
http.Error(rw, result.Who, http.StatusOK)
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
json.NewEncoder(rw).Encode(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Switches) GetSwitches(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
var (
|
||||||
|
limit = 40
|
||||||
|
page = 0
|
||||||
|
)
|
||||||
|
q := req.URL.Query()
|
||||||
|
|
||||||
|
if val := q.Get("limit"); val != "" {
|
||||||
|
i, err := strconv.Atoi(val)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, "limit is bad: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
limit = i
|
||||||
|
}
|
||||||
|
|
||||||
|
if val := q.Get("page"); val != "" {
|
||||||
|
i, err := strconv.Atoi(val)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, "page is bad: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
page = i
|
||||||
|
}
|
||||||
|
|
||||||
|
switches, err := s.Get(req.Context(), limit, page)
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(req.Context(), err)
|
||||||
|
http.Error(rw, "can't get data", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
json.NewEncoder(rw).Encode(switches)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Switches) RegisterSwitch(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
who, err := ioutil.ReadAll(req.Body)
|
||||||
|
defer req.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
last, current, err := s.Switch(req.Context(), string(who))
|
||||||
|
if err != nil {
|
||||||
|
ln.Error(req.Context(), err)
|
||||||
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
json.NewEncoder(rw).Encode(struct {
|
||||||
|
Last Switch `json:"last"`
|
||||||
|
Current Switch `json:"current"`
|
||||||
|
}{
|
||||||
|
Last: last,
|
||||||
|
Current: current,
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue