Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
ff017b45d2
|
|||
|
50179e44ed
|
|||
|
34a3b1ba69
|
@@ -45,6 +45,7 @@ type C struct {
|
|||||||
NWCUri string `env:"ORLY_NWC_URI" usage:"NWC (Nostr Wallet Connect) connection string for Lightning payments"`
|
NWCUri string `env:"ORLY_NWC_URI" usage:"NWC (Nostr Wallet Connect) connection string for Lightning payments"`
|
||||||
SubscriptionEnabled bool `env:"ORLY_SUBSCRIPTION_ENABLED" default:"false" usage:"enable subscription-based access control requiring payment for non-directory events"`
|
SubscriptionEnabled bool `env:"ORLY_SUBSCRIPTION_ENABLED" default:"false" usage:"enable subscription-based access control requiring payment for non-directory events"`
|
||||||
MonthlyPriceSats int64 `env:"ORLY_MONTHLY_PRICE_SATS" default:"6000" usage:"price in satoshis for one month subscription (default ~$2 USD)"`
|
MonthlyPriceSats int64 `env:"ORLY_MONTHLY_PRICE_SATS" default:"6000" usage:"price in satoshis for one month subscription (default ~$2 USD)"`
|
||||||
|
RelayURL string `env:"ORLY_RELAY_URL" usage:"base URL for the relay dashboard (e.g., https://relay.example.com)"`
|
||||||
|
|
||||||
// Web UI and dev mode settings
|
// Web UI and dev mode settings
|
||||||
WebDisableEmbedded bool `env:"ORLY_WEB_DISABLE" default:"false" usage:"disable serving the embedded web UI; useful for hot-reload during development"`
|
WebDisableEmbedded bool `env:"ORLY_WEB_DISABLE" default:"false" usage:"disable serving the embedded web UI; useful for hot-reload during development"`
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ func (l *Listener) handleFirstTimeUser(pubkey []byte) {
|
|||||||
|
|
||||||
// Get payment processor to create welcome note
|
// Get payment processor to create welcome note
|
||||||
if l.Server.paymentProcessor != nil {
|
if l.Server.paymentProcessor != nil {
|
||||||
|
// Set the dashboard URL based on the current HTTP request
|
||||||
|
dashboardURL := l.Server.DashboardURL(l.req)
|
||||||
|
l.Server.paymentProcessor.SetDashboardURL(dashboardURL)
|
||||||
|
|
||||||
if err := l.Server.paymentProcessor.CreateWelcomeNote(pubkey); err != nil {
|
if err := l.Server.paymentProcessor.CreateWelcomeNote(pubkey); err != nil {
|
||||||
log.E.F("failed to create welcome note for first-time user: %v", err)
|
log.E.F("failed to create welcome note for first-time user: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"lol.mleku.dev/chk"
|
"lol.mleku.dev/chk"
|
||||||
"lol.mleku.dev/log"
|
"lol.mleku.dev/log"
|
||||||
|
"next.orly.dev/pkg/crypto/p256k"
|
||||||
|
"next.orly.dev/pkg/encoders/hex"
|
||||||
"next.orly.dev/pkg/protocol/relayinfo"
|
"next.orly.dev/pkg/protocol/relayinfo"
|
||||||
"next.orly.dev/pkg/version"
|
"next.orly.dev/pkg/version"
|
||||||
)
|
)
|
||||||
@@ -63,12 +66,26 @@ func (s *Server) HandleRelayInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
sort.Sort(supportedNIPs)
|
sort.Sort(supportedNIPs)
|
||||||
log.T.Ln("supported NIPs", supportedNIPs)
|
log.T.Ln("supported NIPs", supportedNIPs)
|
||||||
|
// Construct description with dashboard URL
|
||||||
|
dashboardURL := s.DashboardURL(r)
|
||||||
|
description := version.Description + " dashboard: " + dashboardURL
|
||||||
|
|
||||||
|
// Get relay identity pubkey as hex
|
||||||
|
var relayPubkey string
|
||||||
|
if skb, err := s.D.GetRelayIdentitySecret(); err == nil && len(skb) == 32 {
|
||||||
|
sign := new(p256k.Signer)
|
||||||
|
if err := sign.InitSec(skb); err == nil {
|
||||||
|
relayPubkey = hex.Enc(sign.Pub())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info = &relayinfo.T{
|
info = &relayinfo.T{
|
||||||
Name: s.Config.AppName,
|
Name: s.Config.AppName,
|
||||||
Description: version.Description,
|
Description: description,
|
||||||
|
PubKey: relayPubkey,
|
||||||
Nips: supportedNIPs,
|
Nips: supportedNIPs,
|
||||||
Software: version.URL,
|
Software: version.URL,
|
||||||
Version: version.V,
|
Version: strings.TrimPrefix(version.V, "v"),
|
||||||
Limitation: relayinfo.Limits{
|
Limitation: relayinfo.Limits{
|
||||||
AuthRequired: s.Config.ACLMode != "none",
|
AuthRequired: s.Config.ACLMode != "none",
|
||||||
RestrictedWrites: s.Config.ACLMode != "none",
|
RestrictedWrites: s.Config.ACLMode != "none",
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ type PaymentProcessor struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
dashboardURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPaymentProcessor creates a new payment processor
|
// NewPaymentProcessor creates a new payment processor
|
||||||
@@ -301,8 +302,10 @@ Your paid subscription to this relay will expire in 7 days on %s.
|
|||||||
|
|
||||||
Don't lose access to your private relay! Extend your subscription today.
|
Don't lose access to your private relay! Extend your subscription today.
|
||||||
|
|
||||||
Relay: nostr:%s`,
|
Relay: nostr:%s
|
||||||
expiryTime.Format("2006-01-02 15:04:05 UTC"), monthlyPrice, monthlyPrice, string(relayNpubForContent))
|
|
||||||
|
Log in to the relay dashboard to access your configuration at: %s`,
|
||||||
|
expiryTime.Format("2006-01-02 15:04:05 UTC"), monthlyPrice, monthlyPrice, string(relayNpubForContent), pp.getDashboardURL())
|
||||||
|
|
||||||
// Build the event
|
// Build the event
|
||||||
ev := event.New()
|
ev := event.New()
|
||||||
@@ -402,8 +405,10 @@ Simply zap this note with your payment amount:
|
|||||||
|
|
||||||
Thank you for considering supporting decentralized communication!
|
Thank you for considering supporting decentralized communication!
|
||||||
|
|
||||||
Relay: nostr:%s`,
|
Relay: nostr:%s
|
||||||
trialEnd.Format("2006-01-02 15:04:05 UTC"), monthlyPrice, dailyRate, monthlyPrice, string(relayNpubForContent))
|
|
||||||
|
Log in to the relay dashboard to access your configuration at: %s`,
|
||||||
|
trialEnd.Format("2006-01-02 15:04:05 UTC"), monthlyPrice, dailyRate, monthlyPrice, string(relayNpubForContent), pp.getDashboardURL())
|
||||||
|
|
||||||
// Build the event
|
// Build the event
|
||||||
ev := event.New()
|
ev := event.New()
|
||||||
@@ -605,9 +610,9 @@ func (pp *PaymentProcessor) createPaymentNote(payerPubkey []byte, satsReceived i
|
|||||||
return fmt.Errorf("failed to encode relay npub: %w", err)
|
return fmt.Errorf("failed to encode relay npub: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the note content with nostr:npub link
|
// Create the note content with nostr:npub link and dashboard link
|
||||||
content := fmt.Sprintf("Payment received: %d sats for %d days. Subscription expires: %s\n\nRelay: nostr:%s",
|
content := fmt.Sprintf("Payment received: %d sats for %d days. Subscription expires: %s\n\nRelay: nostr:%s\n\nLog in to the relay dashboard to access your configuration at: %s",
|
||||||
satsReceived, days, expiryTime.Format("2006-01-02 15:04:05 UTC"), string(relayNpubForContent))
|
satsReceived, days, expiryTime.Format("2006-01-02 15:04:05 UTC"), string(relayNpubForContent), pp.getDashboardURL())
|
||||||
|
|
||||||
// Build the event
|
// Build the event
|
||||||
ev := event.New()
|
ev := event.New()
|
||||||
@@ -699,7 +704,9 @@ To extend your subscription after the trial ends, simply zap this note with the
|
|||||||
|
|
||||||
Relay: nostr:%s
|
Relay: nostr:%s
|
||||||
|
|
||||||
Enjoy your time on the relay!`, monthlyPrice, monthlyPrice, string(relayNpubForContent))
|
Log in to the relay dashboard to access your configuration at: %s
|
||||||
|
|
||||||
|
Enjoy your time on the relay!`, monthlyPrice, monthlyPrice, string(relayNpubForContent), pp.getDashboardURL())
|
||||||
|
|
||||||
// Build the event
|
// Build the event
|
||||||
ev := event.New()
|
ev := event.New()
|
||||||
@@ -750,6 +757,25 @@ Enjoy your time on the relay!`, monthlyPrice, monthlyPrice, string(relayNpubForC
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDashboardURL sets the dynamic dashboard URL based on HTTP request
|
||||||
|
func (pp *PaymentProcessor) SetDashboardURL(url string) {
|
||||||
|
pp.dashboardURL = url
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDashboardURL returns the dashboard URL for the relay
|
||||||
|
func (pp *PaymentProcessor) getDashboardURL() string {
|
||||||
|
// Use dynamic URL if available
|
||||||
|
if pp.dashboardURL != "" {
|
||||||
|
return pp.dashboardURL
|
||||||
|
}
|
||||||
|
// Fallback to static config
|
||||||
|
if pp.config.RelayURL != "" {
|
||||||
|
return pp.config.RelayURL
|
||||||
|
}
|
||||||
|
// Default fallback if no URL is configured
|
||||||
|
return "https://your-relay.example.com"
|
||||||
|
}
|
||||||
|
|
||||||
// extractNpubFromDescription extracts an npub from the payment description
|
// extractNpubFromDescription extracts an npub from the payment description
|
||||||
func (pp *PaymentProcessor) extractNpubFromDescription(description string) string {
|
func (pp *PaymentProcessor) extractNpubFromDescription(description string) string {
|
||||||
// check if the entire description is just an npub
|
// check if the entire description is just an npub
|
||||||
@@ -794,6 +820,58 @@ func (pp *PaymentProcessor) npubToPubkey(npubStr string) ([]byte, error) {
|
|||||||
return pubkey, nil
|
return pubkey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateRelayProfile creates or updates the relay's kind 0 profile with subscription information
|
||||||
|
func (pp *PaymentProcessor) UpdateRelayProfile() error {
|
||||||
|
// Get relay identity secret to sign the profile
|
||||||
|
skb, err := pp.db.GetRelayIdentitySecret()
|
||||||
|
if err != nil || len(skb) != 32 {
|
||||||
|
return fmt.Errorf("no relay identity configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize signer
|
||||||
|
sign := new(p256k.Signer)
|
||||||
|
if err := sign.InitSec(skb); err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize signer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
monthlyPrice := pp.config.MonthlyPriceSats
|
||||||
|
if monthlyPrice <= 0 {
|
||||||
|
monthlyPrice = 6000
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate daily rate
|
||||||
|
dailyRate := monthlyPrice / 30
|
||||||
|
|
||||||
|
// Get relay wss:// URL - use dashboard URL but with wss:// scheme
|
||||||
|
relayURL := strings.Replace(pp.getDashboardURL(), "https://", "wss://", 1)
|
||||||
|
|
||||||
|
// Create profile content as JSON
|
||||||
|
profileContent := fmt.Sprintf(`{
|
||||||
|
"name": "Relay Bot",
|
||||||
|
"about": "This relay requires a subscription to access. Zap any of my notes to pay for access. Monthly price: %d sats (%d sats/day). Relay: %s",
|
||||||
|
"lud16": "",
|
||||||
|
"nip05": "",
|
||||||
|
"website": "%s"
|
||||||
|
}`, monthlyPrice, dailyRate, relayURL, pp.getDashboardURL())
|
||||||
|
|
||||||
|
// Build the profile event
|
||||||
|
ev := event.New()
|
||||||
|
ev.Kind = kind.ProfileMetadata.K // Kind 0 for profile metadata
|
||||||
|
ev.Pubkey = sign.Pub()
|
||||||
|
ev.CreatedAt = timestamp.Now().V
|
||||||
|
ev.Content = []byte(profileContent)
|
||||||
|
ev.Tags = tag.NewS()
|
||||||
|
|
||||||
|
// Sign and save the event
|
||||||
|
ev.Sign(sign)
|
||||||
|
if _, _, err := pp.db.SaveEvent(pp.ctx, ev); err != nil {
|
||||||
|
return fmt.Errorf("failed to save relay profile: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.I.F("updated relay profile with subscription information")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// decodeAnyPubkey decodes a public key from either hex string or npub format
|
// decodeAnyPubkey decodes a public key from either hex string or npub format
|
||||||
func decodeAnyPubkey(s string) ([]byte, error) {
|
func decodeAnyPubkey(s string) ([]byte, error) {
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
|
|||||||
@@ -113,6 +113,15 @@ func (s *Server) ServiceURL(req *http.Request) (st string) {
|
|||||||
return proto + "://" + host
|
return proto + "://" + host
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DashboardURL constructs HTTPS URL for the dashboard based on the HTTP request
|
||||||
|
func (s *Server) DashboardURL(req *http.Request) string {
|
||||||
|
host := req.Header.Get("X-Forwarded-Host")
|
||||||
|
if host == "" {
|
||||||
|
host = req.Host
|
||||||
|
}
|
||||||
|
return "https://" + host
|
||||||
|
}
|
||||||
|
|
||||||
// UserInterface sets up a basic Nostr NDK interface that allows users to log into the relay user interface
|
// UserInterface sets up a basic Nostr NDK interface that allows users to log into the relay user interface
|
||||||
func (s *Server) UserInterface() {
|
func (s *Server) UserInterface() {
|
||||||
if s.mux == nil {
|
if s.mux == nil {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v0.8.1
|
v0.8.4
|
||||||
Reference in New Issue
Block a user