148 lines
3.6 KiB
Go
148 lines
3.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
|
|
"github.com/gin-gonic/autotls"
|
|
"github.com/mleku/appdata"
|
|
"github.com/mleku/ec/schnorr"
|
|
"github.com/mleku/signr/pkg/nostr"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
type config struct {
|
|
c *cobra.Command
|
|
DataDir, message string
|
|
verbose, color bool
|
|
}
|
|
|
|
var s config
|
|
var DataDirPerm os.FileMode = 0700
|
|
|
|
// rootCmd represents the base command when called without any subcommands
|
|
var rootCmd = &cobra.Command{
|
|
Use: "namestr <domain> <npub> <email> [<relays>...]",
|
|
Short: "zero configuration NIP-05 DNS<->npub identity verification",
|
|
Long: `namestr
|
|
|
|
a simple tool that lets you spawn a full NIP-05 identity service on a server with only a commandline
|
|
|
|
it is intended to support the simplest use case for a single user at a single domain where the domain is the name, as described in NIP-05 as _@domain.name for example, and will show domain.name in the client as the identifier
|
|
|
|
namestr automatically uses LetsEncrypt to generate a signed certificate for HTTPS, and serves up a https://domain.name/.well_known/nostr.json based on the parameters given in the commandline. the email field is mandatory.
|
|
|
|
optionally a list of relays can be named, only their domain name is required, the necessary prefixes will be appended`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
|
|
s.c = cmd
|
|
|
|
if len(args) == 0 {
|
|
s.c.Help()
|
|
os.Exit(0)
|
|
}
|
|
|
|
if len(args) < 2 {
|
|
s.c.Help()
|
|
s.Fatal("at least domain name and npub is required\n")
|
|
}
|
|
domain, npub, relays := args[0], args[1], args[2:]
|
|
s.Log("domain %v\n", domain)
|
|
s.Log("npub %v\n", npub)
|
|
s.Log("relays %v\n", relays)
|
|
|
|
pk, err := nostr.NpubToPublicKey(npub)
|
|
if err != nil {
|
|
s.Fatal("%s\n", err)
|
|
}
|
|
pkb := schnorr.SerializePubKey(pk)
|
|
pkHex := fmt.Sprintf("%x", pkb)
|
|
s.Log("nostr pubkey hex: %s\n", pkHex)
|
|
// generate response test.
|
|
jsonText := `{
|
|
"names": {
|
|
"_": "` + pkHex +
|
|
`"
|
|
},
|
|
"relays": {
|
|
"` + pkHex + `": [
|
|
`
|
|
rl := len(relays) - 1
|
|
for i, rel := range relays {
|
|
if i < rl {
|
|
jsonText +=
|
|
` ` + `"wss://` + rel + `",
|
|
`
|
|
} else {
|
|
jsonText +=
|
|
` ` + `"wss://` + rel + `"
|
|
`
|
|
}
|
|
}
|
|
|
|
jsonText +=
|
|
` ]
|
|
}
|
|
}
|
|
`
|
|
s.message = jsonText
|
|
s.Log("text that will be sent in response to requests:\n%s\n",
|
|
s.message)
|
|
s.DataDir = appdata.GetDataDir("namestr", false)
|
|
_, exists, err := CheckFileExists(s.DataDir)
|
|
if err != nil {
|
|
s.Fatal("error checking if datadir exists: %s", err)
|
|
}
|
|
if !exists {
|
|
s.Info("First run: Creating namestr data directory at %s\n\n",
|
|
s.DataDir)
|
|
if err = os.MkdirAll(s.DataDir, DataDirPerm); err != nil {
|
|
s.Fatal("unable to create data dir, cannot proceed: %s",
|
|
err)
|
|
}
|
|
}
|
|
s.Log("starting up server\n")
|
|
autotls.Run(s, domain)
|
|
s.Log("finished running server\n")
|
|
},
|
|
}
|
|
|
|
func CheckFileExists(name string) (fi os.FileInfo, exists bool, err error) {
|
|
exists = true
|
|
if fi, err = os.Stat(name); err != nil {
|
|
if os.IsNotExist(err) {
|
|
exists = false
|
|
err = nil
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
s.Log("%s\n", r.RequestURI)
|
|
w.WriteHeader(http.StatusOK)
|
|
if r.RequestURI == "/.well-known/nostr.json?name=_" {
|
|
w.Write([]byte(s.message))
|
|
} else {
|
|
w.Write([]byte("gfy"))
|
|
}
|
|
}
|
|
|
|
// Execute adds all child commands to the root command and sets flags
|
|
// appropriately.
|
|
func Execute() {
|
|
err := rootCmd.Execute()
|
|
if err != nil {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
pf := rootCmd.PersistentFlags()
|
|
pf.BoolVarP(&s.verbose, "verbose", "v", false,
|
|
"prints more things")
|
|
pf.BoolVarP(&s.color, "color", "c", false,
|
|
"prints color things")
|
|
}
|