mirror of
https://github.com/getAlby/hub.git
synced 2026-08-13 12:33:39 +02:00
WIP dynamic start
This commit is contained in:
parent
904893c5ab
commit
02e39d7ac0
15 changed files with 219 additions and 180 deletions
15
api.go
15
api.go
|
|
@ -208,10 +208,23 @@ func (svc *Service) ListApps() ([]api.App, error) {
|
|||
func (svc *Service) GetInfo() *api.InfoResponse {
|
||||
info := api.InfoResponse{}
|
||||
info.BackendType = svc.cfg.LNBackendType
|
||||
info.SetupCompleted = svc.lnClient != nil
|
||||
info.SetupCompleted = svc.cfg.LNBackendType != ""
|
||||
info.Running = svc.lnClient != nil
|
||||
return &info
|
||||
}
|
||||
|
||||
func (svc *Service) Start(startRequest *api.StartRequest) (*api.InfoResponse, error) {
|
||||
err := svc.StartApp()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
info := api.InfoResponse{}
|
||||
info.BackendType = svc.cfg.LNBackendType
|
||||
info.SetupCompleted = svc.lnClient != nil
|
||||
info.Running = svc.lnClient != nil
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (svc *Service) Setup(setupRequest *api.SetupRequest) error {
|
||||
|
||||
dbConfig := db.Config{}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ func (svc *Service) RegisterSharedRoutes(e *echo.Echo) {
|
|||
e.GET("/api/info", svc.InfoHandler)
|
||||
e.POST("/api/logout", svc.LogoutHandler)
|
||||
e.POST("/api/setup", svc.SetupHandler)
|
||||
e.POST("/api/start", svc.StartHandler)
|
||||
|
||||
frontend.RegisterHandlers(e)
|
||||
}
|
||||
|
|
@ -67,6 +68,18 @@ func (svc *Service) InfoHandler(c echo.Context) error {
|
|||
return c.JSON(http.StatusOK, responseBody)
|
||||
}
|
||||
|
||||
func (svc *Service) StartHandler(c echo.Context) error {
|
||||
var startRequest api.StartRequest
|
||||
if err := c.Bind(&startRequest); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, ErrorResponse{
|
||||
Message: fmt.Sprintf("Bad request: %s", err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
responseBody, _ := svc.Start(&startRequest)
|
||||
return c.JSON(http.StatusOK, responseBody)
|
||||
}
|
||||
|
||||
func (svc *Service) LogoutHandler(c echo.Context) error {
|
||||
sess, err := session.Get(CookieName, c)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev:http": "yarn prepare:http && vite",
|
||||
"dev:wails": "yarn prepare:wails && VITE_NWC_APP_TYPE=WAILS vite",
|
||||
"build": "tsc && vite build",
|
||||
"build:http": "yarn prepare:http && tsc && vite build",
|
||||
"prepare:wails": "cp ./platform_specific/wails/src/utils/request.ts src/utils/request.ts",
|
||||
"prepare:http": "cp ./platform_specific/http/src/utils/request.ts src/utils/request.ts",
|
||||
"build:wails": "yarn prepare:wails && tsc && VITE_NWC_APP_TYPE=WAILS vite build",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import NotFound from "src/screens/NotFound";
|
|||
import { useInfo } from "./hooks/useInfo";
|
||||
import Loading from "./components/Loading";
|
||||
import { Setup } from "./screens/Setup";
|
||||
import Start from "./screens/Start";
|
||||
|
||||
function App() {
|
||||
const { data: info } = useInfo();
|
||||
|
|
@ -21,21 +22,23 @@ function App() {
|
|||
return <Loading />;
|
||||
}
|
||||
|
||||
let home;
|
||||
if (info?.setupCompleted && info.running) {
|
||||
home = "/apps";
|
||||
} else if (info.setupCompleted && !info.running) {
|
||||
home = "/start";
|
||||
} else {
|
||||
home = "/setup";
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg:white dark:bg-black min-h-full">
|
||||
<div className="bg:white min-h-full dark:bg-black">
|
||||
<Toaster />
|
||||
<HashRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Navbar />}>
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
<Navigate
|
||||
to={info.setupCompleted ? "/apps" : "/setup"}
|
||||
replace
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route index element={<Navigate to={home} replace />} />
|
||||
<Route path="start" element={<Start />} />
|
||||
<Route path="setup" element={<Setup />} />
|
||||
<Route path="apps" element={<AppsList />} />
|
||||
<Route path="apps/:pubkey" element={<ShowApp />} />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Loading from "src/components/Loading";
|
||||
import ConnectButton from "src/components/ConnectButton";
|
||||
import { useCSRF } from "src/hooks/useCSRF";
|
||||
import { useInfo } from "src/hooks/useInfo";
|
||||
import { BackendType } from "src/types";
|
||||
|
|
@ -62,7 +62,7 @@ export function Setup() {
|
|||
value={backendType}
|
||||
onChange={(e) => setBackendType(e.target.value as BackendType)}
|
||||
id="backend-type"
|
||||
className="mb-4 bg-gray-50 border border-gray-300 text-gray-900 focus:ring-purple-700 dark:focus:ring-purple-600 dark:ring-offset-gray-800 focus:ring-2 text-sm rounded-lg block w-full p-2.5 dark:bg-surface-00dp dark:border-gray-700 dark:placeholder-gray-400 dark:text-white"
|
||||
className="dark:bg-surface-00dp mb-4 block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:ring-2 focus:ring-purple-700 dark:border-gray-700 dark:text-white dark:placeholder-gray-400 dark:ring-offset-gray-800 dark:focus:ring-purple-600"
|
||||
>
|
||||
<option value={"BREEZ"}>Breez</option>
|
||||
<option value={"LND"}>LND</option>
|
||||
|
|
@ -78,30 +78,6 @@ export function Setup() {
|
|||
);
|
||||
}
|
||||
|
||||
type ConnectButtonProps = {
|
||||
isConnecting: boolean;
|
||||
};
|
||||
|
||||
function ConnectButton({ isConnecting }: ConnectButtonProps) {
|
||||
return (
|
||||
<button
|
||||
type="submit"
|
||||
className={`mt-4 gap-2 inline-flex w-full ${
|
||||
isConnecting ? "bg-gray-300 dark:bg-gray-700" : "bg-purple-700"
|
||||
} cursor-pointer dark:text-neutral-200 duration-150 focus-visible:ring-2 focus-visible:ring-offset-2 focus:outline-none font-medium hover:bg-purple-900 items-center justify-center px-5 py-3 rounded-md shadow text-white transition`}
|
||||
disabled={isConnecting}
|
||||
>
|
||||
{isConnecting ? (
|
||||
<>
|
||||
<Loading /> Connecting...
|
||||
</>
|
||||
) : (
|
||||
<>Connect</>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
type SetupFormProps = {
|
||||
isConnecting: boolean;
|
||||
handleSubmit(data: unknown): void;
|
||||
|
|
@ -141,7 +117,7 @@ function BreezForm({ isConnecting, handleSubmit }: SetupFormProps) {
|
|||
value={greenlightInviteCode}
|
||||
type="password"
|
||||
id="greenlight-invite-code"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 focus:ring-purple-700 dark:focus:ring-purple-600 dark:ring-offset-gray-800 focus:ring-2 text-sm rounded-lg block w-full p-2.5 dark:bg-surface-00dp dark:border-gray-700 dark:placeholder-gray-400 dark:text-white"
|
||||
className="dark:bg-surface-00dp block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:ring-2 focus:ring-purple-700 dark:border-gray-700 dark:text-white dark:placeholder-gray-400 dark:ring-offset-gray-800 dark:focus:ring-purple-600"
|
||||
/>
|
||||
<label
|
||||
htmlFor="breez-api-key"
|
||||
|
|
@ -155,7 +131,7 @@ function BreezForm({ isConnecting, handleSubmit }: SetupFormProps) {
|
|||
value={breezApiKey}
|
||||
type="password"
|
||||
id="breez-api-key"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 focus:ring-purple-700 dark:focus:ring-purple-600 dark:ring-offset-gray-800 focus:ring-2 text-sm rounded-lg block w-full p-2.5 dark:bg-surface-00dp dark:border-gray-700 dark:placeholder-gray-400 dark:text-white"
|
||||
className="dark:bg-surface-00dp block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:ring-2 focus:ring-purple-700 dark:border-gray-700 dark:text-white dark:placeholder-gray-400 dark:ring-offset-gray-800 dark:focus:ring-purple-600"
|
||||
/>
|
||||
<label
|
||||
htmlFor="mnemonic"
|
||||
|
|
@ -169,7 +145,7 @@ function BreezForm({ isConnecting, handleSubmit }: SetupFormProps) {
|
|||
value={breezMnemonic}
|
||||
type="password"
|
||||
id="mnemonic"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 focus:ring-purple-700 dark:focus:ring-purple-600 dark:ring-offset-gray-800 focus:ring-2 text-sm rounded-lg block w-full p-2.5 dark:bg-surface-00dp dark:border-gray-700 dark:placeholder-gray-400 dark:text-white"
|
||||
className="dark:bg-surface-00dp block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:ring-2 focus:ring-purple-700 dark:border-gray-700 dark:text-white dark:placeholder-gray-400 dark:ring-offset-gray-800 dark:focus:ring-purple-600"
|
||||
/>
|
||||
</>
|
||||
<ConnectButton isConnecting={isConnecting} />
|
||||
|
|
@ -209,7 +185,7 @@ function LNDForm({ isConnecting, handleSubmit }: SetupFormProps) {
|
|||
onChange={(e) => setLndAddress(e.target.value)}
|
||||
value={lndAddress}
|
||||
id="lnd-address"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 focus:ring-purple-700 dark:focus:ring-purple-600 dark:ring-offset-gray-800 focus:ring-2 text-sm rounded-lg block w-full p-2.5 dark:bg-surface-00dp dark:border-gray-700 dark:placeholder-gray-400 dark:text-white"
|
||||
className="dark:bg-surface-00dp block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:ring-2 focus:ring-purple-700 dark:border-gray-700 dark:text-white dark:placeholder-gray-400 dark:ring-offset-gray-800 dark:focus:ring-purple-600"
|
||||
/>
|
||||
|
||||
<label
|
||||
|
|
@ -224,7 +200,7 @@ function LNDForm({ isConnecting, handleSubmit }: SetupFormProps) {
|
|||
value={lndCertHex}
|
||||
type="password"
|
||||
id="lnd-cert-hex"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 focus:ring-purple-700 dark:focus:ring-purple-600 dark:ring-offset-gray-800 focus:ring-2 text-sm rounded-lg block w-full p-2.5 dark:bg-surface-00dp dark:border-gray-700 dark:placeholder-gray-400 dark:text-white"
|
||||
className="dark:bg-surface-00dp block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:ring-2 focus:ring-purple-700 dark:border-gray-700 dark:text-white dark:placeholder-gray-400 dark:ring-offset-gray-800 dark:focus:ring-purple-600"
|
||||
/>
|
||||
<label
|
||||
htmlFor="lnd-macaroon-hex"
|
||||
|
|
@ -238,7 +214,7 @@ function LNDForm({ isConnecting, handleSubmit }: SetupFormProps) {
|
|||
value={lndMacaroonHex}
|
||||
type="password"
|
||||
id="lnd-macaroon-hex"
|
||||
className="bg-gray-50 border border-gray-300 text-gray-900 focus:ring-purple-700 dark:focus:ring-purple-600 dark:ring-offset-gray-800 focus:ring-2 text-sm rounded-lg block w-full p-2.5 dark:bg-surface-00dp dark:border-gray-700 dark:placeholder-gray-400 dark:text-white"
|
||||
className="dark:bg-surface-00dp block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:ring-2 focus:ring-purple-700 dark:border-gray-700 dark:text-white dark:placeholder-gray-400 dark:ring-offset-gray-800 dark:focus:ring-purple-600"
|
||||
/>
|
||||
</>
|
||||
<ConnectButton isConnecting={isConnecting} />
|
||||
|
|
|
|||
62
frontend/src/screens/Start.tsx
Normal file
62
frontend/src/screens/Start.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useCSRF } from "src/hooks/useCSRF";
|
||||
import { request, handleRequestError } from "src/utils/request";
|
||||
import ConnectButton from "src/components/ConnectButton";
|
||||
|
||||
export default function Start() {
|
||||
const [unlockPassword, setUnlockPassword] = React.useState("");
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const navigate = useNavigate();
|
||||
const { data: csrf } = useCSRF();
|
||||
|
||||
async function onSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
try {
|
||||
setLoading(true);
|
||||
if (!csrf) {
|
||||
throw new Error("info not loaded");
|
||||
}
|
||||
const res = await request("/api/start", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-CSRF-Token": csrf,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
unlockPassword,
|
||||
}),
|
||||
});
|
||||
console.log({ res });
|
||||
|
||||
navigate("/apps");
|
||||
} catch (error) {
|
||||
handleRequestError("Failed to connect", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<form onSubmit={onSubmit}>
|
||||
<>
|
||||
<label
|
||||
htmlFor="greenlight-invite-code"
|
||||
className="block font-medium text-gray-900 dark:text-white"
|
||||
>
|
||||
Unlock password
|
||||
</label>
|
||||
<input
|
||||
name="unlock"
|
||||
onChange={(e) => setUnlockPassword(e.target.value)}
|
||||
value={unlockPassword}
|
||||
type="password"
|
||||
className="dark:bg-surface-00dp block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:ring-2 focus:ring-purple-700 dark:border-gray-700 dark:text-white dark:placeholder-gray-400 dark:ring-offset-gray-800 dark:focus:ring-purple-600"
|
||||
/>
|
||||
<ConnectButton isConnecting={loading} />
|
||||
</>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ export type BudgetRenewalType = "daily" | "weekly" | "monthly" | "yearly" | "";
|
|||
|
||||
export type IconMap = {
|
||||
[key in RequestMethodType]: (
|
||||
props: React.SVGAttributes<SVGElement>
|
||||
props: React.SVGAttributes<SVGElement>,
|
||||
) => JSX.Element;
|
||||
};
|
||||
|
||||
|
|
@ -89,6 +89,7 @@ export interface NostrEvent {
|
|||
export interface InfoResponse {
|
||||
backendType: BackendType;
|
||||
setupCompleted: boolean;
|
||||
running: boolean;
|
||||
}
|
||||
|
||||
export interface CreateAppResponse {
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/nbd-wtf/go-nostr v0.25.5
|
||||
github.com/nbd-wtf/ln-decodepay v1.11.1
|
||||
github.com/orandin/lumberjackrus v1.0.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/wailsapp/wails/v2 v2.7.1
|
||||
google.golang.org/grpc v1.53.0
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -656,6 +656,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
|
|||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/orandin/lumberjackrus v1.0.1 h1:7ysDQ0MHD79zIFN9/EiDHjUcgopNi5ehtxFDy8rUkWo=
|
||||
github.com/orandin/lumberjackrus v1.0.1/go.mod h1:xYLt6H8W93pKnQgUQaxsApS0Eb4BwHLOkxk5DVzf5H0=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
|
|
|
|||
199
main.go
199
main.go
|
|
@ -15,158 +15,97 @@ import (
|
|||
"github.com/joho/godotenv"
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
"github.com/nbd-wtf/go-nostr"
|
||||
"github.com/nbd-wtf/go-nostr/nip19"
|
||||
"github.com/orandin/lumberjackrus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TODO: move to service.go
|
||||
func NewService(ctx context.Context) *Service {
|
||||
func NewService(ctx context.Context) (*Service, error) {
|
||||
// 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
|
||||
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")
|
||||
|
||||
ctx, _ = signal.NotifyContext(ctx, os.Interrupt)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
svc := &Service{
|
||||
cfg: cfg,
|
||||
db: db,
|
||||
ctx: ctx,
|
||||
wg: &wg,
|
||||
}
|
||||
|
||||
dbConfig, err := svc.setupDbConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to setup DB config: %v", err)
|
||||
}
|
||||
|
||||
// TODO: should not re-set config like this
|
||||
cfg.NostrSecretKey = dbConfig.NostrSecretKey
|
||||
|
||||
if cfg.NostrSecretKey == "" {
|
||||
log.Fatalf("No nostr secret key set")
|
||||
}
|
||||
|
||||
// calculate pubkey
|
||||
identityPubkey, err := nostr.GetPublicKey(cfg.NostrSecretKey)
|
||||
if err != nil {
|
||||
log.Fatalf("Error converting nostr privkey to pubkey: %v", err)
|
||||
}
|
||||
// TODO: should not re-set config like this
|
||||
cfg.IdentityPubkey = identityPubkey
|
||||
|
||||
npub, err := nip19.EncodePublicKey(identityPubkey)
|
||||
if err != nil {
|
||||
log.Fatalf("Error converting nostr privkey to pubkey: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger := log.New()
|
||||
logger.SetFormatter(&log.JSONFormatter{})
|
||||
logger.SetOutput(os.Stdout)
|
||||
logger.SetLevel(log.InfoLevel)
|
||||
svc.Logger = logger
|
||||
|
||||
logger.Infof("Starting nostr-wallet-connect. npub: %s hex: %s", npub, identityPubkey)
|
||||
|
||||
err = svc.launchLNBackend()
|
||||
hook, err := lumberjackrus.NewHook(
|
||||
&lumberjackrus.LogFile{
|
||||
Filename: "nwc.general.log",
|
||||
},
|
||||
log.InfoLevel,
|
||||
&log.JSONFormatter{},
|
||||
&lumberjackrus.LogFileOpts{
|
||||
log.InfoLevel: &lumberjackrus.LogFile{
|
||||
Filename: "./log/nwc-info.log",
|
||||
MaxAge: 1,
|
||||
MaxBackups: 2,
|
||||
},
|
||||
log.ErrorLevel: &lumberjackrus.LogFile{
|
||||
Filename: "./log/nwc-error.log",
|
||||
MaxAge: 1,
|
||||
MaxBackups: 2,
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
// LN backend not needed immediately, just log errors
|
||||
svc.Logger.Warnf("Failed to launch LN backend: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
logger.AddHook(hook)
|
||||
|
||||
var db *gorm.DB
|
||||
var sqlDb *sql.DB
|
||||
db, err = gorm.Open(sqlite.Open(cfg.DatabaseUri), &gorm.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Enable foreign keys for sqlite
|
||||
db.Exec("PRAGMA foreign_keys=ON;")
|
||||
sqlDb, err = db.DB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sqlDb.SetMaxOpenConns(1)
|
||||
sqlDb.SetMaxIdleConns(5)
|
||||
sqlDb.SetConnMaxLifetime(time.Duration(1800) * time.Second)
|
||||
|
||||
err = migrations.Migrate(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
//connect to the relay
|
||||
svc.Logger.Infof("Connecting to the relay: %s", cfg.Relay)
|
||||
ctx, _ = signal.NotifyContext(ctx, os.Interrupt)
|
||||
|
||||
relay, err := nostr.RelayConnect(ctx, cfg.Relay, nostr.WithNoticeHandler(svc.noticeHandler))
|
||||
if err != nil {
|
||||
svc.Logger.Errorf("Failed to connect to relay: %v", err)
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
svc := &Service{
|
||||
cfg: cfg,
|
||||
db: db,
|
||||
ctx: ctx,
|
||||
wg: &wg,
|
||||
Logger: logger,
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
svc.Logger.Info("Disconnecting from relay...")
|
||||
err = relay.Close()
|
||||
if err != nil {
|
||||
svc.Logger.Error(err)
|
||||
}
|
||||
if svc.lnClient != nil {
|
||||
svc.Logger.Info("Shutting down LN backend...")
|
||||
err = svc.lnClient.Shutdown()
|
||||
if err != nil {
|
||||
svc.Logger.Error(err)
|
||||
}
|
||||
}
|
||||
svc.Logger.Info("Relay subroutine ended")
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
return svc
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func (svc *Service) setupDbConfig() (*db.Config, error) {
|
||||
func (svc *Service) loadConfig() (*db.Config, error) {
|
||||
// setup database config from the env on first run
|
||||
// after first run, changes to ENV will have no effect for these fields
|
||||
// because the database values always take precedence!
|
||||
|
||||
godotenv.Load(".env")
|
||||
cfg := &Config{}
|
||||
err := envconfig.Process("", cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var existingDbConfig db.Config
|
||||
res := svc.db.Limit(1).Find(&existingDbConfig)
|
||||
|
||||
|
|
@ -185,7 +124,6 @@ func (svc *Service) setupDbConfig() (*db.Config, error) {
|
|||
}
|
||||
|
||||
newDbConfig := db.Config{
|
||||
ID: 1,
|
||||
LNBackendType: svc.cfg.LNBackendType,
|
||||
LNDAddress: svc.cfg.LNDAddress,
|
||||
LNDCertHex: svc.cfg.LNDCertHex,
|
||||
|
|
@ -207,7 +145,7 @@ func (svc *Service) launchLNBackend() error {
|
|||
if svc.lnClient != nil {
|
||||
err := svc.lnClient.Shutdown()
|
||||
if err != nil {
|
||||
svc.Logger.Fatalf("Failed to disconnect from current node: %v", err)
|
||||
return err
|
||||
}
|
||||
svc.lnClient = nil
|
||||
}
|
||||
|
|
@ -219,8 +157,15 @@ func (svc *Service) launchLNBackend() error {
|
|||
return errors.New("No LNBackendType specified")
|
||||
}
|
||||
|
||||
svc.cfg.NostrSecretKey = dbConfig.NostrSecretKey
|
||||
identityPubkey, err := nostr.GetPublicKey(svc.cfg.NostrSecretKey)
|
||||
if err != nil {
|
||||
log.Fatalf("Error converting nostr privkey to pubkey: %v", err)
|
||||
}
|
||||
// TODO: should not re-set config like this
|
||||
svc.cfg.IdentityPubkey = identityPubkey
|
||||
|
||||
svc.Logger.Infof("Launching LN Backend: %s", dbConfig.LNBackendType)
|
||||
var err error
|
||||
var lnClient LNClient
|
||||
switch dbConfig.LNBackendType {
|
||||
case LNDBackendType:
|
||||
|
|
|
|||
|
|
@ -21,11 +21,7 @@ import (
|
|||
func main() {
|
||||
log.Info("NWC Starting in HTTP mode")
|
||||
ctx := context.Background()
|
||||
svc := NewService(ctx)
|
||||
|
||||
if svc.cfg.CookieSecret == "" {
|
||||
svc.Logger.Fatalf("required key COOKIE_SECRET missing value")
|
||||
}
|
||||
svc, _ := NewService(ctx)
|
||||
|
||||
echologrus.Logger = svc.Logger
|
||||
e := echo.New()
|
||||
|
|
@ -35,7 +31,7 @@ func main() {
|
|||
//start Echo server
|
||||
go func() {
|
||||
if err := e.Start(fmt.Sprintf(":%v", svc.cfg.Port)); err != nil && err != http.ErrServerClosed {
|
||||
e.Logger.Fatalf("shutting down the server: %v", err)
|
||||
svc.Logger.Fatalf("shutting down the server: %v", err)
|
||||
}
|
||||
}()
|
||||
//handle graceful shutdown
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
func main() {
|
||||
log.Info("NWC Starting in WAILS mode")
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
svc := NewService(ctx)
|
||||
svc, _ := NewService(ctx)
|
||||
|
||||
app := NewApp(svc)
|
||||
LaunchWailsApp(app)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ type CreateAppRequest struct {
|
|||
ReturnTo string `json:"returnTo"`
|
||||
}
|
||||
|
||||
type StartRequest struct {
|
||||
unlockPassword string `json:"unlockPassword"`
|
||||
}
|
||||
|
||||
type SetupRequest struct {
|
||||
LNBackendType string `json:"backendType"`
|
||||
// Breez fields
|
||||
|
|
@ -61,4 +65,5 @@ type User struct {
|
|||
type InfoResponse struct {
|
||||
BackendType string `json:"backendType"`
|
||||
SetupCompleted bool `json:"setupCompleted"`
|
||||
Running bool `json:"running"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ func LaunchWailsApp(app *WailsApp) {
|
|||
AssetServer: &assetserver.Options{
|
||||
Assets: assets,
|
||||
},
|
||||
HideWindowOnClose: true,
|
||||
//BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
|
||||
OnStartup: app.startup,
|
||||
Bind: []interface{}{
|
||||
|
|
|
|||
|
|
@ -77,6 +77,27 @@ func (a *WailsApp) WailsRequestRouter(route string, method string, body string)
|
|||
infoResponse := a.svc.GetInfo()
|
||||
res := WailsRequestRouterResponse{Body: *infoResponse, Error: ""}
|
||||
return res
|
||||
case "/api/start":
|
||||
startRequest := &api.StartRequest{}
|
||||
err := json.Unmarshal([]byte(body), startRequest)
|
||||
if err != nil {
|
||||
a.svc.Logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
}).Errorf("Failed to decode request to wails router: %v", err)
|
||||
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
|
||||
}
|
||||
res, err := a.svc.Start(startRequest)
|
||||
if err != nil {
|
||||
a.svc.Logger.WithFields(logrus.Fields{
|
||||
"route": route,
|
||||
"method": method,
|
||||
"body": body,
|
||||
}).Errorf("Failed to setup node: %v", err)
|
||||
return WailsRequestRouterResponse{Body: res, Error: err.Error()}
|
||||
}
|
||||
return WailsRequestRouterResponse{Body: res, Error: ""}
|
||||
case "/api/csrf":
|
||||
return WailsRequestRouterResponse{Body: "dummy", Error: ""}
|
||||
case "/api/setup":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue