package alby import ( "encoding/json" "fmt" "net" "net/http" "net/url" "git.devvul.com/asara/gologger" "git.devvul.com/asara/well-goknown/config" "github.com/davecgh/go-spew/spew" "github.com/gorilla/schema" "github.com/jmoiron/sqlx" ) var ( DB *sqlx.DB qsDecoder = schema.NewDecoder() ) type AlbyApp struct { Id int32 `json:"id"` Name string `json:"name"` NostrPubkey string `json:"nostrPubkey"` } type AlbyApps []AlbyApp type lnurlp struct { Status string `json:"status"` Tag string `json:"tag"` CommentAllowed int32 `json:"commentAllowed"` Callback string `json:"callback"` MinSendable int64 `json:"minSendable"` MaxSendable int64 `json:"maxSendable"` Metadata string `json:"metadata"` AllowsNostr bool `json:"allowsNostr"` NostrPubkey string `json:"nostrPubkey"` } type lnurlpError struct { Status string `json:"status"` Reason string `json:"reason"` } type NWCReqNostr struct { Id string `json:"id"` Pubkey string `json:"pubkey"` CreatedAt int64 `json:"created_at"` Kind int32 `json:"kind"` Tags [][]string `json:"tags"` Content string `json:"content"` Signature string `json:"sig"` } type NWCReq struct { Nostr string `json:"nostr"` Amount string `json:"amount"` Comment string `json:"comment"` } type NWCSecret struct { Name string Domain string Wallet string ClientPubkey string AppPubkey string Relay string Secret string } func (s NWCSecret) decodeSecret() { u, err := url.Parse(s.Wallet) if err != nil { spew.Dump(err) } s.AppPubkey = u.Host q, _ := url.ParseQuery(u.RawQuery) s.Relay = q["relay"][0] s.Secret = q["Secret"][0] } func GetLnurlp(w http.ResponseWriter, r *http.Request) { l := gologger.Get(config.GetConfig().LogLevel).With().Caller().Logger() albyAdmin := config.GetConfig().AlbyAdminAuth // setup response type w.Header().Set("Content-Type", "application/json") // normalize domain domain, _, err := net.SplitHostPort(r.Host) if err != nil { domain = r.Host } name := r.PathValue("name") // get all alby apps client := http.Client{} req, err := http.NewRequest("GET", "https://alby.devvul.com/api/apps", nil) if err != nil { l.Error().Msgf("unable to generate alby request for %s@%s: %s", name, domain, err.Error()) lnurlpReturnError := &lnurlpError{Status: "ERROR", Reason: "unknown error"} retError, _ := json.Marshal(lnurlpReturnError) w.WriteHeader(http.StatusNotFound) w.Write(retError) return } req.Header = http.Header{"Authorization": {fmt.Sprintf("Bearer %s", albyAdmin)}} resp, err := client.Do(req) defer resp.Body.Close() var albyApps AlbyApps err = json.NewDecoder(resp.Body).Decode(&albyApps) if err != nil { l.Error().Msgf("unable to unmarshal alby request for %s@%s: %s", name, domain, err.Error()) lnurlpReturnError := &lnurlpError{Status: "ERROR", Reason: "unknown error"} retError, _ := json.Marshal(lnurlpReturnError) w.WriteHeader(http.StatusNotFound) w.Write(retError) return } // check if user exists var npk string for _, element := range albyApps { if element.Name == name { npk = element.NostrPubkey } } if len(npk) == 0 { l.Debug().Msgf("user doesn't exist in alby %s@%s: %s", name, domain, err.Error()) lnurlpReturnError := &lnurlpError{Status: "ERROR", Reason: "user does not exist"} retError, _ := json.Marshal(lnurlpReturnError) w.WriteHeader(http.StatusNotFound) w.Write(retError) return } // get server pubkey var secret NWCSecret err = DB.QueryRow("SELECT wallet FROM lnwallets WHERE name=$1 AND domain=$2", name, domain).Scan(&secret.Wallet) if err != nil { l.Debug().Msgf("user doesn't exist in alby %s@%s: %s", name, domain, err.Error()) lnurlpReturnError := &lnurlpError{Status: "ERROR", Reason: "user does not exist"} retError, _ := json.Marshal(lnurlpReturnError) w.WriteHeader(http.StatusNotFound) w.Write(retError) return } secret.decodeSecret() spew.Dump(secret) lnurlpReturn := &lnurlp{ Status: "OK", Tag: "payRequest", CommentAllowed: 255, Callback: fmt.Sprintf("https://%s/.well-known/lnurlp/%s/callback", domain, name), MinSendable: 1000, MaxSendable: 10000000, Metadata: fmt.Sprintf("[[\"text/plain\", \"ln address payment to %s on the devvul server\"],[\"text/identifier\", \"%s@%s\"]]", name, name, domain), AllowsNostr: true, NostrPubkey: npk, } ret, err := json.Marshal(lnurlpReturn) if err != nil { l.Error().Msgf("unable to marshal json for %s@%s: %s", name, domain, err.Error()) lnurlpReturnError := &lnurlpError{Status: "ERROR", Reason: "User not found"} retError, _ := json.Marshal(lnurlpReturnError) w.WriteHeader(http.StatusNotFound) w.Write(retError) return } l.Debug().Msgf("returning pay request callback for %s@%s", name, domain) w.WriteHeader(http.StatusOK) w.Write(ret) return } func GetLnurlpCallback(w http.ResponseWriter, r *http.Request) { l := gologger.Get(config.GetConfig().LogLevel).With().Caller().Logger() //ctx := context.Background() var nwc NWCReq var nwcNostr NWCReqNostr // normalize domain domain, _, err := net.SplitHostPort(r.Host) if err != nil { domain = r.Host } name := r.PathValue("name") err = qsDecoder.Decode(&nwc, r.URL.Query()) if err != nil { l.Error().Msgf("unable to marshal json for %s@%s: %s", name, domain, err.Error()) } json.Unmarshal([]byte(nwc.Nostr), &nwcNostr) // // ev := nostr.Event{ // PubKey: nwc.Pubkey, // CreatedAt: nostr.Now(), // Kind: nostr.KindNWCWalletRequest, // Tags: nil, // Content: "", // } // // ev.Sign() }