mirror of
https://github.com/layer-systems/relay.git
synced 2026-06-04 17:41:11 +02:00
dev2main (#1)
* add management database functionality for allowed and banned pubkeys * add policy to reject events based on validation rules * expose PostgreSQL port in Docker Compose for local access * add event rejection policy for banned pubkeys based on management database --------- Co-authored-by: highperfocused <highperfocused@pm.me>
This commit is contained in:
130
main.go
130
main.go
@@ -1,12 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/fiatjaf/eventstore/postgresql"
|
||||
"github.com/fiatjaf/khatru"
|
||||
"github.com/fiatjaf/khatru/policies"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/nip86"
|
||||
)
|
||||
|
||||
func getEnv(key, fallback string) string {
|
||||
@@ -16,6 +23,34 @@ func getEnv(key, fallback string) string {
|
||||
return fallback
|
||||
}
|
||||
|
||||
func initManagementDB(db *sql.DB) error {
|
||||
// create allowed_pubkeys table
|
||||
_, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS allowed_pubkeys (
|
||||
pubkey TEXT PRIMARY KEY,
|
||||
reason TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create allowed_pubkeys table: %w", err)
|
||||
}
|
||||
|
||||
// create banned_pubkeys table
|
||||
_, err = db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS banned_pubkeys (
|
||||
pubkey TEXT PRIMARY KEY,
|
||||
reason TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create banned_pubkeys table: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// create the relay instance
|
||||
relay := khatru.NewRelay()
|
||||
@@ -37,6 +72,36 @@ func main() {
|
||||
relay.DeleteEvent = append(relay.DeleteEvent, db.DeleteEvent)
|
||||
relay.ReplaceEvent = append(relay.ReplaceEvent, db.ReplaceEvent)
|
||||
|
||||
relay.RejectEvent = append(relay.RejectEvent, policies.ValidateKind)
|
||||
|
||||
// setup management database (second connection for NIP-86)
|
||||
managementDB, err := sql.Open("postgres", getEnv("DATABASE_URL", "postgresql://postgres:postgres@db:5432/khatru-relay?sslmode=disable"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer managementDB.Close()
|
||||
|
||||
// initialize management tables
|
||||
if err := initManagementDB(managementDB); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
relay.RejectEvent = append(relay.RejectEvent,
|
||||
func(ctx context.Context, event *nostr.Event) (reject bool, msg string) {
|
||||
var reason string
|
||||
row := managementDB.QueryRowContext(ctx, `SELECT reason FROM banned_pubkeys WHERE pubkey = $1`, event.PubKey)
|
||||
switch err := row.Scan(&reason); err {
|
||||
case sql.ErrNoRows:
|
||||
return false, ""
|
||||
case nil:
|
||||
return true, fmt.Sprintf("pubkey %s banned: %s", event.PubKey, reason)
|
||||
default:
|
||||
// on unexpected DB errors, do not reject the event solely because of the failure
|
||||
return false, ""
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// // there are many other configurable things you can set
|
||||
// relay.RejectEvent = append(relay.RejectEvent,
|
||||
// // built-in policies
|
||||
@@ -69,6 +134,71 @@ func main() {
|
||||
// },
|
||||
// )
|
||||
|
||||
// management endpoints
|
||||
relay.ManagementAPI.RejectAPICall = append(relay.ManagementAPI.RejectAPICall,
|
||||
func(ctx context.Context, mp nip86.MethodParams) (reject bool, msg string) {
|
||||
user := khatru.GetAuthed(ctx)
|
||||
ownerPubKey := getEnv("RELAY_PUBKEY", "480ec1a7516406090dc042ddf67780ef30f26f3a864e83b417c053a5a611c838")
|
||||
if user != ownerPubKey {
|
||||
return true, "auth-required: only relay owner can access management API"
|
||||
}
|
||||
return false, ""
|
||||
})
|
||||
|
||||
relay.ManagementAPI.AllowPubKey = func(ctx context.Context, pubkey string, reason string) error {
|
||||
_, err := managementDB.Exec(`
|
||||
INSERT INTO allowed_pubkeys (pubkey, reason, created_at)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (pubkey) DO UPDATE SET reason = $2, created_at = $3
|
||||
`, pubkey, reason, time.Now())
|
||||
return err
|
||||
}
|
||||
|
||||
relay.ManagementAPI.BanPubKey = func(ctx context.Context, pubkey string, reason string) error {
|
||||
_, err := managementDB.Exec(`
|
||||
INSERT INTO banned_pubkeys (pubkey, reason, created_at)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (pubkey) DO UPDATE SET reason = $2, created_at = $3
|
||||
`, pubkey, reason, time.Now())
|
||||
return err
|
||||
}
|
||||
|
||||
relay.ManagementAPI.ListAllowedPubKeys = func(ctx context.Context) ([]nip86.PubKeyReason, error) {
|
||||
rows, err := managementDB.Query(`SELECT pubkey, reason FROM allowed_pubkeys ORDER BY created_at DESC`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []nip86.PubKeyReason
|
||||
for rows.Next() {
|
||||
var pk nip86.PubKeyReason
|
||||
if err := rows.Scan(&pk.PubKey, &pk.Reason); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, pk)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
relay.ManagementAPI.ListBannedPubKeys = func(ctx context.Context) ([]nip86.PubKeyReason, error) {
|
||||
rows, err := managementDB.Query(`SELECT pubkey, reason FROM banned_pubkeys ORDER BY created_at DESC`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []nip86.PubKeyReason
|
||||
for rows.Next() {
|
||||
var pk nip86.PubKeyReason
|
||||
if err := rows.Scan(&pk.PubKey, &pk.Reason); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, pk)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
mux := relay.Router()
|
||||
// set up other http handlers
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user