2023-09-06 20:49:18 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2023-12-07 10:56:20 -03:00
|
|
|
"context"
|
2023-10-28 21:40:54 -03:00
|
|
|
"encoding/json"
|
2023-09-06 20:49:18 +02:00
|
|
|
"net/http"
|
2023-10-28 21:40:54 -03:00
|
|
|
"net/url"
|
2023-09-06 20:49:18 +02:00
|
|
|
"os"
|
2023-12-07 10:56:20 -03:00
|
|
|
"os/signal"
|
|
|
|
"syscall"
|
2023-09-06 20:49:18 +02:00
|
|
|
|
2023-12-05 08:18:31 -03:00
|
|
|
"github.com/fiatjaf/eventstore/lmdb"
|
2023-09-06 20:49:18 +02:00
|
|
|
"github.com/fiatjaf/khatru"
|
2023-11-22 17:12:25 -03:00
|
|
|
"github.com/fiatjaf/khatru/policies"
|
2023-10-16 23:09:12 -03:00
|
|
|
"github.com/kelseyhightower/envconfig"
|
2023-10-28 21:40:54 -03:00
|
|
|
"github.com/nbd-wtf/go-nostr"
|
2023-11-18 10:44:48 -03:00
|
|
|
"github.com/nbd-wtf/go-nostr/nip11"
|
2023-10-16 23:09:12 -03:00
|
|
|
"github.com/rs/zerolog"
|
2023-12-07 10:56:20 -03:00
|
|
|
"golang.org/x/sync/errgroup"
|
2023-09-06 20:49:18 +02:00
|
|
|
)
|
|
|
|
|
2023-10-16 23:09:12 -03:00
|
|
|
type Settings struct {
|
2023-10-18 11:58:09 -03:00
|
|
|
Port string `envconfig:"PORT" default:"3334"`
|
2023-11-02 10:08:33 -03:00
|
|
|
Domain string `envconfig:"DOMAIN" required:"true"`
|
2023-10-16 23:09:12 -03:00
|
|
|
RelayName string `envconfig:"RELAY_NAME" required:"true"`
|
|
|
|
RelayPubkey string `envconfig:"RELAY_PUBKEY" required:"true"`
|
|
|
|
RelayDescription string `envconfig:"RELAY_DESCRIPTION"`
|
|
|
|
RelayContact string `envconfig:"RELAY_CONTACT"`
|
2023-11-02 21:25:10 -03:00
|
|
|
RelayIcon string `envconfig:"RELAY_ICON"`
|
2023-11-02 10:08:33 -03:00
|
|
|
DatabasePath string `envconfig:"DATABASE_PATH" default:"./db"`
|
2024-02-23 15:20:17 +01:00
|
|
|
UserdataPath string `envconfig:"USERDATA_PATH" default:"./users.json"`
|
2024-02-02 09:29:54 -03:00
|
|
|
|
|
|
|
MaxInvitesPerPerson int `envconfig:"MAX_INVITES_PER_PERSON" default:"3"`
|
2023-10-16 23:09:12 -03:00
|
|
|
}
|
|
|
|
|
2023-10-11 00:19:33 +02:00
|
|
|
var (
|
2023-10-29 13:45:46 -03:00
|
|
|
s Settings
|
2023-12-05 08:18:31 -03:00
|
|
|
db = lmdb.LMDBBackend{MaxLimit: 50000}
|
2023-10-29 13:45:46 -03:00
|
|
|
log = zerolog.New(os.Stderr).Output(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()
|
|
|
|
whitelist = make(Whitelist)
|
2023-11-02 10:08:33 -03:00
|
|
|
relay = khatru.NewRelay()
|
2023-10-11 00:19:33 +02:00
|
|
|
)
|
2023-09-13 19:48:24 +02:00
|
|
|
|
2023-09-06 20:49:18 +02:00
|
|
|
func main() {
|
2024-02-23 15:20:17 +01:00
|
|
|
|
2023-10-16 23:09:12 -03:00
|
|
|
err := envconfig.Process("", &s)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal().Err(err).Msg("couldn't process envconfig")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-11-02 10:08:33 -03:00
|
|
|
// load db
|
|
|
|
db.Path = s.DatabasePath
|
|
|
|
if err := db.Init(); err != nil {
|
|
|
|
log.Fatal().Err(err).Msg("failed to initialize database")
|
|
|
|
return
|
|
|
|
}
|
2023-12-07 10:56:20 -03:00
|
|
|
defer db.Close()
|
2023-11-02 10:08:33 -03:00
|
|
|
log.Debug().Str("path", db.Path).Msg("initialized database")
|
2023-09-06 20:49:18 +02:00
|
|
|
|
2023-11-02 10:08:33 -03:00
|
|
|
// init relay
|
2023-11-18 10:41:45 -03:00
|
|
|
relay.Info.Name = s.RelayName
|
|
|
|
relay.Info.PubKey = s.RelayPubkey
|
|
|
|
relay.Info.Description = s.RelayDescription
|
|
|
|
relay.Info.Contact = s.RelayContact
|
|
|
|
relay.Info.Icon = s.RelayIcon
|
2023-11-18 10:44:48 -03:00
|
|
|
relay.Info.Limitation = &nip11.RelayLimitationDocument{
|
|
|
|
RestrictedWrites: true,
|
|
|
|
}
|
2023-09-13 19:48:24 +02:00
|
|
|
|
2023-09-06 20:49:18 +02:00
|
|
|
relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent)
|
|
|
|
relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents)
|
|
|
|
relay.DeleteEvent = append(relay.DeleteEvent, db.DeleteEvent)
|
2023-11-02 20:59:50 -03:00
|
|
|
relay.RejectEvent = append(relay.RejectEvent,
|
2023-11-22 17:12:25 -03:00
|
|
|
policies.PreventLargeTags(100),
|
|
|
|
policies.PreventTooManyIndexableTags(8, []int{3, 10002}, nil),
|
|
|
|
policies.PreventTooManyIndexableTags(1000, nil, []int{3, 10002}),
|
|
|
|
policies.RestrictToSpecifiedKinds(supportedKinds...),
|
2023-11-12 09:29:57 -03:00
|
|
|
rejectEventsFromUsersNotInWhitelist,
|
2023-11-02 20:59:50 -03:00
|
|
|
validateAndFilterReports,
|
|
|
|
)
|
2023-11-07 17:31:33 -03:00
|
|
|
relay.OverwriteFilter = append(relay.OverwriteFilter,
|
2023-11-22 17:12:25 -03:00
|
|
|
policies.RemoveAllButKinds(supportedKinds...),
|
2023-11-07 17:31:33 -03:00
|
|
|
removeAuthorsNotWhitelisted,
|
|
|
|
)
|
|
|
|
relay.RejectFilter = append(relay.RejectFilter,
|
2023-11-22 17:12:25 -03:00
|
|
|
policies.NoSearchQueries,
|
2023-11-07 17:31:33 -03:00
|
|
|
)
|
2023-11-02 16:08:53 -03:00
|
|
|
|
|
|
|
// load users registry
|
|
|
|
if err := loadWhitelist(); err != nil {
|
|
|
|
log.Fatal().Err(err).Msg("failed to load whitelist")
|
|
|
|
return
|
|
|
|
}
|
2023-09-06 20:49:18 +02:00
|
|
|
|
2023-11-02 16:08:53 -03:00
|
|
|
// http routes
|
2023-10-28 20:21:15 -03:00
|
|
|
relay.Router().HandleFunc("/add-to-whitelist", addToWhitelistHandler)
|
|
|
|
relay.Router().HandleFunc("/remove-from-whitelist", removeFromWhitelistHandler)
|
2023-11-02 10:08:33 -03:00
|
|
|
relay.Router().HandleFunc("/reports", reportsViewerHandler)
|
2024-02-01 12:51:05 -03:00
|
|
|
relay.Router().HandleFunc("/", inviteTreeHandler)
|
2023-09-06 20:49:18 +02:00
|
|
|
|
2023-10-18 11:58:09 -03:00
|
|
|
log.Info().Msg("running on http://0.0.0.0:" + s.Port)
|
2023-12-07 10:56:20 -03:00
|
|
|
|
|
|
|
server := &http.Server{Addr: ":" + s.Port, Handler: relay}
|
|
|
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
|
|
defer cancel()
|
|
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
|
|
g.Go(server.ListenAndServe)
|
|
|
|
g.Go(func() error {
|
|
|
|
<-ctx.Done()
|
|
|
|
return server.Shutdown(context.Background())
|
|
|
|
})
|
|
|
|
if err := g.Wait(); err != nil {
|
|
|
|
log.Debug().Err(err).Msg("exit reason")
|
2023-10-18 11:58:09 -03:00
|
|
|
}
|
2023-09-06 20:49:18 +02:00
|
|
|
}
|
2023-10-28 21:40:54 -03:00
|
|
|
|
|
|
|
func getLoggedUser(r *http.Request) string {
|
|
|
|
if cookie, _ := r.Cookie("nip98"); cookie != nil {
|
|
|
|
if evtj, err := url.QueryUnescape(cookie.Value); err == nil {
|
|
|
|
var evt nostr.Event
|
|
|
|
if err := json.Unmarshal([]byte(evtj), &evt); err == nil {
|
2023-11-02 10:08:33 -03:00
|
|
|
if tag := evt.Tags.GetFirst([]string{"domain", ""}); tag != nil && (*tag)[1] == s.Domain {
|
|
|
|
if ok, _ := evt.CheckSignature(); ok {
|
|
|
|
return evt.PubKey
|
|
|
|
}
|
|
|
|
}
|
2023-10-28 21:40:54 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|