feat: add lnurl with lightning.pub
This commit is contained in:
parent
6b6d6a9610
commit
ef6b900cbf
6 changed files with 132 additions and 5 deletions
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.devvul.com/asara/gologger v0.8.0
|
git.devvul.com/asara/gologger v0.8.0
|
||||||
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/fiatjaf/eventstore v0.8.1
|
github.com/fiatjaf/eventstore v0.8.1
|
||||||
github.com/fiatjaf/khatru v0.8.0
|
github.com/fiatjaf/khatru v0.8.0
|
||||||
github.com/jmoiron/sqlx v1.4.0
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
|
|
87
lightningpub/well-known.go
Normal file
87
lightningpub/well-known.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package lightningpub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.devvul.com/asara/gologger"
|
||||||
|
"git.devvul.com/asara/well-goknown/config"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DB *sqlx.DB
|
||||||
|
)
|
||||||
|
|
||||||
|
type lnurlp struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Callback string `json:"callback"`
|
||||||
|
MaxSendable int64 `json:"maxSendable"`
|
||||||
|
MinSendable int64 `json:"minSendable"`
|
||||||
|
Metadata string `json:"metadata"`
|
||||||
|
AllowsNostr bool `json:"allowsNostr"`
|
||||||
|
NostrPubkey string `json:"nostrPubkey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLnurlp(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := gologger.Get(config.GetConfig().LogLevel).With().Str("context", "lightningpub").Logger()
|
||||||
|
|
||||||
|
// normalize domain
|
||||||
|
domain, _, err := net.SplitHostPort(r.Host)
|
||||||
|
if err != nil {
|
||||||
|
domain = r.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
name := r.PathValue("name")
|
||||||
|
var lnwallet string
|
||||||
|
err = DB.QueryRow("SELECT wallet FROM lnwallets WHERE name=$1 AND domain=$2", name, domain).Scan(&lnwallet)
|
||||||
|
if err != nil {
|
||||||
|
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
|
||||||
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//upstreamUrl := fmt.Sprintf("https://%s/api/guest/lnurl_pay/info?k1=%s", domain, lnwallet)
|
||||||
|
upstreamUrl := fmt.Sprintf("https://%s/api/guest/lnurl_pay/info?k1=%s", domain, lnwallet)
|
||||||
|
upstreamPayload, err := http.Get(upstreamUrl)
|
||||||
|
if err != nil {
|
||||||
|
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
|
||||||
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer upstreamPayload.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(upstreamPayload.Body)
|
||||||
|
if err != nil {
|
||||||
|
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
|
||||||
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lnurlpReturn := lnurlp{}
|
||||||
|
err = json.Unmarshal(body, &lnurlpReturn)
|
||||||
|
if err != nil {
|
||||||
|
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
|
||||||
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m := fmt.Sprintf("[[\"text/plain\", \"ln address payment to a user on the devvul server\"],[\"text/identifier\", \"%s@%s\"]]", name, domain)
|
||||||
|
lnurlpReturn.Metadata = m
|
||||||
|
|
||||||
|
ret, err := json.Marshal(lnurlpReturn)
|
||||||
|
if err != nil {
|
||||||
|
l.Debug().Msgf("user (%s@%s) doesn't exist: %s", name, domain, err.Error())
|
||||||
|
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Debug().Msgf("returning lnwallet for %s@%s", name, domain)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(ret)
|
||||||
|
return
|
||||||
|
}
|
7
main.go
7
main.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"git.devvul.com/asara/gologger"
|
"git.devvul.com/asara/gologger"
|
||||||
"git.devvul.com/asara/well-goknown/config"
|
"git.devvul.com/asara/well-goknown/config"
|
||||||
"git.devvul.com/asara/well-goknown/db"
|
"git.devvul.com/asara/well-goknown/db"
|
||||||
|
"git.devvul.com/asara/well-goknown/lightningpub"
|
||||||
"git.devvul.com/asara/well-goknown/matrix"
|
"git.devvul.com/asara/well-goknown/matrix"
|
||||||
"git.devvul.com/asara/well-goknown/nostr"
|
"git.devvul.com/asara/well-goknown/nostr"
|
||||||
"github.com/fiatjaf/eventstore/postgresql"
|
"github.com/fiatjaf/eventstore/postgresql"
|
||||||
|
@ -20,12 +21,12 @@ func main() {
|
||||||
db, _ := db.NewDB()
|
db, _ := db.NewDB()
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
|
lightningpub.DB = db
|
||||||
nostr.DB = db
|
nostr.DB = db
|
||||||
nostr.RelayDb = postgresql.PostgresBackend{DatabaseURL: config.GetConfig().DbUrl}
|
nostr.RelayDb = postgresql.PostgresBackend{DatabaseURL: config.GetConfig().DbUrl}
|
||||||
if err := nostr.RelayDb.Init(); err != nil {
|
if err := nostr.RelayDb.Init(); err != nil {
|
||||||
l.Panic().Msgf("unable to connect to relay db: %s", err.Error())
|
l.Panic().Msgf("unable to connect to relay db: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
relay := nostr.NewRelay(Version)
|
relay := nostr.NewRelay(Version)
|
||||||
|
|
||||||
// matrix endpoints
|
// matrix endpoints
|
||||||
|
@ -38,6 +39,10 @@ func main() {
|
||||||
http.HandleFunc("/.well-known/nostr.json", nostr.GetNostrAddr)
|
http.HandleFunc("/.well-known/nostr.json", nostr.GetNostrAddr)
|
||||||
http.Handle("/relay", relay)
|
http.Handle("/relay", relay)
|
||||||
|
|
||||||
|
// lnurlp endpoint
|
||||||
|
l.Debug().Msg("enabling lnurlp well-known endpoint")
|
||||||
|
http.HandleFunc("/.well-known/lnurlp/{name}", lightningpub.GetLnurlp)
|
||||||
|
|
||||||
// start server
|
// start server
|
||||||
port := config.GetConfig().ListenAddr
|
port := config.GetConfig().ListenAddr
|
||||||
l.Info().Msgf("starting server on %s", port)
|
l.Info().Msgf("starting server on %s", port)
|
||||||
|
|
5
migrations/000002_lnurl.down.sql
Normal file
5
migrations/000002_lnurl.down.sql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
BEGIN;
|
||||||
|
DROP TRIGGER IF EXISTS update_lnwallets_update_ts on lnwallets;
|
||||||
|
DROP FUNCTION IF EXISTS lnwallets_update_ts();
|
||||||
|
DROP TABLE IF EXISTS lnwallets;
|
||||||
|
COMMIT;
|
29
migrations/000002_lnurl.up.sql
Normal file
29
migrations/000002_lnurl.up.sql
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
BEGIN;
|
||||||
|
CREATE TABLE IF NOT EXISTS lnwallets (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
owner_id INT NOT NULL,
|
||||||
|
name TEXT UNIQUE NOT NULL,
|
||||||
|
domain TEXT NOT NULL,
|
||||||
|
wallet TEXT NOT NULL,
|
||||||
|
create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
update_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
CONSTRAINT fk_owner FOREIGN KEY(owner_id) REFERENCES users(id),
|
||||||
|
CONSTRAINT ck_name CHECK (name ~ '^[a-z0-9]*$')
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE FUNCTION lnwallets_update_ts()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.update_ts = now();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ language 'plpgsql';
|
||||||
|
|
||||||
|
CREATE TRIGGER update_lnwallets_update_ts
|
||||||
|
BEFORE UPDATE
|
||||||
|
ON
|
||||||
|
lnwallets
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE lnwallets_update_ts();
|
||||||
|
|
||||||
|
COMMIT;
|
|
@ -14,8 +14,8 @@ func RejectUnregisteredNpubs(ctx context.Context, event *nostr.Event) (reject bo
|
||||||
var err error
|
var err error
|
||||||
l := gologger.Get(config.GetConfig().LogLevel).With().Str("context", "nostr-reject-unregistered").Logger()
|
l := gologger.Get(config.GetConfig().LogLevel).With().Str("context", "nostr-reject-unregistered").Logger()
|
||||||
|
|
||||||
// always allow seals, lightning ephemeral messages, auth messages
|
// always allow seals, lightning ephemeral messages, auth messages, addressable events
|
||||||
if event.Kind == 13 || event.Kind == 21000 || event.Kind == 22242 || event.Kind == 30078 {
|
if event.Kind == 13 || event.Kind == 21000 || event.Kind == 22242 || event.Kind == 30078 || event.Kind == 1059 {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ func RejectUnregisteredNpubs(ctx context.Context, event *nostr.Event) (reject bo
|
||||||
}
|
}
|
||||||
|
|
||||||
npubs := []string{authenticatedUser}
|
npubs := []string{authenticatedUser}
|
||||||
// add recipients of dms (nip04)/private dms (nip17)/gift wraps (nip59) to npubs list
|
// add recipients of dms/private dms/gift wraps/signature requests to npubs list
|
||||||
if event.Kind == 4 || event.Kind == 14 || event.Kind == 1059 {
|
if event.Kind == 4 || event.Kind == 14 || event.Kind == 1059 || event.Kind == 24133 {
|
||||||
for _, npub := range event.Tags.GetAll([]string{"p"}) {
|
for _, npub := range event.Tags.GetAll([]string{"p"}) {
|
||||||
npubs = append(npubs, npub.Value())
|
npubs = append(npubs, npub.Value())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue