diff --git a/.env.example b/.env.example index 6e36280..edd9f90 100644 --- a/.env.example +++ b/.env.example @@ -7,6 +7,16 @@ PRIVATE_RELAY_NPUB="npub1utx00neqgqln72j22kej3ux7803c2k986henvvha4thuwfkper4s7r5 PRIVATE_RELAY_DESCRIPTION="A safe place to store my drafts and ecash" PRIVATE_RELAY_ICON="https://i.nostr.build/6G6wW.gif" +## Private Relay Rate Limiters +PRIVATE_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL=50 +PRIVATE_RELAY_EVENT_IP_LIMITER_INTERVAL=1 +PRIVATE_RELAY_EVENT_IP_LIMITER_MAX_TOKENS=100 +PRIVATE_RELAY_ALLOW_EMPTY_FILTERS=true +PRIVATE_RELAY_ALLOW_COMPLEX_FILTERS=true +PRIVATE_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL=3 +PRIVATE_RELAY_CONNECTION_RATE_LIMITER_INTERVAL=5 +PRIVATE_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS=9 + ## Chat Relay Settings CHAT_RELAY_NAME="utxo's chat relay" CHAT_RELAY_NPUB="npub1utx00neqgqln72j22kej3ux7803c2k986henvvha4thuwfkper4s7r50e8" @@ -16,12 +26,32 @@ CHAT_RELAY_WOT_DEPTH=3 CHAT_RELAY_WOT_REFRESH_INTERVAL_HOURS=24 CHAT_RELAY_MINIMUM_FOLLOWERS=3 +## Chat Relay Rate Limiters +CHAT_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL=50 +CHAT_RELAY_EVENT_IP_LIMITER_INTERVAL=1 +CHAT_RELAY_EVENT_IP_LIMITER_MAX_TOKENS=100 +CHAT_RELAY_ALLOW_EMPTY_FILTERS=false +CHAT_RELAY_ALLOW_COMPLEX_FILTERS=false +CHAT_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL=3 +CHAT_RELAY_CONNECTION_RATE_LIMITER_INTERVAL=3 +CHAT_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS=9 + ## Outbox Relay Settings OUTBOX_RELAY_NAME="utxo's outbox relay" OUTBOX_RELAY_NPUB="npub1utx00neqgqln72j22kej3ux7803c2k986henvvha4thuwfkper4s7r50e8" OUTBOX_RELAY_DESCRIPTION="a relay for public messages" OUTBOX_RELAY_ICON="https://i.nostr.build/6G6wW.gif" +## Outbox Relay Rate Limiters +OUTBOX_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL=10 +OUTBOX_RELAY_EVENT_IP_LIMITER_INTERVAL=60 +OUTBOX_RELAY_EVENT_IP_LIMITER_MAX_TOKENS=100 +OUTBOX_RELAY_ALLOW_EMPTY_FILTERS=false +OUTBOX_RELAY_ALLOW_COMPLEX_FILTERS=false +OUTBOX_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL=3 +OUTBOX_RELAY_CONNECTION_RATE_LIMITER_INTERVAL=1 +OUTBOX_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS=9 + ## Inbox Relay Settings INBOX_RELAY_NAME="utxo's inbox relay" INBOX_RELAY_NPUB="npub1utx00neqgqln72j22kej3ux7803c2k986henvvha4thuwfkper4s7r50e8" @@ -29,26 +59,33 @@ INBOX_RELAY_DESCRIPTION="send your interactions with my notes here" INBOX_RELAY_ICON="https://i.nostr.build/6G6wW.gif" INBOX_PULL_INTERVAL_SECONDS=600 +## Inbox Relay Rate Limiters +INBOX_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL=10 +INBOX_RELAY_EVENT_IP_LIMITER_INTERVAL=1 +INBOX_RELAY_EVENT_IP_LIMITER_MAX_TOKENS=20 +INBOX_RELAY_ALLOW_EMPTY_FILTERS=false +INBOX_RELAY_ALLOW_COMPLEX_FILTERS=false +INBOX_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL=3 +INBOX_RELAY_CONNECTION_RATE_LIMITER_INTERVAL=1 +INBOX_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS=9 + + ## Import Settings IMPORT_START_DATE="2023-01-20" IMPORT_QUERY_INTERVAL_SECONDS=600 IMPORT_SEED_RELAYS="relay.damus.io,nos.lol,relay.nostr.band,relay.snort.social,nostr.land,nostr.mom,relay.nos.social,relay.primal.net,relay.nostr.bg,no.str.cr,nostr21.com,nostrue.com,relay.siamstr.com,wot.utxo.one,nostrelites.org,wot.nostr.party,wot.sovbit.host,wot.girino.org,relay.lnau.net,wot.siamstr.com,wot.sudocarlos.com,relay.otherstuff.fyi,relay.lexingtonbitcoin.org,wot.azzamo.net,wot.swarmstr.com,zap.watch,satsage.xyz,wons.calva.dev" -IMPORT_SEED_RELAYS_FILE="" +IMPORT_SEED_RELAYS_FILE="relays_blastr.json" ## Backup Settings BACKUP_PROVIDER="aws" BACKUP_INTERVAL_HOURS=24 ## AWS Backup Settings - REQUIRED IF BACKUP_PROVIDER="aws" -AWS_ACCESS_KEY_ID="AKIA" -AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" -AWS_REGION="us-west-2" -AWS_BUCKET_NAME="utxo-relay-backups" +AWS_ACCESS_KEY_ID="access" +AWS_SECRET_ACCESS_KEY="secret" +AWS_REGION="us-east-1" +AWS_BUCKET_NAME="backups" ## Blastr Settings BLASTR_RELAYS="relay.damus.io,nos.lol,relay.nostr.band,relay.snort.social,nostr.land,nostr.mom,relay.nos.social,relay.primal.net,relay.nostr.bg,no.str.cr,nostr21.com,nostrue.com,relay.siamstr.com,wot.utxo.one,nostrelites.org,wot.nostr.party,wot.sovbit.host,wot.girino.org,relay.lnau.net,wot.siamstr.com,wot.sudocarlos.com,relay.otherstuff.fyi,relay.lexingtonbitcoin.org,wot.azzamo.net,wot.swarmstr.com,zap.watch,satsage.xyz,wons.calva.dev" -BLASTR_RELAYS_FILE="" - -## OPTIONAL: Docker UID and GID - should be the same as the user running the docker container -DOCKER_UID=1000 -DOCKER_GID=1000 +BLASTR_RELAYS_FILE="relays_blastr.json" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0a290f7..05e575c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .env relays_import.json relays_blastr.json +haven \ No newline at end of file diff --git a/config.go b/config.go index 38eb215..fe4b516 100644 --- a/config.go +++ b/config.go @@ -1,12 +1,12 @@ package main import ( + "encoding/json" + "io/ioutil" "log" "os" "strconv" "strings" - "encoding/json" - "io/ioutil" "github.com/joho/godotenv" ) @@ -146,6 +146,17 @@ func getEnvInt(key string, defaultValue int) int { return defaultValue } +func getEnvBool(key string, defaultValue bool) bool { + if value, ok := os.LookupEnv(key); ok { + boolValue, err := strconv.ParseBool(value) + if err != nil { + panic(err) + } + return boolValue + } + return defaultValue +} + var art = ` ██╗ ██╗ █████╗ ██╗ ██╗███████╗███╗ ██╗ ██║ ██║██╔══██╗██║ ██║██╔════╝████╗ ██║ diff --git a/docker-compose.tor.yml b/docker-compose.tor.yml index b014df6..acb5301 100644 --- a/docker-compose.tor.yml +++ b/docker-compose.tor.yml @@ -10,7 +10,7 @@ services: - "./db:/app/db" - "./haven:/app/haven" ports: - - "3335" + - "3355" user: "${DOCKER_UID:-1000}:${DOCKER_GID:-1000}" tor: diff --git a/docker-compose.yml b/docker-compose.yml index 0c2ea58..1b37e98 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,5 +10,5 @@ services: - "./db:/app/db" - "./haven:/app/haven" ports: - - "3335:3335" + - "3355:3355" user: "${DOCKER_UID:-1000}:${DOCKER_GID:-1000}" diff --git a/haven b/haven new file mode 100755 index 0000000..b3e3221 Binary files /dev/null and b/haven differ diff --git a/init.go b/init.go index fd06b5f..15caf7e 100644 --- a/init.go +++ b/init.go @@ -1,8 +1,11 @@ package main import ( + "time" + "github.com/fiatjaf/eventstore/lmdb" "github.com/fiatjaf/khatru" + "github.com/fiatjaf/khatru/policies" ) var ( @@ -66,6 +69,8 @@ func initRelays() { panic(err) } + initRelayLimits() + privateRelay.Info.Name = config.PrivateRelayName privateRelay.Info.PubKey = nPubToPubkey(config.PrivateRelayNpub) privateRelay.Info.Description = config.PrivateRelayDescription @@ -74,6 +79,31 @@ func initRelays() { 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 @@ -82,6 +112,31 @@ func initRelays() { 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 @@ -89,6 +144,31 @@ func initRelays() { 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, + ), + ) + inboxRelay.Info.Name = config.InboxRelayName inboxRelay.Info.PubKey = nPubToPubkey(config.InboxRelayNpub) inboxRelay.Info.Description = config.InboxRelayDescription @@ -96,4 +176,30 @@ func initRelays() { 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, + ), + ) + } diff --git a/limits.go b/limits.go new file mode 100644 index 0000000..6460e5b --- /dev/null +++ b/limits.go @@ -0,0 +1,113 @@ +package main + +import ( + "encoding/json" + "log" +) + +var ( + privateRelayLimits PrivateRelayLimits + chatRelayLimits ChatRelayLimits + inboxRelayLimits InboxRelayLimits + outboxRelayLimits OutboxRelayLimits +) + +type PrivateRelayLimits struct { + EventIPLimiterTokensPerInterval int + EventIPLimiterInterval int + EventIPLimiterMaxTokens int + AllowEmptyFilters bool + AllowComplexFilters bool + ConnectionRateLimiterTokensPerInterval int + ConnectionRateLimiterInterval int + ConnectionRateLimiterMaxTokens int +} + +type ChatRelayLimits struct { + EventIPLimiterTokensPerInterval int + EventIPLimiterInterval int + EventIPLimiterMaxTokens int + AllowEmptyFilters bool + AllowComplexFilters bool + ConnectionRateLimiterTokensPerInterval int + ConnectionRateLimiterInterval int + ConnectionRateLimiterMaxTokens int +} + +type InboxRelayLimits struct { + EventIPLimiterTokensPerInterval int + EventIPLimiterInterval int + EventIPLimiterMaxTokens int + AllowEmptyFilters bool + AllowComplexFilters bool + ConnectionRateLimiterTokensPerInterval int + ConnectionRateLimiterInterval int + ConnectionRateLimiterMaxTokens int +} + +type OutboxRelayLimits struct { + EventIPLimiterTokensPerInterval int + EventIPLimiterInterval int + EventIPLimiterMaxTokens int + AllowEmptyFilters bool + AllowComplexFilters bool + ConnectionRateLimiterTokensPerInterval int + ConnectionRateLimiterInterval int + ConnectionRateLimiterMaxTokens int +} + +func initRelayLimits() { + privateRelayLimits = PrivateRelayLimits{ + EventIPLimiterTokensPerInterval: getEnvInt("PRIVATE_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL", 50), + EventIPLimiterInterval: getEnvInt("PRIVATE_RELAY_EVENT_IP_LIMITER_INTERVAL", 1), + EventIPLimiterMaxTokens: getEnvInt("PRIVATE_RELAY_EVENT_IP_LIMITER_MAX_TOKENS", 100), + AllowEmptyFilters: getEnvBool("PRIVATE_RELAY_ALLOW_EMPTY_FILTERS", true), + AllowComplexFilters: getEnvBool("PRIVATE_RELAY_ALLOW_COMPLEX_FILTERS", true), + ConnectionRateLimiterTokensPerInterval: getEnvInt("PRIVATE_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL", 3), + ConnectionRateLimiterInterval: getEnvInt("PRIVATE_RELAY_CONNECTION_RATE_LIMITER_INTERVAL", 5), + ConnectionRateLimiterMaxTokens: getEnvInt("PRIVATE_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS", 9), + } + + chatRelayLimits = ChatRelayLimits{ + EventIPLimiterTokensPerInterval: getEnvInt("CHAT_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL", 50), + EventIPLimiterInterval: getEnvInt("CHAT_RELAY_EVENT_IP_LIMITER_INTERVAL", 1), + EventIPLimiterMaxTokens: getEnvInt("CHAT_RELAY_EVENT_IP_LIMITER_MAX_TOKENS", 100), + AllowEmptyFilters: getEnvBool("CHAT_RELAY_ALLOW_EMPTY_FILTERS", false), + AllowComplexFilters: getEnvBool("CHAT_RELAY_ALLOW_COMPLEX_FILTERS", false), + ConnectionRateLimiterTokensPerInterval: getEnvInt("CHAT_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL", 3), + ConnectionRateLimiterInterval: getEnvInt("CHAT_RELAY_CONNECTION_RATE_LIMITER_INTERVAL", 3), + ConnectionRateLimiterMaxTokens: getEnvInt("CHAT_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS", 9), + } + + inboxRelayLimits = InboxRelayLimits{ + EventIPLimiterTokensPerInterval: getEnvInt("INBOX_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL", 10), + EventIPLimiterInterval: getEnvInt("INBOX_RELAY_EVENT_IP_LIMITER_INTERVAL", 1), + EventIPLimiterMaxTokens: getEnvInt("INBOX_RELAY_EVENT_IP_LIMITER_MAX_TOKENS", 20), + AllowEmptyFilters: getEnvBool("INBOX_RELAY_ALLOW_EMPTY_FILTERS", false), + AllowComplexFilters: getEnvBool("INBOX_RELAY_ALLOW_COMPLEX_FILTERS", false), + ConnectionRateLimiterTokensPerInterval: getEnvInt("INBOX_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL", 3), + ConnectionRateLimiterInterval: getEnvInt("INBOX_RELAY_CONNECTION_RATE_LIMITER_INTERVAL", 1), + ConnectionRateLimiterMaxTokens: getEnvInt("INBOX_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS", 9), + } + + outboxRelayLimits = OutboxRelayLimits{ + EventIPLimiterTokensPerInterval: getEnvInt("OUTBOX_RELAY_EVENT_IP_LIMITER_TOKENS_PER_INTERVAL", 10), + EventIPLimiterInterval: getEnvInt("OUTBOX_RELAY_EVENT_IP_LIMITER_INTERVAL", 60), + EventIPLimiterMaxTokens: getEnvInt("OUTBOX_RELAY_EVENT_IP_LIMITER_MAX_TOKENS", 100), + AllowEmptyFilters: getEnvBool("OUTBOX_RELAY_ALLOW_EMPTY_FILTERS", false), + AllowComplexFilters: getEnvBool("OUTBOX_RELAY_ALLOW_COMPLEX_FILTERS", false), + ConnectionRateLimiterTokensPerInterval: getEnvInt("OUTBOX_RELAY_CONNECTION_RATE_LIMITER_TOKENS_PER_INTERVAL", 3), + ConnectionRateLimiterInterval: getEnvInt("OUTBOX_RELAY_CONNECTION_RATE_LIMITER_INTERVAL", 1), + ConnectionRateLimiterMaxTokens: getEnvInt("OUTBOX_RELAY_CONNECTION_RATE_LIMITER_MAX_TOKENS", 9), + } + + prettyPrintLimits("Private relay limits", privateRelayLimits) + prettyPrintLimits("Chat relay limits", chatRelayLimits) + prettyPrintLimits("Inbox relay limits", inboxRelayLimits) + prettyPrintLimits("Outbox relay limits", outboxRelayLimits) +} + +func prettyPrintLimits(label string, value interface{}) { + b, _ := json.MarshalIndent(value, "", " ") + log.Printf("🚧 %s:\n%s\n", label, string(b)) +} diff --git a/main.go b/main.go index aba545a..cc32bdb 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "io" "log" "net/http" + "text/template" "github.com/fiatjaf/khatru" "github.com/nbd-wtf/go-nostr" @@ -96,6 +97,31 @@ func makeNewRelay(relayType string) *khatru.Relay { return true, "auth-required: publishing this event requires authentication" }) + mux := privateRelay.Router() + static := http.FileServer(http.Dir("templates/static")) + + mux.Handle("GET /static/", http.StripPrefix("/static/", static)) + mux.Handle("GET /favicon.ico", http.StripPrefix("/", static)) + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + tmpl := template.Must(template.ParseFiles("templates/index.html")) + data := struct { + RelayName string + RelayPubkey string + RelayDescription string + RelayURL string + }{ + RelayName: config.PrivateRelayName, + RelayPubkey: nPubToPubkey(config.PrivateRelayNpub), + RelayDescription: config.PrivateRelayDescription, + RelayURL: "wss://" + config.RelayURL + "/outbox", + } + err := tmpl.Execute(w, data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + }) + return privateRelay case "/chat": @@ -150,6 +176,31 @@ func makeNewRelay(relayType string) *khatru.Relay { return true, "only direct messages are allowed in this relay" }) + mux := chatRelay.Router() + static := http.FileServer(http.Dir("templates/static")) + + mux.Handle("GET /static/", http.StripPrefix("/static/", static)) + mux.Handle("GET /favicon.ico", http.StripPrefix("/", static)) + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + tmpl := template.Must(template.ParseFiles("templates/index.html")) + data := struct { + RelayName string + RelayPubkey string + RelayDescription string + RelayURL string + }{ + RelayName: config.ChatRelayName, + RelayPubkey: nPubToPubkey(config.ChatRelayNpub), + RelayDescription: config.ChatRelayDescription, + RelayURL: "wss://" + config.RelayURL + "/chat", + } + err := tmpl.Execute(w, data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + }) + return chatRelay case "/inbox": @@ -170,6 +221,31 @@ func makeNewRelay(relayType string) *khatru.Relay { return true, "you can only post notes if you've tagged the owner of this relay" }) + mux := inboxRelay.Router() + static := http.FileServer(http.Dir("templates/static")) + + mux.Handle("GET /static/", http.StripPrefix("/static/", static)) + mux.Handle("GET /favicon.ico", http.StripPrefix("/", static)) + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + tmpl := template.Must(template.ParseFiles("templates/index.html")) + data := struct { + RelayName string + RelayPubkey string + RelayDescription string + RelayURL string + }{ + RelayName: config.InboxRelayName, + RelayPubkey: nPubToPubkey(config.InboxRelayNpub), + RelayDescription: config.InboxRelayDescription, + RelayURL: "wss://" + config.RelayURL + "/inbox", + } + err := tmpl.Execute(w, data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + }) + return inboxRelay default: // default to outbox @@ -187,6 +263,31 @@ func makeNewRelay(relayType string) *khatru.Relay { return true, "only notes signed by the owner of this relay are allowed" }) + mux := outboxRelay.Router() + static := http.FileServer(http.Dir("templates/static")) + + mux.Handle("GET /static/", http.StripPrefix("/static/", static)) + mux.Handle("GET /favicon.ico", http.StripPrefix("/", static)) + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + tmpl := template.Must(template.ParseFiles("templates/index.html")) + data := struct { + RelayName string + RelayPubkey string + RelayDescription string + RelayURL string + }{ + RelayName: config.OutboxRelayName, + RelayPubkey: nPubToPubkey(config.OutboxRelayNpub), + RelayDescription: config.OutboxRelayDescription, + RelayURL: "wss://" + config.RelayURL + "/outbox", + } + err := tmpl.Execute(w, data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + }) + return outboxRelay } } diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..09c179c --- /dev/null +++ b/templates/index.html @@ -0,0 +1,64 @@ + + + + + + + + {{.RelayName}} + + + + +
+ +
+ +

+ {{.RelayName}} +

+ + +

+ {{.RelayDescription}} +

+ + + + {{.RelayURL}} + +
+
+ + + + + diff --git a/templates/static/android-chrome-192x192.png b/templates/static/android-chrome-192x192.png new file mode 100644 index 0000000..8f148a7 Binary files /dev/null and b/templates/static/android-chrome-192x192.png differ diff --git a/templates/static/android-chrome-256x256.png b/templates/static/android-chrome-256x256.png new file mode 100644 index 0000000..6391f81 Binary files /dev/null and b/templates/static/android-chrome-256x256.png differ diff --git a/templates/static/apple-touch-icon.png b/templates/static/apple-touch-icon.png new file mode 100644 index 0000000..a14d17c Binary files /dev/null and b/templates/static/apple-touch-icon.png differ diff --git a/templates/static/browserconfig.xml b/templates/static/browserconfig.xml new file mode 100644 index 0000000..b3930d0 --- /dev/null +++ b/templates/static/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/templates/static/favicon-16x16.png b/templates/static/favicon-16x16.png new file mode 100644 index 0000000..4d5b6ed Binary files /dev/null and b/templates/static/favicon-16x16.png differ diff --git a/templates/static/favicon-32x32.png b/templates/static/favicon-32x32.png new file mode 100644 index 0000000..2f293bf Binary files /dev/null and b/templates/static/favicon-32x32.png differ diff --git a/templates/static/favicon.ico b/templates/static/favicon.ico new file mode 100644 index 0000000..6da5dc0 Binary files /dev/null and b/templates/static/favicon.ico differ diff --git a/templates/static/mstile-150x150.png b/templates/static/mstile-150x150.png new file mode 100644 index 0000000..1abf1a2 Binary files /dev/null and b/templates/static/mstile-150x150.png differ diff --git a/templates/static/safari-pinned-tab.svg b/templates/static/safari-pinned-tab.svg new file mode 100644 index 0000000..4176fd1 --- /dev/null +++ b/templates/static/safari-pinned-tab.svg @@ -0,0 +1,15 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + +