mirror of
https://github.com/aljazceru/haven.git
synced 2025-12-17 13:54:20 +01:00
291 lines
7.9 KiB
Go
291 lines
7.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/fiatjaf/eventstore/badger"
|
|
"github.com/fiatjaf/eventstore/lmdb"
|
|
"github.com/fiatjaf/khatru"
|
|
"github.com/fiatjaf/khatru/blossom"
|
|
"github.com/fiatjaf/khatru/policies"
|
|
"github.com/nbd-wtf/go-nostr"
|
|
)
|
|
|
|
var (
|
|
privateRelay = khatru.NewRelay()
|
|
privateDB = getPrivateDB()
|
|
)
|
|
|
|
var (
|
|
chatRelay = khatru.NewRelay()
|
|
chatDB = getChatDB()
|
|
)
|
|
|
|
var (
|
|
outboxRelay = khatru.NewRelay()
|
|
outboxDB = getOutboxDB()
|
|
)
|
|
|
|
var (
|
|
inboxRelay = khatru.NewRelay()
|
|
inboxDB = getInboxDB()
|
|
)
|
|
|
|
type DBBackend interface {
|
|
Init() error
|
|
Close()
|
|
CountEvents(ctx context.Context, filter nostr.Filter) (int64, error)
|
|
DeleteEvent(ctx context.Context, evt *nostr.Event) error
|
|
QueryEvents(ctx context.Context, filter nostr.Filter) (chan *nostr.Event, error)
|
|
SaveEvent(ctx context.Context, evt *nostr.Event) error
|
|
Serial() []byte
|
|
}
|
|
|
|
func getPrivateDB() DBBackend {
|
|
switch config.DBEngine {
|
|
case "lmdb":
|
|
return &lmdb.LMDBBackend{
|
|
Path: "db/private",
|
|
}
|
|
case "badger":
|
|
return &badger.BadgerBackend{
|
|
Path: "db/private",
|
|
}
|
|
default:
|
|
return &lmdb.LMDBBackend{
|
|
Path: "db/private",
|
|
}
|
|
}
|
|
}
|
|
|
|
func getChatDB() DBBackend {
|
|
switch config.DBEngine {
|
|
case "lmdb":
|
|
return &lmdb.LMDBBackend{
|
|
Path: "db/chat",
|
|
}
|
|
case "badger":
|
|
return &badger.BadgerBackend{
|
|
Path: "db/chat",
|
|
}
|
|
default:
|
|
return &lmdb.LMDBBackend{
|
|
Path: "db/chat",
|
|
}
|
|
}
|
|
}
|
|
|
|
func getOutboxDB() DBBackend {
|
|
switch config.DBEngine {
|
|
case "lmdb":
|
|
return &lmdb.LMDBBackend{
|
|
Path: "db/outbox",
|
|
}
|
|
case "badger":
|
|
return &badger.BadgerBackend{
|
|
Path: "db/outbox",
|
|
}
|
|
default:
|
|
return &lmdb.LMDBBackend{
|
|
Path: "db/outbox",
|
|
}
|
|
}
|
|
}
|
|
|
|
func getInboxDB() DBBackend {
|
|
switch config.DBEngine {
|
|
case "lmdb":
|
|
return &lmdb.LMDBBackend{
|
|
Path: "db/inbox",
|
|
}
|
|
case "badger":
|
|
return &badger.BadgerBackend{
|
|
Path: "db/inbox",
|
|
}
|
|
default:
|
|
return &lmdb.LMDBBackend{
|
|
Path: "db/inbox",
|
|
}
|
|
}
|
|
}
|
|
|
|
func initRelays() {
|
|
if err := privateDB.Init(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := chatDB.Init(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := outboxDB.Init(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := inboxDB.Init(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
initRelayLimits()
|
|
|
|
privateRelay.Info.Name = config.PrivateRelayName
|
|
privateRelay.Info.PubKey = nPubToPubkey(config.PrivateRelayNpub)
|
|
privateRelay.Info.Description = config.PrivateRelayDescription
|
|
privateRelay.Info.Icon = config.PrivateRelayIcon
|
|
privateRelay.Info.Version = config.RelayVersion
|
|
privateRelay.Info.Software = config.RelaySoftware
|
|
privateRelay.ServiceURL = "https://" + config.RelayURL + "/private"
|
|
|
|
if !privateRelayLimits.AllowEmptyFilters {
|
|
privateRelay.RejectFilter = append(privateRelay.RejectFilter, policies.NoEmptyFilters)
|
|
}
|
|
|
|
if !privateRelayLimits.AllowComplexFilters {
|
|
privateRelay.RejectFilter = append(privateRelay.RejectFilter, policies.NoComplexFilters)
|
|
}
|
|
|
|
privateRelay.RejectEvent = append(privateRelay.RejectEvent,
|
|
policies.RejectEventsWithBase64Media,
|
|
policies.EventIPRateLimiter(
|
|
privateRelayLimits.EventIPLimiterTokensPerInterval,
|
|
time.Minute*time.Duration(privateRelayLimits.EventIPLimiterInterval),
|
|
privateRelayLimits.EventIPLimiterMaxTokens,
|
|
),
|
|
)
|
|
|
|
privateRelay.RejectConnection = append(privateRelay.RejectConnection,
|
|
policies.ConnectionRateLimiter(
|
|
privateRelayLimits.ConnectionRateLimiterTokensPerInterval,
|
|
time.Minute*time.Duration(privateRelayLimits.ConnectionRateLimiterInterval),
|
|
privateRelayLimits.ConnectionRateLimiterMaxTokens,
|
|
),
|
|
)
|
|
|
|
chatRelay.Info.Name = config.ChatRelayName
|
|
chatRelay.Info.PubKey = nPubToPubkey(config.ChatRelayNpub)
|
|
chatRelay.Info.Description = config.ChatRelayDescription
|
|
chatRelay.Info.Icon = config.ChatRelayIcon
|
|
chatRelay.Info.Version = config.RelayVersion
|
|
chatRelay.Info.Software = config.RelaySoftware
|
|
chatRelay.ServiceURL = "https://" + config.RelayURL + "/chat"
|
|
|
|
if !chatRelayLimits.AllowEmptyFilters {
|
|
chatRelay.RejectFilter = append(chatRelay.RejectFilter, policies.NoEmptyFilters)
|
|
}
|
|
|
|
if !chatRelayLimits.AllowComplexFilters {
|
|
chatRelay.RejectFilter = append(chatRelay.RejectFilter, policies.NoComplexFilters)
|
|
}
|
|
|
|
chatRelay.RejectEvent = append(chatRelay.RejectEvent,
|
|
policies.RejectEventsWithBase64Media,
|
|
policies.EventIPRateLimiter(
|
|
chatRelayLimits.EventIPLimiterTokensPerInterval,
|
|
time.Minute*time.Duration(chatRelayLimits.EventIPLimiterInterval),
|
|
chatRelayLimits.EventIPLimiterMaxTokens,
|
|
),
|
|
)
|
|
|
|
chatRelay.RejectConnection = append(chatRelay.RejectConnection,
|
|
policies.ConnectionRateLimiter(
|
|
chatRelayLimits.ConnectionRateLimiterTokensPerInterval,
|
|
time.Minute*time.Duration(chatRelayLimits.ConnectionRateLimiterInterval),
|
|
chatRelayLimits.ConnectionRateLimiterMaxTokens,
|
|
),
|
|
)
|
|
|
|
outboxRelay.Info.Name = config.OutboxRelayName
|
|
outboxRelay.Info.PubKey = nPubToPubkey(config.OutboxRelayNpub)
|
|
outboxRelay.Info.Description = config.OutboxRelayDescription
|
|
outboxRelay.Info.Icon = config.OutboxRelayIcon
|
|
outboxRelay.Info.Version = config.RelayVersion
|
|
outboxRelay.Info.Software = config.RelaySoftware
|
|
|
|
if !outboxRelayLimits.AllowEmptyFilters {
|
|
outboxRelay.RejectFilter = append(outboxRelay.RejectFilter, policies.NoEmptyFilters)
|
|
}
|
|
|
|
if !outboxRelayLimits.AllowComplexFilters {
|
|
outboxRelay.RejectFilter = append(outboxRelay.RejectFilter, policies.NoComplexFilters)
|
|
}
|
|
|
|
outboxRelay.RejectEvent = append(outboxRelay.RejectEvent,
|
|
policies.RejectEventsWithBase64Media,
|
|
policies.EventIPRateLimiter(
|
|
outboxRelayLimits.EventIPLimiterTokensPerInterval,
|
|
time.Minute*time.Duration(outboxRelayLimits.EventIPLimiterInterval),
|
|
outboxRelayLimits.EventIPLimiterMaxTokens,
|
|
),
|
|
)
|
|
|
|
outboxRelay.RejectConnection = append(outboxRelay.RejectConnection,
|
|
policies.ConnectionRateLimiter(
|
|
outboxRelayLimits.ConnectionRateLimiterTokensPerInterval,
|
|
time.Minute*time.Duration(outboxRelayLimits.ConnectionRateLimiterInterval),
|
|
outboxRelayLimits.ConnectionRateLimiterMaxTokens,
|
|
),
|
|
)
|
|
addr := fmt.Sprintf("%s:%d", config.RelayBindAddress, config.RelayPort)
|
|
|
|
bl := blossom.New(outboxRelay, addr)
|
|
bl.Store = blossom.EventStoreBlobIndexWrapper{Store: outboxDB, ServiceURL: bl.ServiceURL}
|
|
bl.StoreBlob = append(bl.StoreBlob, func(ctx context.Context, sha256 string, body []byte) error {
|
|
if khatru.GetAuthed(ctx) != nPubToPubkey(config.OwnerNpub) {
|
|
return fmt.Errorf("auth-required: only the relay owner can store media")
|
|
}
|
|
|
|
file, err := fs.Create(config.BlossomPath + sha256)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := io.Copy(file, bytes.NewReader(body)); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
bl.LoadBlob = append(bl.LoadBlob, func(ctx context.Context, sha256 string) (io.Reader, error) {
|
|
return fs.Open(config.BlossomPath + sha256)
|
|
})
|
|
bl.DeleteBlob = append(bl.DeleteBlob, func(ctx context.Context, sha256 string) error {
|
|
return fs.Remove(config.BlossomPath + sha256)
|
|
})
|
|
|
|
inboxRelay.Info.Name = config.InboxRelayName
|
|
inboxRelay.Info.PubKey = nPubToPubkey(config.InboxRelayNpub)
|
|
inboxRelay.Info.Description = config.InboxRelayDescription
|
|
inboxRelay.Info.Icon = config.InboxRelayIcon
|
|
inboxRelay.Info.Version = config.RelayVersion
|
|
inboxRelay.Info.Software = config.RelaySoftware
|
|
inboxRelay.ServiceURL = "https://" + config.RelayURL + "/inbox"
|
|
|
|
if !inboxRelayLimits.AllowEmptyFilters {
|
|
inboxRelay.RejectFilter = append(inboxRelay.RejectFilter, policies.NoEmptyFilters)
|
|
}
|
|
|
|
if !inboxRelayLimits.AllowComplexFilters {
|
|
inboxRelay.RejectFilter = append(inboxRelay.RejectFilter, policies.NoComplexFilters)
|
|
}
|
|
|
|
inboxRelay.RejectEvent = append(inboxRelay.RejectEvent,
|
|
policies.RejectEventsWithBase64Media,
|
|
policies.EventIPRateLimiter(
|
|
inboxRelayLimits.EventIPLimiterTokensPerInterval,
|
|
time.Minute*time.Duration(inboxRelayLimits.EventIPLimiterInterval),
|
|
inboxRelayLimits.EventIPLimiterMaxTokens,
|
|
),
|
|
)
|
|
|
|
inboxRelay.RejectConnection = append(inboxRelay.RejectConnection,
|
|
policies.ConnectionRateLimiter(
|
|
inboxRelayLimits.ConnectionRateLimiterTokensPerInterval,
|
|
time.Minute*time.Duration(inboxRelayLimits.ConnectionRateLimiterInterval),
|
|
inboxRelayLimits.ConnectionRateLimiterMaxTokens,
|
|
),
|
|
)
|
|
|
|
}
|