Added counting
Changed Count post URL Archiving crashes now removes crashes that match the archive
This commit is contained in:
@@ -118,11 +118,26 @@ If an error status code is returned then the body will be as follows.
|
||||
|
||||
### Count
|
||||
|
||||
API Key must have the `Count` permission.
|
||||
API Key must have the `count` permission.
|
||||
|
||||
Request:
|
||||
|
||||
> POST: /count/{uuid}
|
||||
> POST: /count
|
||||
|
||||
```json
|
||||
{
|
||||
id: "uuid", // Should be an empty string on first request. If invalid or too old, a new UUID will be returned.
|
||||
platform: "web"
|
||||
}
|
||||
```
|
||||
|
||||
Returns:
|
||||
|
||||
```json
|
||||
{
|
||||
id: "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
### User Count
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ArchivedCrash struct {
|
||||
@@ -163,6 +164,38 @@ func (b *Backend) actualCrashArchive(w http.ResponseWriter, ap App, toArchive Ar
|
||||
}
|
||||
err := crash.Archive(toArchive)
|
||||
if err != nil {
|
||||
log.Println()
|
||||
log.Println("error archive crash:", err)
|
||||
return
|
||||
}
|
||||
first, _, _ := strings.Cut(toArchive.Stack, "\n")
|
||||
crashes, err := crash.Find(map[string]any{"error": toArchive.Error, "firstLine": first})
|
||||
if err == ErrNotFound {
|
||||
return
|
||||
} else if err != nil {
|
||||
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||
return
|
||||
}
|
||||
for _, c := range crashes {
|
||||
ogLen := len(c.Individual)
|
||||
for i := 0; i < len(c.Individual); i++ {
|
||||
ind := c.Individual[i]
|
||||
if ind.Stack == toArchive.Stack {
|
||||
if toArchive.Platform == "all" || toArchive.Platform == ind.Platform {
|
||||
c.Individual = append(c.Individual[:i], c.Individual[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(c.Individual) == 0 {
|
||||
err = crash.Remove(c.ID)
|
||||
if err != nil {
|
||||
log.Println("error removing empty crash report:", err)
|
||||
}
|
||||
} else if len(c.Individual) < ogLen {
|
||||
err = crash.PartUpdate(c.ID, map[string]any{"individual": c.Individual})
|
||||
if err != nil {
|
||||
log.Println("error updating individual crash reports:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewBackend(keyTable Table[ApiKey], apps ...App) (*Backend, error) {
|
||||
}
|
||||
}
|
||||
if hasLog {
|
||||
b.m.HandleFunc("POST /count/{uuid}", b.countLog)
|
||||
b.m.HandleFunc("POST /count", b.countLog)
|
||||
b.m.HandleFunc("GET /count", b.getCount)
|
||||
}
|
||||
if hasCrash {
|
||||
@@ -59,8 +59,7 @@ func NewBackend(keyTable Table[ApiKey], apps ...App) (*Backend, error) {
|
||||
|
||||
func (b *Backend) cleanupLoop() {
|
||||
for range time.Tick(24 * time.Hour) {
|
||||
oldTim := time.Now().Add(-30 * 24 * time.Hour)
|
||||
old := (oldTim.Year() * 10000) + (int(oldTim.Month()) * 100) + oldTim.Day()
|
||||
old := getDate(time.Now().Add(-30 * 24 * time.Hour))
|
||||
for _, a := range b.apps {
|
||||
tab := a.CountTable()
|
||||
if tab == nil {
|
||||
@@ -71,6 +70,10 @@ func (b *Backend) cleanupLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
func getDate(t time.Time) int {
|
||||
return (t.Year() * 10000) + (int(t.Month()) * 100) + t.Day()
|
||||
}
|
||||
|
||||
func (b *Backend) EnableManagementKey(managementID string) {
|
||||
b.managementKeyID = managementID
|
||||
b.m.HandleFunc("DELETE /{appID}/crash/{crashID}", b.managementDeleteCrash)
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
package darkstorm
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.M) {
|
||||
os.Exit(t.Run())
|
||||
func TestStuff(t *testing.T) {
|
||||
for i := 0; i < 50; i++ {
|
||||
go func() {
|
||||
id, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(id.String())
|
||||
}()
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
t.Fatal("end")
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ type CountTable interface {
|
||||
Table[CountLog]
|
||||
// Remove all Log items that have a CountLog.Date value less then the given value.
|
||||
RemoveOldLogs(date int)
|
||||
// Get count. If platform is an empty string or "all", the full count should be given
|
||||
Count(platform string) int
|
||||
}
|
||||
|
||||
type CrashTable interface {
|
||||
Table[CrashReport]
|
||||
// Move a crash type to archive. All instances that perfectly match that appear in CrashReport.Individual should be deleted.
|
||||
// If a CrashReport ends up with an empty Individual array it should also be deleted.
|
||||
// Move a crash type to archive. Crashes that match the archived crash will be automatically removed from the CrashTable.
|
||||
Archive(ArchivedCrash) error
|
||||
IsArchived(IndividualCrash) bool
|
||||
// Add the IndividualCrash report to the crash table. If a CrashReport exists that matches, then it gets added to CrashReport.Individual.
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
package darkstorm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type CountLog struct {
|
||||
ID string
|
||||
Platform string
|
||||
Date int
|
||||
ID string `json:"id" bson:"_id"`
|
||||
Platform string `json:"platform" bson:"platform"`
|
||||
Date int `json:"date" bson:"date"`
|
||||
}
|
||||
|
||||
func (c CountLog) GetID() string {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
type countLogReq struct {
|
||||
ID string
|
||||
Platform string
|
||||
}
|
||||
|
||||
func (b *Backend) countLog(w http.ResponseWriter, r *http.Request) {
|
||||
hdr, err := b.VerifyHeader(w, r, "count", false)
|
||||
if hdr == nil {
|
||||
@@ -23,7 +32,69 @@ func (b *Backend) countLog(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
return
|
||||
}
|
||||
//TODO
|
||||
defer r.Body.Close()
|
||||
var req countLogReq
|
||||
err = json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil || req.Platform == "" {
|
||||
ReturnError(w, http.StatusBadRequest, "invalidBody", "Bad request")
|
||||
return
|
||||
}
|
||||
ap := b.GetApp(hdr.Key)
|
||||
count := ap.CountTable()
|
||||
if count == nil {
|
||||
ReturnError(w, http.StatusInternalServerError, "misconfigured", "Server Misconfigured")
|
||||
return
|
||||
}
|
||||
curDate := getDate(time.Now())
|
||||
if req.ID == "" {
|
||||
err = addToCountTable(w, count, req.Platform, curDate)
|
||||
if err != nil {
|
||||
log.Println("error adding to count table:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
l, err := count.Get(req.ID)
|
||||
if err == ErrNotFound {
|
||||
err = addToCountTable(w, count, req.Platform, curDate)
|
||||
if err != nil {
|
||||
log.Println("error adding to count table:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if l.Date >= curDate {
|
||||
json.NewEncoder(w).Encode(map[string]string{"id": req.ID})
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
return
|
||||
}
|
||||
err = count.PartUpdate(req.ID, map[string]any{"date": curDate})
|
||||
if err != nil {
|
||||
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(map[string]string{"id": req.ID})
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
||||
func addToCountTable(w http.ResponseWriter, c CountTable, platform string, curDate int) error {
|
||||
id, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
log.Println("error generating new log UUID:", err)
|
||||
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||
return err
|
||||
}
|
||||
err = c.Insert(CountLog{
|
||||
ID: id.String(),
|
||||
Platform: platform,
|
||||
Date: curDate,
|
||||
})
|
||||
if err != nil {
|
||||
log.Println("error inserting new count log:", err)
|
||||
ReturnError(w, http.StatusInternalServerError, "internal", "Server error")
|
||||
return err
|
||||
}
|
||||
json.NewEncoder(w).Encode(map[string]string{"id": id.String()})
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) getCount(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -44,5 +115,11 @@ func (b *Backend) getCount(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
ap = b.GetApp(hdr.Key)
|
||||
}
|
||||
//TODO
|
||||
count := ap.CountTable()
|
||||
if count == nil {
|
||||
ReturnError(w, http.StatusBadRequest, "badRequest", "Trying to get user count on app that doesn't have a count table")
|
||||
return
|
||||
}
|
||||
out := count.Count(r.URL.Query().Get("platform"))
|
||||
json.NewEncoder(w).Encode(map[string]int{"count": out})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user