Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/track metrics #1019

Merged
merged 36 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9ca7463
started metrics
elraphty Oct 26, 2023
f9a74fe
added redis
elraphty Oct 27, 2023
f8f6d2c
added redis get and set functions
elraphty Oct 27, 2023
3b5bc15
added Redis map
elraphty Oct 27, 2023
bd57716
modified redis
elraphty Oct 28, 2023
504f0ee
Merge branch 'master' into feat/track_metrics
elraphty Oct 30, 2023
af51e07
added endpoints for metrics
elraphty Oct 30, 2023
fd2fc0e
merged master
elraphty Oct 30, 2023
452c1c2
merged master
elraphty Nov 22, 2023
d27e816
added bounty metrics
elraphty Nov 23, 2023
153c154
added column for date difference
elraphty Nov 24, 2023
c1a27f9
added completetion_date difference
elraphty Nov 24, 2023
bff247e
Merge branch 'master' into feat/track_metrics
elraphty Nov 26, 2023
cfcc9c1
added metrics to redis
elraphty Nov 26, 2023
6b48054
refactored redis
elraphty Nov 26, 2023
a2dedb0
Merge branch 'master' into feat/track_metrics
elraphty Nov 28, 2023
f54d142
added bounty by range
elraphty Nov 28, 2023
9e8b87a
enforce price in number
elraphty Nov 28, 2023
2585fda
added prettier info
elraphty Nov 28, 2023
d4d56e5
renamed routes
elraphty Nov 28, 2023
e996718
added Redis to Readme
elraphty Nov 29, 2023
af9c997
used completed functions
elraphty Nov 29, 2023
85c3426
added more test for the difference function
elraphty Nov 29, 2023
4f082eb
added tests for all helpers
elraphty Nov 30, 2023
089d952
added superadmin middleware
elraphty Nov 30, 2023
0de702d
added superadmin middleware functionality
elraphty Nov 30, 2023
d03c108
Merge branch 'master' into feat/track_metrics
elraphty Dec 3, 2023
1792647
added bounty data for metrics
elraphty Dec 3, 2023
42ebd4e
added SUPER_ADMINS to README
elraphty Dec 3, 2023
7fb0e88
slight changes
elraphty Dec 4, 2023
b1ce47b
refactored the metrics average sum
elraphty Dec 6, 2023
7c74653
uncommented redis
elraphty Dec 6, 2023
28b7092
added Redis error clause to metrics code
elraphty Dec 13, 2023
84fa142
fixed merge conflicts
elraphty Dec 13, 2023
78c81b9
fixed config test error
elraphty Dec 13, 2023
1844321
change SUPER_ADMINS to ADMINS
elraphty Dec 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,24 @@ 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 =
```

### 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("SUPER_ADMINS")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you can add this ENV var to the README and what it is used for that would be helpful

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elraphty can you change this value to just ADMINS aswell SUPER_ADMINS seems a bit much especially since we don't even have the concept of admins implemented yet

// 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, splitArray[i])
}
}
} else {
superAdmins = append(superAdmins, adminStrings)
}
}
return superAdmins
}

func GenerateRandomString() string {
Expand Down
24 changes: 23 additions & 1 deletion config/config_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package config

import "testing"
import (
"testing"

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

func TestInitConfig(t *testing.T) {
InitConfig()
Expand All @@ -17,3 +21,21 @@ func TestInitConfig(t *testing.T) {
t.Error("Could not load random jwtKey")
}
}

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)
}
146 changes: 146 additions & 0 deletions db/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package db

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

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

func (db database) TotalPeopleByDateRange(r PaymentDateRange) int64 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont see this being used anywhere

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) PaidDifferenceSum(r PaymentDateRange) uint {
var sum uint
db.db.Model(&Bounty{}).Where("paid_date_difference != ?",
"").Where("created >= ?", r.StartDate).Where("created <= ?", r.EndDate).Select("SUM(paid_date_difference)").Row().Scan(&sum)
return sum
}

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

func (db database) AveragePaidTime(r PaymentDateRange) uint {
paidSum := DB.PaidDifferenceSum(r)
paidCount := DB.PaidDifferenceCount(r)
if paidCount != 0 && paidSum != 0 {
avg := paidSum / uint(paidCount)
avgDays := math.Round(float64(avg))
return uint(avgDays)
}
return 0
}

func (db database) CompletedDifferenceSum(r PaymentDateRange) uint {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont see this being used anywhere

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

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

func (db database) AverageCompletedTime(r PaymentDateRange) uint {
paidSum := DB.CompletedDifferenceSum(r)
paidCount := DB.CompletedDifferenceCount(r)
if paidCount != 0 && paidSum != 0 {
avg := paidSum / uint(paidCount)
avgDays := math.Round(float64(avg))
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
Loading