well-goknown/alby/well-known.go

220 lines
5.7 KiB
Go

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() {
l := gologger.Get(config.GetConfig().LogLevel).With().Caller().Logger()
u, err := url.Parse(s.Wallet)
if err != nil {
l.Error().Msgf("failed")
}
s.AppPubkey = u.Host
q, _ := url.ParseQuery(u.RawQuery)
s.Relay = q.Get("relay")
s.Secret = q.Get("secret")
}
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 name, domain, wallet, pubkey FROM lnwallets WHERE name=$1 AND domain=$2", name, domain).
Scan(&secret.Name, &secret.Domain, &secret.Wallet, &secret.ClientPubkey)
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()
}