mirror of
https://github.com/0ceanSlim/grain.git
synced 2024-11-22 08:37:13 +00:00
200 lines
5.2 KiB
Go
200 lines
5.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"grain/config"
|
|
"grain/server/db"
|
|
"grain/server/handlers/kinds"
|
|
"grain/server/handlers/response"
|
|
"grain/server/utils"
|
|
"strconv"
|
|
|
|
relay "grain/server/types"
|
|
|
|
"golang.org/x/net/websocket"
|
|
)
|
|
|
|
func HandleEvent(ws *websocket.Conn, message []interface{}) {
|
|
if len(message) != 2 {
|
|
fmt.Println("Invalid EVENT message format")
|
|
response.SendNotice(ws, "", "Invalid EVENT message format")
|
|
return
|
|
}
|
|
|
|
eventData, ok := message[1].(map[string]interface{})
|
|
if !ok {
|
|
fmt.Println("Invalid event data format")
|
|
response.SendNotice(ws, "", "Invalid event data format")
|
|
return
|
|
}
|
|
eventBytes, err := json.Marshal(eventData)
|
|
if err != nil {
|
|
fmt.Println("Error marshaling event data:", err)
|
|
response.SendNotice(ws, "", "Error marshaling event data")
|
|
return
|
|
}
|
|
|
|
var evt relay.Event
|
|
err = json.Unmarshal(eventBytes, &evt)
|
|
if err != nil {
|
|
fmt.Println("Error unmarshaling event data:", err)
|
|
response.SendNotice(ws, "", "Error unmarshaling event data")
|
|
return
|
|
}
|
|
|
|
eventSize := len(eventBytes) // Calculate event size
|
|
HandleKind(context.TODO(), evt, ws, eventSize)
|
|
|
|
fmt.Println("Event processed:", evt.ID)
|
|
}
|
|
|
|
func HandleKind(ctx context.Context, evt relay.Event, ws *websocket.Conn, eventSize int) {
|
|
if !utils.CheckSignature(evt) {
|
|
response.SendOK(ws, evt.ID, false, "invalid: signature verification failed")
|
|
return
|
|
}
|
|
|
|
collection := db.GetCollection(evt.Kind)
|
|
rateLimiter := config.GetRateLimiter()
|
|
sizeLimiter := config.GetSizeLimiter()
|
|
|
|
if config.GetConfig().DomainWhitelist.Enabled {
|
|
domains := config.GetConfig().DomainWhitelist.Domains
|
|
pubkeys, err := utils.FetchPubkeysFromDomains(domains)
|
|
if err != nil {
|
|
fmt.Println("Error fetching pubkeys from domains:", err)
|
|
response.SendNotice(ws, "", "Error fetching pubkeys from domains")
|
|
return
|
|
}
|
|
for _, pubkey := range pubkeys {
|
|
config.GetConfig().PubkeyWhitelist.Pubkeys = append(config.GetConfig().PubkeyWhitelist.Pubkeys, pubkey)
|
|
}
|
|
}
|
|
|
|
// Check if the kind is whitelisted
|
|
if config.GetConfig().KindWhitelist.Enabled && !isKindWhitelisted(evt.Kind) {
|
|
response.SendOK(ws, evt.ID, false, "not allowed: event kind is not whitelisted")
|
|
return
|
|
}
|
|
|
|
// Check pubkey/npub whitelist only if the kind is not whitelisted
|
|
if config.GetConfig().PubkeyWhitelist.Enabled && !isPubKeyWhitelisted(evt.PubKey) {
|
|
response.SendOK(ws, evt.ID, false, "not allowed: pubkey or npub is not whitelisted")
|
|
return
|
|
}
|
|
|
|
category := determineCategory(evt.Kind)
|
|
|
|
if allowed, msg := rateLimiter.AllowEvent(evt.Kind, category); !allowed {
|
|
response.SendOK(ws, evt.ID, false, msg)
|
|
return
|
|
}
|
|
|
|
if allowed, msg := sizeLimiter.AllowSize(evt.Kind, eventSize); !allowed {
|
|
response.SendOK(ws, evt.ID, false, msg)
|
|
return
|
|
}
|
|
|
|
var err error
|
|
switch {
|
|
case evt.Kind == 0:
|
|
err = kinds.HandleKind0(ctx, evt, collection, ws)
|
|
case evt.Kind == 1:
|
|
err = kinds.HandleKind1(ctx, evt, collection, ws)
|
|
case evt.Kind == 2:
|
|
err = kinds.HandleKind2(ctx, evt, ws)
|
|
case evt.Kind == 3:
|
|
err = kinds.HandleReplaceableKind(ctx, evt, collection, ws)
|
|
case evt.Kind == 5:
|
|
err = kinds.HandleKind5(ctx, evt, db.GetClient(), ws)
|
|
case evt.Kind >= 4 && evt.Kind < 45:
|
|
err = kinds.HandleRegularKind(ctx, evt, collection, ws)
|
|
case evt.Kind >= 1000 && evt.Kind < 10000:
|
|
err = kinds.HandleRegularKind(ctx, evt, collection, ws)
|
|
case evt.Kind >= 10000 && evt.Kind < 20000:
|
|
err = kinds.HandleReplaceableKind(ctx, evt, collection, ws)
|
|
case evt.Kind >= 20000 && evt.Kind < 30000:
|
|
fmt.Println("Ephemeral event received and ignored:", evt.ID)
|
|
case evt.Kind >= 30000 && evt.Kind < 40000:
|
|
err = kinds.HandleParameterizedReplaceableKind(ctx, evt, collection, ws)
|
|
default:
|
|
err = kinds.HandleUnknownKind(ctx, evt, collection, ws)
|
|
}
|
|
|
|
if err != nil {
|
|
response.SendOK(ws, evt.ID, false, fmt.Sprintf("error: %v", err))
|
|
return
|
|
}
|
|
|
|
response.SendOK(ws, evt.ID, true, "")
|
|
}
|
|
|
|
func determineCategory(kind int) string {
|
|
switch {
|
|
case kind == 0, kind == 3, kind >= 10000 && kind < 20000:
|
|
return "replaceable"
|
|
case kind == 1, kind >= 4 && kind < 45, kind >= 1000 && kind < 10000:
|
|
return "regular"
|
|
case kind == 2:
|
|
return "deprecated"
|
|
case kind >= 20000 && kind < 30000:
|
|
return "ephemeral"
|
|
case kind >= 30000 && kind < 40000:
|
|
return "parameterized_replaceable"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// Helper function to check if a pubkey or npub is whitelisted
|
|
func isPubKeyWhitelisted(pubKey string) bool {
|
|
cfg := config.GetConfig()
|
|
if !cfg.PubkeyWhitelist.Enabled {
|
|
return true
|
|
}
|
|
|
|
// Check pubkeys
|
|
for _, whitelistedKey := range cfg.PubkeyWhitelist.Pubkeys {
|
|
if pubKey == whitelistedKey {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Check npubs
|
|
for _, npub := range cfg.PubkeyWhitelist.Npubs {
|
|
decodedPubKey, err := utils.DecodeNpub(npub)
|
|
if err != nil {
|
|
fmt.Println("Error decoding npub:", err)
|
|
continue
|
|
}
|
|
if pubKey == decodedPubKey {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func isKindWhitelisted(kind int) bool {
|
|
cfg := config.GetConfig()
|
|
if !cfg.KindWhitelist.Enabled {
|
|
return true
|
|
}
|
|
|
|
// Check event kinds
|
|
for _, whitelistedKindStr := range cfg.KindWhitelist.Kinds {
|
|
whitelistedKind, err := strconv.Atoi(whitelistedKindStr)
|
|
if err != nil {
|
|
fmt.Println("Error converting whitelisted kind to int:", err)
|
|
continue
|
|
}
|
|
if kind == whitelistedKind {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|