add switch tracking support

This commit is contained in:
Cadey Ratio 2020-01-11 21:14:56 -05:00
parent 9f0f1dbbc5
commit 9add4289cb
7 changed files with 283 additions and 0 deletions

View File

@ -11,9 +11,11 @@ import (
"github.com/facebookarchive/flagenv"
_ "github.com/joho/godotenv/autoload"
r "gopkg.in/rethinkdb/rethinkdb-go.v6"
"within.website/ln"
"within.website/ln/ex"
"within.website/mi/rethink"
"within.website/mi/switchcounter"
"within.website/x/web/useragent"
)
@ -36,6 +38,9 @@ var (
// meta
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() {
@ -89,6 +94,28 @@ func main() {
mux: mux,
}
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)
go func(cf func()) {

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"flag"
"net/http"
"strings"
"time"
"github.com/google/uuid"
@ -75,6 +76,10 @@ func (pm PasetoMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
goto ok
}
if strings.HasPrefix(r.URL.EscapedPath(), "/webhooks/") {
goto ok
}
err = pm.v2.Verify(tok, pm.pubKey, &newJsonToken, &newFooter)
if err != nil {
ln.Error(r.Context(), err)

1
go.mod
View File

@ -6,6 +6,7 @@ require (
christine.website v1.3.1
github.com/McKael/madon v2.3.0+incompatible
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/oauth1 v0.6.0
github.com/facebookarchive/flagenv v0.0.0-20160425205200-fcd59fca7456

1
go.sum
View File

@ -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/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/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/cenkalti/backoff v2.0.0+incompatible h1:5IIPUHhlnUZbcHQsQou5k1Tn58nJkeJL9U+ig5CHJbY=
github.com/cenkalti/backoff v2.0.0+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=

66
switchcounter/ingest.go Normal file
View File

@ -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
}

23
switchcounter/storage.go Normal file
View File

@ -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
}

160
switchcounter/switch.go Normal file
View File

@ -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,
})
}