Skip to content

Commit

Permalink
wails setup
Browse files Browse the repository at this point in the history
  • Loading branch information
im-adithya committed Dec 21, 2023
1 parent 37a9195 commit bc54108
Show file tree
Hide file tree
Showing 17 changed files with 879 additions and 270 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ node_modules
nostr-wallet-connect
nwc.db

build

frontend/dist
frontend/node_modules
250 changes: 250 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
package main

import (
"context"
"database/sql"
"fmt"
"net/http"
"os"
"os/signal"
"strings"
"sync"
"time"

echologrus "github.com/davrux/echo-logrus/v4"
"github.com/getAlby/nostr-wallet-connect/migrations"
"github.com/glebarez/sqlite"
"github.com/joho/godotenv"
"github.com/kelseyhightower/envconfig"
"github.com/labstack/echo/v4"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip19"
log "github.com/sirupsen/logrus"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gorm.io/driver/postgres"
"gorm.io/gorm"

"github.com/jackc/pgx/v5/stdlib"
sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql"
gormtrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1"
)

// App struct
type Wailsapp struct {
ctx context.Context
}

// NewApp creates a new App application struct
func NewApp() *Wailsapp {
return &Wailsapp{}
}

// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *Wailsapp) startup(ctx context.Context) {
a.ctx = ctx
}

// Greet returns a greeting for the given name
func (a *Wailsapp) Greet(name string) string {
return fmt.Sprintf("Hello %s, It's show time!", name)
}

func (a *Wailsapp) Start() {
// Load config from environment variables / .env file
godotenv.Load(".env")
cfg := &Config{}
err := envconfig.Process("", cfg)
if err != nil {
log.Fatalf("Error loading environment variables: %v", err)
}

var db *gorm.DB
var sqlDb *sql.DB
if strings.HasPrefix(cfg.DatabaseUri, "postgres://") || strings.HasPrefix(cfg.DatabaseUri, "postgresql://") || strings.HasPrefix(cfg.DatabaseUri, "unix://") {
if os.Getenv("DATADOG_AGENT_URL") != "" {
sqltrace.Register("pgx", &stdlib.Driver{}, sqltrace.WithServiceName("nostr-wallet-connect"))
sqlDb, err = sqltrace.Open("pgx", cfg.DatabaseUri)
if err != nil {
log.Fatalf("Failed to open DB %v", err)
}
db, err = gormtrace.Open(postgres.New(postgres.Config{Conn: sqlDb}), &gorm.Config{})
if err != nil {
log.Fatalf("Failed to open DB %v", err)
}
} else {
db, err = gorm.Open(postgres.Open(cfg.DatabaseUri), &gorm.Config{})
if err != nil {
log.Fatalf("Failed to open DB %v", err)
}
sqlDb, err = db.DB()
if err != nil {
log.Fatalf("Failed to set DB config: %v", err)
}
}
} else {
db, err = gorm.Open(sqlite.Open(cfg.DatabaseUri), &gorm.Config{})
if err != nil {
log.Fatalf("Failed to open DB %v", err)
}
// Override SQLite config to max one connection
cfg.DatabaseMaxConns = 1
// Enable foreign keys for sqlite
db.Exec("PRAGMA foreign_keys=ON;")
sqlDb, err = db.DB()
if err != nil {
log.Fatalf("Failed to set DB config: %v", err)
}
}
sqlDb.SetMaxOpenConns(cfg.DatabaseMaxConns)
sqlDb.SetMaxIdleConns(cfg.DatabaseMaxIdleConns)
sqlDb.SetConnMaxLifetime(time.Duration(cfg.DatabaseConnMaxLifetime) * time.Second)

err = migrations.Migrate(db)
if err != nil {
log.Fatalf("Migration failed: %v", err)
}
log.Println("Any pending migrations ran successfully")

if cfg.NostrSecretKey == "" {
if cfg.LNBackendType == AlbyBackendType {
//not allowed
log.Fatal("Nostr private key is required with this backend type.")
}
//first look up if we already have the private key in the database
//else, generate and store private key
identity := &Identity{}
err = db.FirstOrInit(identity).Error
if err != nil {
log.WithError(err).Fatal("Error retrieving private key from database")
}
if identity.Privkey == "" {
log.Info("No private key found in database, generating & saving.")
identity.Privkey = nostr.GeneratePrivateKey()
err = db.Save(identity).Error
if err != nil {
log.WithError(err).Fatal("Error saving private key to database")
}
}
cfg.NostrSecretKey = identity.Privkey
}

identityPubkey, err := nostr.GetPublicKey(cfg.NostrSecretKey)
if err != nil {
log.Fatalf("Error converting nostr privkey to pubkey: %v", err)
}
cfg.IdentityPubkey = identityPubkey
npub, err := nip19.EncodePublicKey(identityPubkey)
if err != nil {
log.Fatalf("Error converting nostr privkey to pubkey: %v", err)
}

log.Infof("Starting nostr-wallet-connect. npub: %s hex: %s", npub, identityPubkey)
svc := &Service{
cfg: cfg,
db: db,
}

if os.Getenv("DATADOG_AGENT_URL") != "" {
tracer.Start(tracer.WithService("nostr-wallet-connect"))
defer tracer.Stop()
}

echologrus.Logger = log.New()
echologrus.Logger.SetFormatter(&log.JSONFormatter{})
echologrus.Logger.SetOutput(os.Stdout)
echologrus.Logger.SetLevel(log.InfoLevel)
svc.Logger = echologrus.Logger

e := echo.New()
ctx := context.Background()
ctx, _ = signal.NotifyContext(ctx, os.Interrupt)
var wg sync.WaitGroup
switch cfg.LNBackendType {
case LNDBackendType:
lndClient, err := NewLNDService(ctx, svc, e)
if err != nil {
svc.Logger.Fatal(err)
}
svc.lnClient = lndClient
case AlbyBackendType:
oauthService, err := NewAlbyOauthService(svc, e)
if err != nil {
svc.Logger.Fatal(err)
}
svc.lnClient = oauthService
}

//register shared routes
svc.RegisterSharedRoutes(e)
// start Echo server
wg.Add(1)
go func() {
if err := e.Start(fmt.Sprintf(":%v", svc.cfg.Port)); err != nil && err != http.ErrServerClosed {
e.Logger.Fatal("shutting down the server")
}
//handle graceful shutdown
<-ctx.Done()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
e.Shutdown(ctx)
svc.Logger.Info("Echo server exited")
wg.Done()
}()

//connect to the relay
svc.Logger.Infof("Connecting to the relay: %s", cfg.Relay)

relay, err := nostr.RelayConnect(ctx, cfg.Relay, nostr.WithNoticeHandler(svc.noticeHandler))
if err != nil {
svc.Logger.Fatal(err)
}

//publish event with NIP-47 info
err = svc.PublishNip47Info(ctx, relay)
if err != nil {
svc.Logger.WithError(err).Error("Could not publish NIP47 info")
}

//Start infinite loop which will be only broken by canceling ctx (SIGINT)
//TODO: we can start this loop for multiple relays
for {
svc.Logger.Info("Subscribing to events")
sub, err := relay.Subscribe(ctx, svc.createFilters())
if err != nil {
svc.Logger.Fatal(err)
}
err = svc.StartSubscription(ctx, sub)
if err != nil {
//err being non-nil means that we have an error on the websocket error channel. In this case we just try to reconnect.
svc.Logger.WithError(err).Error("Got an error from the relay while listening to subscription. Reconnecting...")
relay, err = nostr.RelayConnect(ctx, cfg.Relay)
if err != nil {
svc.Logger.Fatal(err)
}
continue
}
//err being nil means that the context was canceled and we should exit the program.
break
}
err = relay.Close()
if err != nil {
svc.Logger.Error(err)
}
svc.Logger.Info("Graceful shutdown completed. Goodbye.")
}

func (svc *Service) createFilters() nostr.Filters {
filter := nostr.Filter{
Tags: nostr.TagMap{"p": []string{svc.cfg.IdentityPubkey}},
Kinds: []int{NIP_47_REQUEST_KIND},
}
if svc.cfg.ClientPubkey != "" {
filter.Authors = []string{svc.cfg.ClientPubkey}
}
return []nostr.Filter{filter}
}

func (svc *Service) noticeHandler(notice string) {
svc.Logger.Infof("Received a notice %s", notice)
}
14 changes: 3 additions & 11 deletions echo_handlers.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package main

import (
"embed"
"encoding/hex"
"errors"
"fmt"
"html/template"
"io"
"io/fs"
"net/http"
"net/url"
"strconv"
Expand All @@ -27,12 +25,6 @@ import (
"gorm.io/gorm"
)

//go:embed public/*
var embeddedAssets embed.FS

//go:embed views/*
var embeddedViews embed.FS

type TemplateRegistry struct {
templates map[string]*template.Template
}
Expand All @@ -58,9 +50,9 @@ func (svc *Service) RegisterSharedRoutes(e *echo.Echo) {
e.Use(session.Middleware(sessions.NewCookieStore([]byte(svc.cfg.CookieSecret))))
e.Use(ddEcho.Middleware(ddEcho.WithServiceName("nostr-wallet-connect")))

assetSubdir, _ := fs.Sub(embeddedAssets, "public")
assetHandler := http.FileServer(http.FS(assetSubdir))
e.GET("/public/*", echo.WrapHandler(http.StripPrefix("/public/", assetHandler)))
// assetSubdir, _ := fs.Sub(embeddedAssets, "public")
// assetHandler := http.FileServer(http.FS(assetSubdir))
// e.GET("/public/*", echo.WrapHandler(http.StripPrefix("/public/", assetHandler)))
e.GET("/api/getCSRFToken", svc.CSRFHandler)
e.GET("/api/apps", svc.AppsListHandler)
e.GET("/api/apps/:pubkey", svc.AppsShowHandler)
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json.md5
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ceedfa9d05a93b4082a44060c09940f6
2 changes: 1 addition & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { Routes, Route, Navigate, BrowserRouter } from "react-router-dom";

import { UserProvider } from "./context/UserContext";
import RequireAuth from "./context/RequireAuth";
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/screens/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useNavigate } from "react-router-dom";
import { useUser } from "../context/UserContext";
import { useEffect } from "react";
import {Start} from "../../wailsjs/go/main/Wailsapp"

function Login() {
const { info } = useUser()
Expand All @@ -12,6 +13,11 @@ function Login() {
}
}, [navigate, info?.user]);


function start() {
Start();
}

return(
<div className="text-center">
<img
Expand All @@ -26,6 +32,8 @@ function Login() {
Nostr Wallet Connect
</h1>

<div onClick={start}>Ready? 🙂</div>

<h2 className="text-md text-gray-700 dark:text-neutral-300">
Securely connect your Alby Account to Nostr clients and applications.
</h2>
Expand Down
6 changes: 6 additions & 0 deletions frontend/wailsjs/go/main/Wailsapp.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT

export function Greet(arg1:string):Promise<string>;

export function Start():Promise<void>;
11 changes: 11 additions & 0 deletions frontend/wailsjs/go/main/Wailsapp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT

export function Greet(arg1) {
return window['go']['main']['Wailsapp']['Greet'](arg1);
}

export function Start() {
return window['go']['main']['Wailsapp']['Start']();
}
24 changes: 24 additions & 0 deletions frontend/wailsjs/runtime/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@wailsapp/runtime",
"version": "2.0.0",
"description": "Wails Javascript runtime library",
"main": "runtime.js",
"types": "runtime.d.ts",
"scripts": {
},
"repository": {
"type": "git",
"url": "git+https://github.com/wailsapp/wails.git"
},
"keywords": [
"Wails",
"Javascript",
"Go"
],
"author": "Lea Anthony <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/wailsapp/wails/issues"
},
"homepage": "https://github.com/wailsapp/wails#readme"
}
Loading

0 comments on commit bc54108

Please sign in to comment.