Skip to content

Commit

Permalink
Merge pull request #1019 from stakwork/feat/track_metrics
Browse files Browse the repository at this point in the history
Feat/track metrics
  • Loading branch information
elraphty authored Dec 13, 2023
2 parents 508b902 + 1844321 commit ab480c0
Show file tree
Hide file tree
Showing 20 changed files with 1,019 additions and 12 deletions.
4 changes: 4 additions & 0 deletions Contribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@
- Folders should be named in camel case i.e (peopleData)
- Typescript files should be named in camel case also
- Only the index.tsx files should be named in small letters

### Prettier Fixing

- Make sure you run ```yarn run prettier``` to fix prettier error before submitting
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,34 @@ Meme image upload works with Relay enabled, so a running Relay is required for M
MEME_URL=
```

### Add REDIS for cache

- Create a Redis instance
- Create a .env file and populate the .env files with these variables

If you have a Redis url add the REDIS_URL variable to .env

```REDIS_URL = ```

else add these variables to the env to enable Redis

```
REDIS_HOST =
REDIS_DB =
REDIS_USER =
REDIS_PASS =
```

### Add SuperAdmins to access admin dashboard

Add comma separated public keys to the SUPER_ADMINS env var in the .env file,
any user public key added to this comaa separated strings will have access to the admin dashboard
e.g '{pubkey}, {pubkey}, {pubkey}'

```
ADMINS
```

### For Contributions

Read the contribution doc [here](./Contribution.md)
Expand Down
73 changes: 73 additions & 0 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,79 @@ func PubKeyContext(next http.Handler) http.Handler {
})
}

// PubKeyContext parses pukey from signed timestamp
func PubKeyContextSuperAdmin(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.URL.Query().Get("token")
if token == "" {
token = r.Header.Get("x-jwt")
}

if token == "" {
fmt.Println("[auth] no token")
http.Error(w, http.StatusText(401), 401)
return
}

isJwt := strings.Contains(token, ".") && !strings.HasPrefix(token, ".")

if isJwt {
claims, err := DecodeJwt(token)

if err != nil {
fmt.Println("Failed to parse JWT")
http.Error(w, http.StatusText(401), 401)
return
}

if claims.VerifyExpiresAt(time.Now().UnixNano(), true) {
fmt.Println("Token has expired")
http.Error(w, http.StatusText(401), 401)
return
}

pubkey := fmt.Sprintf("%v", claims["pubkey"])
if !AdminCheck(pubkey) {
fmt.Println("Not a super admin")
http.Error(w, http.StatusText(401), 401)
return
}

ctx := context.WithValue(r.Context(), ContextKey, claims["pubkey"])
next.ServeHTTP(w, r.WithContext(ctx))
} else {
pubkey, err := VerifyTribeUUID(token, true)

if pubkey == "" || err != nil {
fmt.Println("[auth] no pubkey || err != nil")
if err != nil {
fmt.Println(err)
}
http.Error(w, http.StatusText(401), 401)
return
}

if !AdminCheck(pubkey) {
fmt.Println("Not a super admin")
http.Error(w, http.StatusText(401), 401)
return
}

ctx := context.WithValue(r.Context(), ContextKey, pubkey)
next.ServeHTTP(w, r.WithContext(ctx))
}
})
}

func AdminCheck(pubkey string) bool {
for _, val := range config.SuperAdmins {
if val == pubkey {
return true
}
}
return false
}

// VerifyTribeUUID takes base64 uuid and returns hex pubkey
func VerifyTribeUUID(uuid string, checkTimestamp bool) (string, error) {

Expand Down
25 changes: 25 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var RelayUrl string
var MemeUrl string
var RelayAuthKey string
var RelayNodeKey string
var SuperAdmins []string = []string{""}

// these are constants for the store
var InvoiceList = "INVOICELIST"
Expand All @@ -29,6 +30,9 @@ func InitConfig() {
RelayUrl = os.Getenv("RELAY_URL")
MemeUrl = os.Getenv("MEME_URL")
RelayAuthKey = os.Getenv("RELAY_AUTH_KEY")
AdminStrings := os.Getenv("ADMINS")
// Add to super admins
SuperAdmins = StripSuperAdmins(AdminStrings)

// only make this call if there is a Relay auth key
if RelayAuthKey != "" {
Expand All @@ -46,7 +50,28 @@ func InitConfig() {
if JwtKey == "" {
JwtKey = GenerateRandomString()
}
}

func StripSuperAdmins(adminStrings string) []string {
superAdmins := []string{}
if adminStrings != "" {
if strings.Contains(adminStrings, ",") {
splitArray := strings.Split(adminStrings, ",")
splitLength := len(splitArray)

for i := 0; i < splitLength; i++ {
// append indexes, and skip all the commas
if splitArray[i] == "," {
continue
} else {
superAdmins = append(superAdmins, strings.TrimSpace(splitArray[i]))
}
}
} else {
superAdmins = append(superAdmins, strings.TrimSpace(adminStrings))
}
}
return superAdmins
}

func GenerateRandomString() string {
Expand Down
24 changes: 21 additions & 3 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package config

import (
"fmt"
"github.com/h2non/gock"
"os"
"testing"

"github.com/h2non/gock"
"github.com/stretchr/testify/assert"
)

func TestInitConfig(t *testing.T) {
Expand Down Expand Up @@ -46,8 +48,6 @@ func TestGenerateRandomString(t *testing.T) {
func TestGetNodePubKey(t *testing.T) {
defer gock.Off()

//response := map[string]string{"identity_pubkey": "1234"}
//success := map[string]bool{"success": true}
response := NodeGetInfoResponse{IdentityPubkey: "1234"}
nodeGetInfo := NodeGetInfo{Success: true, Response: response}

Expand All @@ -66,3 +66,21 @@ func TestGetNodePubKey(t *testing.T) {
}

}

func TestStripSuperAdmins(t *testing.T) {
testAdminList := "hello, hi, yes, now"
admins := StripSuperAdmins(testAdminList)
assert.Equal(t, len(admins), 4)

testAdminNocomma := "hello"
adminsNoComma := StripSuperAdmins(testAdminNocomma)
assert.Equal(t, len(adminsNoComma), 1)

testNoAdmins := ""
noAdmins := StripSuperAdmins(testNoAdmins)
assert.Equal(t, len(noAdmins), 0)

test2Admins := "hello, hi"
admins2 := StripSuperAdmins(test2Admins)
assert.Equal(t, len(admins2), 2)
}
156 changes: 156 additions & 0 deletions db/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package db

import (
"fmt"
"math"
"net/http"

"github.com/stakwork/sphinx-tribes/utils"
)

var SecondsToDateConversion = 60 * 60 * 24

func (db database) TotalPeopleByDateRange(r PaymentDateRange) int64 {
var count int64
db.db.Model(&Person{}).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Count(&count)
return count
}

func (db database) TotalOrganizationsByDateRange(r PaymentDateRange) int64 {
var count int64
db.db.Model(&Organization{}).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Count(&count)
return count
}

func (db database) TotalPaymentsByDateRange(r PaymentDateRange) uint {
var sum uint
db.db.Model(&PaymentHistory{}).Where("payment_type = ?", r.PaymentType).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Select("SUM(amount)").Row().Scan(&sum)
return sum
}

func (db database) TotalSatsPosted(r PaymentDateRange) uint {
var sum uint
db.db.Model(&Bounty{}).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Select("SUM(price)").Row().Scan(&sum)
return sum
}

func (db database) TotalSatsPaid(r PaymentDateRange) uint {
var sum uint
db.db.Model(&Bounty{}).Where("paid = ?", true).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Select("SUM(price)").Row().Scan(&sum)
return sum
}

func (db database) SatsPaidPercentage(r PaymentDateRange) uint {
satsPosted := DB.TotalSatsPosted(r)
satsPaid := DB.TotalSatsPaid(r)
if satsPaid != 0 && satsPosted != 0 {
value := (satsPaid * 100) / satsPosted
paidPercentage := math.Round(float64(value))
return uint(paidPercentage)
}
return 0
}

func (db database) TotalPaidBounties(r PaymentDateRange) int64 {
var count int64
db.db.Model(&Bounty{}).Where("paid = ?", true).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Count(&count)
return count
}

func (db database) TotalBountiesPosted(r PaymentDateRange) int64 {
var count int64
db.db.Model(&Bounty{}).Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Count(&count)
return count
}

func (db database) BountiesPaidPercentage(r PaymentDateRange) uint {
bountiesPosted := DB.TotalBountiesPosted(r)
bountiesPaid := DB.TotalPaidBounties(r)
if bountiesPaid != 0 && bountiesPosted != 0 {
value := bountiesPaid * 100 / bountiesPosted
paidPercentage := math.Round(float64(value))
return uint(paidPercentage)
}
return 0
}

func (db database) PaidDifference(r PaymentDateRange) []DateDifference {
ms := []DateDifference{}

db.db.Raw(`SELECT EXTRACT(EPOCH FROM (paid_date - TO_TIMESTAMP(created))) as diff FROM public.bounty WHERE paid_date IS NOT NULL AND created >= '` + r.StartDate + `' AND created <= '` + r.EndDate + `' `).Find(&ms)
return ms
}

func (db database) PaidDifferenceCount(r PaymentDateRange) int64 {
var count int64
list := db.PaidDifference(r)
count = int64(len(list))
return count
}

func (db database) AveragePaidTime(r PaymentDateRange) uint {
paidList := DB.PaidDifference(r)
paidCount := DB.PaidDifferenceCount(r)
var paidSum uint
for _, diff := range paidList {
paidSum = uint(math.Round(diff.Diff))
}
return CalculateAverageDays(paidCount, paidSum)
}

func (db database) CompletedDifference(r PaymentDateRange) []DateDifference {
ms := []DateDifference{}

db.db.Raw(`SELECT EXTRACT(EPOCH FROM (completion_date - TO_TIMESTAMP(created))) as diff FROM public.bounty WHERE completion_date IS NOT NULL AND created >= '` + r.StartDate + `' AND created <= '` + r.EndDate + `' `).Find(&ms)
return ms
}

func (db database) CompletedDifferenceCount(r PaymentDateRange) int64 {
var count int64
list := db.CompletedDifference(r)
count = int64(len(list))
return count
}

func (db database) AverageCompletedTime(r PaymentDateRange) uint {
paidList := DB.CompletedDifference(r)
paidCount := DB.CompletedDifferenceCount(r)
var paidSum uint
for _, diff := range paidList {
paidSum = uint(math.Round(diff.Diff))
}
return CalculateAverageDays(paidCount, paidSum)
}

func CalculateAverageDays(paidCount int64, paidSum uint) uint {
if paidCount != 0 && paidSum != 0 {
avg := paidSum / uint(paidCount)
avgSeconds := math.Round(float64(avg))
avgDays := math.Round(avgSeconds / float64(SecondsToDateConversion))
return uint(avgDays)
}
return 0
}

func (db database) GetBountiesByDateRange(r PaymentDateRange, re *http.Request) []Bounty {
offset, limit, sortBy, direction, _ := utils.GetPaginationParams(re)

orderQuery := ""
limitQuery := ""

if sortBy != "" && direction != "" {
orderQuery = "ORDER BY " + "body." + sortBy + " " + direction
} else {
orderQuery = " ORDER BY " + "body." + sortBy + "" + "DESC"
}
if limit != 0 {
limitQuery = fmt.Sprintf("LIMIT %d OFFSET %d", limit, offset)
}

query := `SELECT * public.bounty WHERE created >= '` + r.StartDate + `' AND created <= '` + r.EndDate + `'`
allQuery := query + " " + " " + orderQuery + " " + limitQuery

b := []Bounty{}
db.db.Raw(allQuery).Scan(&b)
return b
}
Loading

0 comments on commit ab480c0

Please sign in to comment.