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
|
### Count
|
||||||
|
|
||||||
API Key must have the `Count` permission.
|
API Key must have the `count` permission.
|
||||||
|
|
||||||
Request:
|
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
|
### User Count
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ArchivedCrash struct {
|
type ArchivedCrash struct {
|
||||||
@@ -163,6 +164,38 @@ func (b *Backend) actualCrashArchive(w http.ResponseWriter, ap App, toArchive Ar
|
|||||||
}
|
}
|
||||||
err := crash.Archive(toArchive)
|
err := crash.Archive(toArchive)
|
||||||
if err != nil {
|
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 {
|
if hasLog {
|
||||||
b.m.HandleFunc("POST /count/{uuid}", b.countLog)
|
b.m.HandleFunc("POST /count", b.countLog)
|
||||||
b.m.HandleFunc("GET /count", b.getCount)
|
b.m.HandleFunc("GET /count", b.getCount)
|
||||||
}
|
}
|
||||||
if hasCrash {
|
if hasCrash {
|
||||||
@@ -59,8 +59,7 @@ func NewBackend(keyTable Table[ApiKey], apps ...App) (*Backend, error) {
|
|||||||
|
|
||||||
func (b *Backend) cleanupLoop() {
|
func (b *Backend) cleanupLoop() {
|
||||||
for range time.Tick(24 * time.Hour) {
|
for range time.Tick(24 * time.Hour) {
|
||||||
oldTim := time.Now().Add(-30 * 24 * time.Hour)
|
old := getDate(time.Now().Add(-30 * 24 * time.Hour))
|
||||||
old := (oldTim.Year() * 10000) + (int(oldTim.Month()) * 100) + oldTim.Day()
|
|
||||||
for _, a := range b.apps {
|
for _, a := range b.apps {
|
||||||
tab := a.CountTable()
|
tab := a.CountTable()
|
||||||
if tab == nil {
|
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) {
|
func (b *Backend) EnableManagementKey(managementID string) {
|
||||||
b.managementKeyID = managementID
|
b.managementKeyID = managementID
|
||||||
b.m.HandleFunc("DELETE /{appID}/crash/{crashID}", b.managementDeleteCrash)
|
b.m.HandleFunc("DELETE /{appID}/crash/{crashID}", b.managementDeleteCrash)
|
||||||
|
|||||||
@@ -1,10 +1,23 @@
|
|||||||
package darkstorm
|
package darkstorm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(t *testing.M) {
|
func TestStuff(t *testing.T) {
|
||||||
os.Exit(t.Run())
|
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]
|
Table[CountLog]
|
||||||
// Remove all Log items that have a CountLog.Date value less then the given value.
|
// Remove all Log items that have a CountLog.Date value less then the given value.
|
||||||
RemoveOldLogs(date int)
|
RemoveOldLogs(date int)
|
||||||
|
// Get count. If platform is an empty string or "all", the full count should be given
|
||||||
Count(platform string) int
|
Count(platform string) int
|
||||||
}
|
}
|
||||||
|
|
||||||
type CrashTable interface {
|
type CrashTable interface {
|
||||||
Table[CrashReport]
|
Table[CrashReport]
|
||||||
// Move a crash type to archive. All instances that perfectly match that appear in CrashReport.Individual should be deleted.
|
// Move a crash type to archive. Crashes that match the archived crash will be automatically removed from the CrashTable.
|
||||||
// If a CrashReport ends up with an empty Individual array it should also be deleted.
|
|
||||||
Archive(ArchivedCrash) error
|
Archive(ArchivedCrash) error
|
||||||
IsArchived(IndividualCrash) bool
|
IsArchived(IndividualCrash) bool
|
||||||
// Add the IndividualCrash report to the crash table. If a CrashReport exists that matches, then it gets added to CrashReport.Individual.
|
// 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
|
package darkstorm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CountLog struct {
|
type CountLog struct {
|
||||||
ID string
|
ID string `json:"id" bson:"_id"`
|
||||||
Platform string
|
Platform string `json:"platform" bson:"platform"`
|
||||||
Date int
|
Date int `json:"date" bson:"date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c CountLog) GetID() string {
|
func (c CountLog) GetID() string {
|
||||||
return c.ID
|
return c.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type countLogReq struct {
|
||||||
|
ID string
|
||||||
|
Platform string
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Backend) countLog(w http.ResponseWriter, r *http.Request) {
|
func (b *Backend) countLog(w http.ResponseWriter, r *http.Request) {
|
||||||
hdr, err := b.VerifyHeader(w, r, "count", false)
|
hdr, err := b.VerifyHeader(w, r, "count", false)
|
||||||
if hdr == nil {
|
if hdr == nil {
|
||||||
@@ -23,7 +32,69 @@ func (b *Backend) countLog(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
return
|
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) {
|
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 {
|
} else {
|
||||||
ap = b.GetApp(hdr.Key)
|
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