handle-bunker.go raw

   1  package app
   2  
   3  import (
   4  	"encoding/json"
   5  	"net/http"
   6  	"strings"
   7  
   8  	"next.orly.dev/pkg/nostr/encoders/bech32encoding"
   9  	"next.orly.dev/pkg/nostr/encoders/hex"
  10  	"next.orly.dev/pkg/nostr/interfaces/signer/p8k"
  11  	"next.orly.dev/pkg/lol/chk"
  12  	"next.orly.dev/pkg/lol/log"
  13  )
  14  
  15  // BunkerInfoResponse is returned by the /api/bunker/info endpoint.
  16  type BunkerInfoResponse struct {
  17  	RelayURL     string `json:"relay_url"`      // WebSocket URL for NIP-46 connections
  18  	RelayNpub    string `json:"relay_npub"`     // Relay's npub
  19  	RelayPubkey  string `json:"relay_pubkey"`   // Relay's hex pubkey
  20  	ACLMode      string `json:"acl_mode"`       // Current ACL mode
  21  	Available    bool   `json:"available"`      // Whether bunker is available
  22  }
  23  
  24  // handleBunkerInfo returns bunker connection information.
  25  // This is a public endpoint that doesn't require authentication.
  26  func (s *Server) handleBunkerInfo(w http.ResponseWriter, r *http.Request) {
  27  	if r.Method != http.MethodGet {
  28  		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
  29  		return
  30  	}
  31  
  32  	// Get relay identity
  33  	relaySecret, err := s.DB.GetOrCreateRelayIdentitySecret()
  34  	if chk.E(err) {
  35  		log.E.F("failed to get relay identity: %v", err)
  36  		http.Error(w, "Failed to get relay identity", http.StatusInternalServerError)
  37  		return
  38  	}
  39  
  40  	// Derive public key
  41  	sign, err := p8k.New()
  42  	if chk.E(err) {
  43  		http.Error(w, "Failed to create signer", http.StatusInternalServerError)
  44  		return
  45  	}
  46  	if err := sign.InitSec(relaySecret); chk.E(err) {
  47  		http.Error(w, "Failed to initialize signer", http.StatusInternalServerError)
  48  		return
  49  	}
  50  	relayPubkey := sign.Pub()
  51  	relayPubkeyHex := hex.Enc(relayPubkey)
  52  
  53  	// Encode as npub
  54  	relayNpubBytes, err := bech32encoding.BinToNpub(relayPubkey)
  55  	relayNpub := string(relayNpubBytes)
  56  	if chk.E(err) {
  57  		relayNpub = relayPubkeyHex // Fallback to hex
  58  	}
  59  
  60  	// Build WebSocket URL from service URL
  61  	serviceURL := s.ServiceURL(r)
  62  	wsURL := strings.Replace(serviceURL, "https://", "wss://", 1)
  63  	wsURL = strings.Replace(wsURL, "http://", "ws://", 1)
  64  
  65  	// Bunker is available when ACL mode is not "none"
  66  	available := s.Config.ACLMode != "none"
  67  
  68  	resp := BunkerInfoResponse{
  69  		RelayURL:    wsURL,
  70  		RelayNpub:   relayNpub,
  71  		RelayPubkey: relayPubkeyHex,
  72  		ACLMode:     s.Config.ACLMode,
  73  		Available:   available,
  74  	}
  75  
  76  	w.Header().Set("Content-Type", "application/json")
  77  	json.NewEncoder(w).Encode(resp)
  78  }
  79