wire localhost screen
This commit is contained in:
parent
671875e67f
commit
c2cab40a2e
21 changed files with 1553 additions and 590 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
create lnd classes: `npx protoc -I ./others --ts_out=./lnd others/*`
|
create lnd classes: `npx protoc -I ./others --ts_out=./lnd others/*`
|
||||||
create server classes: `npx protoc -I ./service --pub_out=. service/*`
|
create server classes: `npx protoc -I ./service --pub_out=. service/*`
|
||||||
|
create wizard classes: `npx protoc -I ./wizard --pub_out=./wizard_service wizard/*`
|
||||||
|
|
||||||
export PATH=$PATH:~/Lightning.Pub/proto
|
export PATH=$PATH:~/Lightning.Pub/proto
|
||||||
65
proto/wizard/wizard_methods.proto
Normal file
65
proto/wizard/wizard_methods.proto
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package wizard_methods;
|
||||||
|
|
||||||
|
import "google/protobuf/descriptor.proto";
|
||||||
|
import "wizard_structs.proto";
|
||||||
|
|
||||||
|
option go_package = "github.com/shocknet/lightning.pub";
|
||||||
|
option (file_options) = {
|
||||||
|
supported_http_methods:["post", "get"];
|
||||||
|
supported_auths:{
|
||||||
|
id: "guest"
|
||||||
|
name: "Guest"
|
||||||
|
context:[]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
message MethodQueryOptions {
|
||||||
|
repeated string items = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend google.protobuf.MethodOptions { // TODO: move this stuff to dep repo?
|
||||||
|
string auth_type = 50003;
|
||||||
|
string http_method = 50004;
|
||||||
|
string http_route = 50005;
|
||||||
|
MethodQueryOptions query = 50006;
|
||||||
|
bool nostr = 50007;
|
||||||
|
bool batch = 50008;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
message ProtoFileOptions {
|
||||||
|
message SupportedAuth {
|
||||||
|
string id = 1;
|
||||||
|
string name = 2;
|
||||||
|
bool encrypted = 3;
|
||||||
|
map<string,string> context = 4;
|
||||||
|
}
|
||||||
|
repeated SupportedAuth supported_auths = 1;
|
||||||
|
repeated string supported_http_methods = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend google.protobuf.FileOptions {
|
||||||
|
ProtoFileOptions file_options = 50004;
|
||||||
|
}
|
||||||
|
|
||||||
|
service Wizard {
|
||||||
|
// <Guest>
|
||||||
|
rpc WizardState(wizard_structs.Empty) returns (wizard_structs.StateResponse){
|
||||||
|
option (auth_type) = "Guest";
|
||||||
|
option (http_method) = "get";
|
||||||
|
option (http_route) = "/wizard/state";
|
||||||
|
};
|
||||||
|
rpc WizardConfig(wizard_structs.ConfigRequest) returns (wizard_structs.ConfigResponse){
|
||||||
|
option (auth_type) = "Guest";
|
||||||
|
option (http_method) = "post";
|
||||||
|
option (http_route) = "/wizard/config";
|
||||||
|
};
|
||||||
|
rpc WizardConfirm(wizard_structs.ConfirmRequest) returns (wizard_structs.ConfirmResponse){
|
||||||
|
option (auth_type) = "Guest";
|
||||||
|
option (http_method) = "post";
|
||||||
|
option (http_route) = "/wizard/confirm";
|
||||||
|
};
|
||||||
|
// </Guest>
|
||||||
|
}
|
||||||
32
proto/wizard/wizard_structs.proto
Normal file
32
proto/wizard/wizard_structs.proto
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package wizard_structs;
|
||||||
|
|
||||||
|
option go_package = "github.com/shocknet/lightning.pub";
|
||||||
|
|
||||||
|
message Empty {}
|
||||||
|
|
||||||
|
message StateResponse {
|
||||||
|
bool already_initialized = 1;
|
||||||
|
}
|
||||||
|
message ConfigRequest {
|
||||||
|
string source_name = 1;
|
||||||
|
string relay_url = 2;
|
||||||
|
bool automate_liquidity = 3;
|
||||||
|
bool push_backups_to_nostr = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
message ConfigResponse {
|
||||||
|
bool already_initialized = 1;
|
||||||
|
repeated string seed = 2;
|
||||||
|
string confirmation_id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConfirmRequest {
|
||||||
|
string confirmation_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConfirmResponse {
|
||||||
|
string admin_key = 1;
|
||||||
|
}
|
||||||
75
proto/wizard_service/autogenerated/client.md
Normal file
75
proto/wizard_service/autogenerated/client.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# NOSTR API DEFINITION
|
||||||
|
|
||||||
|
|
||||||
|
A nostr request will take the same parameter and give the same response as an http request, but it will use nostr as transport, to do that it will send encrypted events to the server public key, in the event 6 thing are required:
|
||||||
|
- __rpcName__: string containing the name of the method
|
||||||
|
- __params__: a map with the all the url params for the method
|
||||||
|
- __query__: a map with the the url query for the method
|
||||||
|
- __body__: the body of the method request
|
||||||
|
- __requestId__: id of the request to be able to get a response
|
||||||
|
|
||||||
|
The nostr server will send back a message response, and inside the body there will also be a __requestId__ to identify the request this response is answering
|
||||||
|
|
||||||
|
## NOSTR Methods
|
||||||
|
### These are the nostr methods the client implements to communicate with the API via nostr
|
||||||
|
|
||||||
|
# HTTP API DEFINITION
|
||||||
|
|
||||||
|
## Supported HTTP Auths
|
||||||
|
### These are the supported http auth types, to give different type of access to the API users
|
||||||
|
|
||||||
|
- __Guest__:
|
||||||
|
- expected context content
|
||||||
|
|
||||||
|
## HTTP Methods
|
||||||
|
### These are the http methods the client implements to communicate with the API
|
||||||
|
|
||||||
|
- WizardState
|
||||||
|
- auth type: __Guest__
|
||||||
|
- http method: __get__
|
||||||
|
- http route: __/wizard/state__
|
||||||
|
- This methods has an __empty__ __request__ body
|
||||||
|
- output: [StateResponse](#StateResponse)
|
||||||
|
|
||||||
|
- WizardConfig
|
||||||
|
- auth type: __Guest__
|
||||||
|
- http method: __post__
|
||||||
|
- http route: __/wizard/config__
|
||||||
|
- input: [ConfigRequest](#ConfigRequest)
|
||||||
|
- output: [ConfigResponse](#ConfigResponse)
|
||||||
|
|
||||||
|
- WizardConfirm
|
||||||
|
- auth type: __Guest__
|
||||||
|
- http method: __post__
|
||||||
|
- http route: __/wizard/confirm__
|
||||||
|
- input: [ConfirmRequest](#ConfirmRequest)
|
||||||
|
- output: [ConfirmResponse](#ConfirmResponse)
|
||||||
|
|
||||||
|
# INPUTS AND OUTPUTS
|
||||||
|
|
||||||
|
## Messages
|
||||||
|
### The content of requests and response from the methods
|
||||||
|
|
||||||
|
### ConfirmResponse
|
||||||
|
- __admin_key__: _string_
|
||||||
|
|
||||||
|
### Empty
|
||||||
|
|
||||||
|
### StateResponse
|
||||||
|
- __already_initialized__: _boolean_
|
||||||
|
|
||||||
|
### ConfigRequest
|
||||||
|
- __source_name__: _string_
|
||||||
|
- __relay_url__: _string_
|
||||||
|
- __automate_liquidity__: _boolean_
|
||||||
|
- __push_backups_to_nostr__: _boolean_
|
||||||
|
|
||||||
|
### ConfigResponse
|
||||||
|
- __already_initialized__: _boolean_
|
||||||
|
- __seed__: ARRAY of: _string_
|
||||||
|
- __confirmation_id__: _string_
|
||||||
|
|
||||||
|
### ConfirmRequest
|
||||||
|
- __confirmation_id__: _string_
|
||||||
|
## Enums
|
||||||
|
### The enumerators used in the messages
|
||||||
221
proto/wizard_service/autogenerated/debug.txt
Normal file
221
proto/wizard_service/autogenerated/debug.txt
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
([]*main.Method) (len=3 cap=4) {
|
||||||
|
(*main.Method)(0xc0002221e0)({
|
||||||
|
in: (main.MethodMessage) {
|
||||||
|
name: (string) (len=5) "Empty",
|
||||||
|
hasZeroFields: (bool) true
|
||||||
|
},
|
||||||
|
name: (string) (len=11) "WizardState",
|
||||||
|
out: (main.MethodMessage) {
|
||||||
|
name: (string) (len=13) "StateResponse",
|
||||||
|
hasZeroFields: (bool) false
|
||||||
|
},
|
||||||
|
opts: (*main.methodOptions)(0xc00009e6c0)({
|
||||||
|
authType: (*main.supportedAuth)(0xc0003bd290)({
|
||||||
|
id: (string) (len=5) "guest",
|
||||||
|
name: (string) (len=5) "Guest",
|
||||||
|
context: (map[string]string) {
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
method: (string) (len=3) "get",
|
||||||
|
route: (main.decodedRoute) {
|
||||||
|
route: (string) (len=13) "/wizard/state",
|
||||||
|
params: ([]string) <nil>
|
||||||
|
},
|
||||||
|
query: ([]string) <nil>,
|
||||||
|
nostr: (bool) false,
|
||||||
|
batch: (bool) false
|
||||||
|
}),
|
||||||
|
serverStream: (bool) false
|
||||||
|
}),
|
||||||
|
(*main.Method)(0xc000222230)({
|
||||||
|
in: (main.MethodMessage) {
|
||||||
|
name: (string) (len=13) "ConfigRequest",
|
||||||
|
hasZeroFields: (bool) false
|
||||||
|
},
|
||||||
|
name: (string) (len=12) "WizardConfig",
|
||||||
|
out: (main.MethodMessage) {
|
||||||
|
name: (string) (len=14) "ConfigResponse",
|
||||||
|
hasZeroFields: (bool) false
|
||||||
|
},
|
||||||
|
opts: (*main.methodOptions)(0xc00009e840)({
|
||||||
|
authType: (*main.supportedAuth)(0xc0003bd350)({
|
||||||
|
id: (string) (len=5) "guest",
|
||||||
|
name: (string) (len=5) "Guest",
|
||||||
|
context: (map[string]string) {
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
method: (string) (len=4) "post",
|
||||||
|
route: (main.decodedRoute) {
|
||||||
|
route: (string) (len=14) "/wizard/config",
|
||||||
|
params: ([]string) <nil>
|
||||||
|
},
|
||||||
|
query: ([]string) <nil>,
|
||||||
|
nostr: (bool) false,
|
||||||
|
batch: (bool) false
|
||||||
|
}),
|
||||||
|
serverStream: (bool) false
|
||||||
|
}),
|
||||||
|
(*main.Method)(0xc0002225a0)({
|
||||||
|
in: (main.MethodMessage) {
|
||||||
|
name: (string) (len=14) "ConfirmRequest",
|
||||||
|
hasZeroFields: (bool) false
|
||||||
|
},
|
||||||
|
name: (string) (len=13) "WizardConfirm",
|
||||||
|
out: (main.MethodMessage) {
|
||||||
|
name: (string) (len=15) "ConfirmResponse",
|
||||||
|
hasZeroFields: (bool) false
|
||||||
|
},
|
||||||
|
opts: (*main.methodOptions)(0xc00009e9c0)({
|
||||||
|
authType: (*main.supportedAuth)(0xc0003bd410)({
|
||||||
|
id: (string) (len=5) "guest",
|
||||||
|
name: (string) (len=5) "Guest",
|
||||||
|
context: (map[string]string) {
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
method: (string) (len=4) "post",
|
||||||
|
route: (main.decodedRoute) {
|
||||||
|
route: (string) (len=15) "/wizard/confirm",
|
||||||
|
params: ([]string) <nil>
|
||||||
|
},
|
||||||
|
query: ([]string) <nil>,
|
||||||
|
nostr: (bool) false,
|
||||||
|
batch: (bool) false
|
||||||
|
}),
|
||||||
|
serverStream: (bool) false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
([]*main.Enum) <nil>
|
||||||
|
|
||||||
|
(map[string]*main.Message) (len=6) {
|
||||||
|
(string) (len=13) "StateResponse": (*main.Message)(0xc0003ee1c0)({
|
||||||
|
fullName: (string) (len=13) "StateResponse",
|
||||||
|
name: (string) (len=13) "StateResponse",
|
||||||
|
fields: ([]*main.Field) (len=1 cap=1) {
|
||||||
|
(*main.Field)(0xc0003bccc0)({
|
||||||
|
name: (string) (len=19) "already_initialized",
|
||||||
|
kind: (string) (len=4) "bool",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) false,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
(string) (len=13) "ConfigRequest": (*main.Message)(0xc0003ee200)({
|
||||||
|
fullName: (string) (len=13) "ConfigRequest",
|
||||||
|
name: (string) (len=13) "ConfigRequest",
|
||||||
|
fields: ([]*main.Field) (len=4 cap=4) {
|
||||||
|
(*main.Field)(0xc0003bccf0)({
|
||||||
|
name: (string) (len=11) "source_name",
|
||||||
|
kind: (string) (len=6) "string",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) false,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
}),
|
||||||
|
(*main.Field)(0xc0003bcd20)({
|
||||||
|
name: (string) (len=9) "relay_url",
|
||||||
|
kind: (string) (len=6) "string",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) false,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
}),
|
||||||
|
(*main.Field)(0xc0003bcd50)({
|
||||||
|
name: (string) (len=18) "automate_liquidity",
|
||||||
|
kind: (string) (len=4) "bool",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) false,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
}),
|
||||||
|
(*main.Field)(0xc0003bcd80)({
|
||||||
|
name: (string) (len=21) "push_backups_to_nostr",
|
||||||
|
kind: (string) (len=4) "bool",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) false,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
(string) (len=14) "ConfigResponse": (*main.Message)(0xc0003ee240)({
|
||||||
|
fullName: (string) (len=14) "ConfigResponse",
|
||||||
|
name: (string) (len=14) "ConfigResponse",
|
||||||
|
fields: ([]*main.Field) (len=3 cap=4) {
|
||||||
|
(*main.Field)(0xc0003bcdb0)({
|
||||||
|
name: (string) (len=19) "already_initialized",
|
||||||
|
kind: (string) (len=4) "bool",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) false,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
}),
|
||||||
|
(*main.Field)(0xc0003bcde0)({
|
||||||
|
name: (string) (len=4) "seed",
|
||||||
|
kind: (string) (len=6) "string",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) true,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
}),
|
||||||
|
(*main.Field)(0xc0003bce10)({
|
||||||
|
name: (string) (len=15) "confirmation_id",
|
||||||
|
kind: (string) (len=6) "string",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) false,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
(string) (len=14) "ConfirmRequest": (*main.Message)(0xc0003ee280)({
|
||||||
|
fullName: (string) (len=14) "ConfirmRequest",
|
||||||
|
name: (string) (len=14) "ConfirmRequest",
|
||||||
|
fields: ([]*main.Field) (len=1 cap=1) {
|
||||||
|
(*main.Field)(0xc0003bce40)({
|
||||||
|
name: (string) (len=15) "confirmation_id",
|
||||||
|
kind: (string) (len=6) "string",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) false,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
(string) (len=15) "ConfirmResponse": (*main.Message)(0xc0003ee2c0)({
|
||||||
|
fullName: (string) (len=15) "ConfirmResponse",
|
||||||
|
name: (string) (len=15) "ConfirmResponse",
|
||||||
|
fields: ([]*main.Field) (len=1 cap=1) {
|
||||||
|
(*main.Field)(0xc0003bce70)({
|
||||||
|
name: (string) (len=9) "admin_key",
|
||||||
|
kind: (string) (len=6) "string",
|
||||||
|
isMap: (bool) false,
|
||||||
|
isArray: (bool) false,
|
||||||
|
isEnum: (bool) false,
|
||||||
|
isMessage: (bool) false,
|
||||||
|
isOptional: (bool) false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
(string) (len=5) "Empty": (*main.Message)(0xc0003ee080)({
|
||||||
|
fullName: (string) (len=5) "Empty",
|
||||||
|
name: (string) (len=5) "Empty",
|
||||||
|
fields: ([]*main.Field) <nil>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
parsing file: wizard_structs 6
|
||||||
|
parsing file: wizard_methods 2
|
||||||
|
-> [{guest Guest map[]}]
|
||||||
|
|
||||||
104
proto/wizard_service/autogenerated/ts/express_server.ts
Normal file
104
proto/wizard_service/autogenerated/ts/express_server.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
// This file was autogenerated from a .proto file, DO NOT EDIT!
|
||||||
|
|
||||||
|
import express, { Response, json, urlencoded } from 'express'
|
||||||
|
import cors from 'cors'
|
||||||
|
import * as Types from './types.js'
|
||||||
|
export type Logger = { log: (v: any) => void, error: (v: any) => void }
|
||||||
|
export type ServerOptions = {
|
||||||
|
allowCors?: true
|
||||||
|
staticFiles?: string
|
||||||
|
allowNotImplementedMethods?: true
|
||||||
|
logger?: Logger
|
||||||
|
throwErrors?: true
|
||||||
|
logMethod?: true
|
||||||
|
logBody?: true
|
||||||
|
metricsCallback: (metrics: Types.RequestMetric[]) => void
|
||||||
|
GuestAuthGuard: (authorizationHeader?: string) => Promise<Types.GuestContext>
|
||||||
|
}
|
||||||
|
declare module 'express-serve-static-core' { interface Request { startTime?: bigint, bodySize?: number, startTimeMs: number } }
|
||||||
|
const logErrorAndReturnResponse = (error: Error, response: string, res: Response, logger: Logger, metric: Types.RequestMetric, metricsCallback: (metrics: Types.RequestMetric[]) => void) => {
|
||||||
|
logger.error(error.message || error); metricsCallback([{ ...metric, error: response }]); res.json({ status: 'ERROR', reason: response })
|
||||||
|
}
|
||||||
|
export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
||||||
|
const logger = opts.logger || { log: console.log, error: console.error }
|
||||||
|
const app = express()
|
||||||
|
if (opts.allowCors) {
|
||||||
|
app.use(cors())
|
||||||
|
}
|
||||||
|
app.use((req, _, next) => { req.startTime = process.hrtime.bigint(); req.startTimeMs = Date.now(); next() })
|
||||||
|
app.use(json())
|
||||||
|
app.use(urlencoded({ extended: true }))
|
||||||
|
if (opts.logMethod) app.use((req, _, next) => { console.log(req.method, req.path); if (opts.logBody) console.log(req.body); next() })
|
||||||
|
if (!opts.allowNotImplementedMethods && !methods.WizardState) throw new Error('method: WizardState is not implemented')
|
||||||
|
app.get('/wizard/state', async (req, res) => {
|
||||||
|
const info: Types.RequestInfo = { rpcName: 'WizardState', batch: false, nostr: false, batchSize: 0}
|
||||||
|
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||||
|
let authCtx: Types.AuthContext = {}
|
||||||
|
try {
|
||||||
|
if (!methods.WizardState) throw new Error('method: WizardState is not implemented')
|
||||||
|
const authContext = await opts.GuestAuthGuard(req.headers['authorization'])
|
||||||
|
authCtx = authContext
|
||||||
|
stats.guard = process.hrtime.bigint()
|
||||||
|
stats.validate = stats.guard
|
||||||
|
const query = req.query
|
||||||
|
const params = req.params
|
||||||
|
const response = await methods.WizardState({rpcName:'WizardState', ctx:authContext })
|
||||||
|
stats.handle = process.hrtime.bigint()
|
||||||
|
res.json({status: 'OK', ...response})
|
||||||
|
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||||
|
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||||
|
})
|
||||||
|
if (!opts.allowNotImplementedMethods && !methods.WizardConfig) throw new Error('method: WizardConfig is not implemented')
|
||||||
|
app.post('/wizard/config', async (req, res) => {
|
||||||
|
const info: Types.RequestInfo = { rpcName: 'WizardConfig', batch: false, nostr: false, batchSize: 0}
|
||||||
|
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||||
|
let authCtx: Types.AuthContext = {}
|
||||||
|
try {
|
||||||
|
if (!methods.WizardConfig) throw new Error('method: WizardConfig is not implemented')
|
||||||
|
const authContext = await opts.GuestAuthGuard(req.headers['authorization'])
|
||||||
|
authCtx = authContext
|
||||||
|
stats.guard = process.hrtime.bigint()
|
||||||
|
const request = req.body
|
||||||
|
const error = Types.ConfigRequestValidate(request)
|
||||||
|
stats.validate = process.hrtime.bigint()
|
||||||
|
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
|
||||||
|
const query = req.query
|
||||||
|
const params = req.params
|
||||||
|
const response = await methods.WizardConfig({rpcName:'WizardConfig', ctx:authContext , req: request})
|
||||||
|
stats.handle = process.hrtime.bigint()
|
||||||
|
res.json({status: 'OK', ...response})
|
||||||
|
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||||
|
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||||
|
})
|
||||||
|
if (!opts.allowNotImplementedMethods && !methods.WizardConfirm) throw new Error('method: WizardConfirm is not implemented')
|
||||||
|
app.post('/wizard/confirm', async (req, res) => {
|
||||||
|
const info: Types.RequestInfo = { rpcName: 'WizardConfirm', batch: false, nostr: false, batchSize: 0}
|
||||||
|
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||||
|
let authCtx: Types.AuthContext = {}
|
||||||
|
try {
|
||||||
|
if (!methods.WizardConfirm) throw new Error('method: WizardConfirm is not implemented')
|
||||||
|
const authContext = await opts.GuestAuthGuard(req.headers['authorization'])
|
||||||
|
authCtx = authContext
|
||||||
|
stats.guard = process.hrtime.bigint()
|
||||||
|
const request = req.body
|
||||||
|
const error = Types.ConfirmRequestValidate(request)
|
||||||
|
stats.validate = process.hrtime.bigint()
|
||||||
|
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
|
||||||
|
const query = req.query
|
||||||
|
const params = req.params
|
||||||
|
const response = await methods.WizardConfirm({rpcName:'WizardConfirm', ctx:authContext , req: request})
|
||||||
|
stats.handle = process.hrtime.bigint()
|
||||||
|
res.json({status: 'OK', ...response})
|
||||||
|
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
|
||||||
|
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
|
||||||
|
})
|
||||||
|
if (opts.staticFiles) {
|
||||||
|
app.use(express.static(opts.staticFiles))
|
||||||
|
app.get('*', function (_, res) { res.sendFile('index.html', { root: opts.staticFiles })})
|
||||||
|
}
|
||||||
|
var server: { close: () => void } | undefined
|
||||||
|
return {
|
||||||
|
Close: () => { if (!server) { throw new Error('tried closing server before starting') } else server.close() },
|
||||||
|
Listen: (port: number) => { server = app.listen(port, () => logger.log('Example app listening on port ' + port)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
57
proto/wizard_service/autogenerated/ts/http_client.ts
Normal file
57
proto/wizard_service/autogenerated/ts/http_client.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
// This file was autogenerated from a .proto file, DO NOT EDIT!
|
||||||
|
import axios from 'axios'
|
||||||
|
import * as Types from './types.js'
|
||||||
|
export type ResultError = { status: 'ERROR', reason: string }
|
||||||
|
|
||||||
|
export type ClientParams = {
|
||||||
|
baseUrl: string
|
||||||
|
retrieveGuestAuth: () => Promise<string | null>
|
||||||
|
encryptCallback: (plain: any) => Promise<any>
|
||||||
|
decryptCallback: (encrypted: any) => Promise<any>
|
||||||
|
deviceId: string
|
||||||
|
checkResult?: true
|
||||||
|
}
|
||||||
|
export default (params: ClientParams) => ({
|
||||||
|
WizardState: async (): Promise<ResultError | ({ status: 'OK' }& Types.StateResponse)> => {
|
||||||
|
const auth = await params.retrieveGuestAuth()
|
||||||
|
if (auth === null) throw new Error('retrieveGuestAuth() returned null')
|
||||||
|
let finalRoute = '/wizard/state'
|
||||||
|
const { data } = await axios.get(params.baseUrl + finalRoute, { headers: { 'authorization': auth } })
|
||||||
|
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||||
|
if (data.status === 'OK') {
|
||||||
|
const result = data
|
||||||
|
if(!params.checkResult) return { status: 'OK', ...result }
|
||||||
|
const error = Types.StateResponseValidate(result)
|
||||||
|
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||||
|
}
|
||||||
|
return { status: 'ERROR', reason: 'invalid response' }
|
||||||
|
},
|
||||||
|
WizardConfig: async (request: Types.ConfigRequest): Promise<ResultError | ({ status: 'OK' }& Types.ConfigResponse)> => {
|
||||||
|
const auth = await params.retrieveGuestAuth()
|
||||||
|
if (auth === null) throw new Error('retrieveGuestAuth() returned null')
|
||||||
|
let finalRoute = '/wizard/config'
|
||||||
|
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||||
|
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||||
|
if (data.status === 'OK') {
|
||||||
|
const result = data
|
||||||
|
if(!params.checkResult) return { status: 'OK', ...result }
|
||||||
|
const error = Types.ConfigResponseValidate(result)
|
||||||
|
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||||
|
}
|
||||||
|
return { status: 'ERROR', reason: 'invalid response' }
|
||||||
|
},
|
||||||
|
WizardConfirm: async (request: Types.ConfirmRequest): Promise<ResultError | ({ status: 'OK' }& Types.ConfirmResponse)> => {
|
||||||
|
const auth = await params.retrieveGuestAuth()
|
||||||
|
if (auth === null) throw new Error('retrieveGuestAuth() returned null')
|
||||||
|
let finalRoute = '/wizard/confirm'
|
||||||
|
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
|
||||||
|
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
|
||||||
|
if (data.status === 'OK') {
|
||||||
|
const result = data
|
||||||
|
if(!params.checkResult) return { status: 'OK', ...result }
|
||||||
|
const error = Types.ConfirmResponseValidate(result)
|
||||||
|
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
|
||||||
|
}
|
||||||
|
return { status: 'ERROR', reason: 'invalid response' }
|
||||||
|
},
|
||||||
|
})
|
||||||
11
proto/wizard_service/autogenerated/ts/nostr_client.ts
Normal file
11
proto/wizard_service/autogenerated/ts/nostr_client.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// This file was autogenerated from a .proto file, DO NOT EDIT!
|
||||||
|
import { NostrRequest } from './nostr_transport.js'
|
||||||
|
import * as Types from './types.js'
|
||||||
|
export type ResultError = { status: 'ERROR', reason: string }
|
||||||
|
|
||||||
|
export type NostrClientParams = {
|
||||||
|
pubDestination: string
|
||||||
|
checkResult?: true
|
||||||
|
}
|
||||||
|
export default (params: NostrClientParams, send: (to:string, message: NostrRequest) => Promise<any>, subscribe: (to:string, message: NostrRequest, cb:(res:any)=> void) => void) => ({
|
||||||
|
})
|
||||||
34
proto/wizard_service/autogenerated/ts/nostr_transport.ts
Normal file
34
proto/wizard_service/autogenerated/ts/nostr_transport.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
// This file was autogenerated from a .proto file, DO NOT EDIT!
|
||||||
|
|
||||||
|
import * as Types from './types.js'
|
||||||
|
export type Logger = { log: (v: any) => void, error: (v: any) => void }
|
||||||
|
type NostrResponse = (message: object) => void
|
||||||
|
export type NostrRequest = {
|
||||||
|
rpcName?: string
|
||||||
|
params?: Record<string, string>
|
||||||
|
query?: Record<string, string>
|
||||||
|
body?: any
|
||||||
|
authIdentifier?: string
|
||||||
|
requestId?: string
|
||||||
|
appId?: string
|
||||||
|
}
|
||||||
|
export type NostrOptions = {
|
||||||
|
logger?: Logger
|
||||||
|
throwErrors?: true
|
||||||
|
metricsCallback: (metrics: Types.RequestMetric[]) => void
|
||||||
|
}
|
||||||
|
const logErrorAndReturnResponse = (error: Error, response: string, res: NostrResponse, logger: Logger, metric: Types.RequestMetric, metricsCallback: (metrics: Types.RequestMetric[]) => void) => {
|
||||||
|
logger.error(error.message || error); metricsCallback([{ ...metric, error: response }]); res({ status: 'ERROR', reason: response })
|
||||||
|
}
|
||||||
|
export default (methods: Types.ServerMethods, opts: NostrOptions) => {
|
||||||
|
const logger = opts.logger || { log: console.log, error: console.error }
|
||||||
|
return async (req: NostrRequest, res: NostrResponse, startString: string, startMs: number) => {
|
||||||
|
const startTime = BigInt(startString)
|
||||||
|
const info: Types.RequestInfo = { rpcName: req.rpcName || 'unkown', batch: false, nostr: true, batchSize: 0 }
|
||||||
|
const stats: Types.RequestStats = { startMs, start: startTime, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
|
||||||
|
let authCtx: Types.AuthContext = {}
|
||||||
|
switch (req.rpcName) {
|
||||||
|
default: logger.error('unknown rpc call name from nostr event:'+req.rpcName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
163
proto/wizard_service/autogenerated/ts/types.ts
Normal file
163
proto/wizard_service/autogenerated/ts/types.ts
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
// This file was autogenerated from a .proto file, DO NOT EDIT!
|
||||||
|
|
||||||
|
export type ResultError = { status: 'ERROR', reason: string }
|
||||||
|
export type RequestInfo = { rpcName: string, batch: boolean, nostr: boolean, batchSize: number }
|
||||||
|
export type RequestStats = { startMs:number, start:bigint, parse: bigint, guard: bigint, validate: bigint, handle: bigint }
|
||||||
|
export type RequestMetric = AuthContext & RequestInfo & RequestStats & { error?: string }
|
||||||
|
export type GuestContext = {
|
||||||
|
}
|
||||||
|
export type GuestMethodInputs = WizardState_Input | WizardConfig_Input | WizardConfirm_Input
|
||||||
|
export type GuestMethodOutputs = WizardState_Output | WizardConfig_Output | WizardConfirm_Output
|
||||||
|
export type AuthContext = GuestContext
|
||||||
|
|
||||||
|
export type WizardState_Input = {rpcName:'WizardState'}
|
||||||
|
export type WizardState_Output = ResultError | ({ status: 'OK' } & StateResponse)
|
||||||
|
|
||||||
|
export type WizardConfig_Input = {rpcName:'WizardConfig', req: ConfigRequest}
|
||||||
|
export type WizardConfig_Output = ResultError | ({ status: 'OK' } & ConfigResponse)
|
||||||
|
|
||||||
|
export type WizardConfirm_Input = {rpcName:'WizardConfirm', req: ConfirmRequest}
|
||||||
|
export type WizardConfirm_Output = ResultError | ({ status: 'OK' } & ConfirmResponse)
|
||||||
|
|
||||||
|
export type ServerMethods = {
|
||||||
|
WizardState?: (req: WizardState_Input & {ctx: GuestContext }) => Promise<StateResponse>
|
||||||
|
WizardConfig?: (req: WizardConfig_Input & {ctx: GuestContext }) => Promise<ConfigResponse>
|
||||||
|
WizardConfirm?: (req: WizardConfirm_Input & {ctx: GuestContext }) => Promise<ConfirmResponse>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type OptionsBaseMessage = {
|
||||||
|
allOptionalsAreSet?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ConfigResponse = {
|
||||||
|
already_initialized: boolean
|
||||||
|
seed: string[]
|
||||||
|
confirmation_id: string
|
||||||
|
}
|
||||||
|
export const ConfigResponseOptionalFields: [] = []
|
||||||
|
export type ConfigResponseOptions = OptionsBaseMessage & {
|
||||||
|
checkOptionalsAreSet?: []
|
||||||
|
already_initialized_CustomCheck?: (v: boolean) => boolean
|
||||||
|
seed_CustomCheck?: (v: string[]) => boolean
|
||||||
|
confirmation_id_CustomCheck?: (v: string) => boolean
|
||||||
|
}
|
||||||
|
export const ConfigResponseValidate = (o?: ConfigResponse, opts: ConfigResponseOptions = {}, path: string = 'ConfigResponse::root.'): Error | null => {
|
||||||
|
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||||
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
|
if (typeof o.already_initialized !== 'boolean') return new Error(`${path}.already_initialized: is not a boolean`)
|
||||||
|
if (opts.already_initialized_CustomCheck && !opts.already_initialized_CustomCheck(o.already_initialized)) return new Error(`${path}.already_initialized: custom check failed`)
|
||||||
|
|
||||||
|
if (!Array.isArray(o.seed)) return new Error(`${path}.seed: is not an array`)
|
||||||
|
for (let index = 0; index < o.seed.length; index++) {
|
||||||
|
if (typeof o.seed[index] !== 'string') return new Error(`${path}.seed[${index}]: is not a string`)
|
||||||
|
}
|
||||||
|
if (opts.seed_CustomCheck && !opts.seed_CustomCheck(o.seed)) return new Error(`${path}.seed: custom check failed`)
|
||||||
|
|
||||||
|
if (typeof o.confirmation_id !== 'string') return new Error(`${path}.confirmation_id: is not a string`)
|
||||||
|
if (opts.confirmation_id_CustomCheck && !opts.confirmation_id_CustomCheck(o.confirmation_id)) return new Error(`${path}.confirmation_id: custom check failed`)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ConfirmRequest = {
|
||||||
|
confirmation_id: string
|
||||||
|
}
|
||||||
|
export const ConfirmRequestOptionalFields: [] = []
|
||||||
|
export type ConfirmRequestOptions = OptionsBaseMessage & {
|
||||||
|
checkOptionalsAreSet?: []
|
||||||
|
confirmation_id_CustomCheck?: (v: string) => boolean
|
||||||
|
}
|
||||||
|
export const ConfirmRequestValidate = (o?: ConfirmRequest, opts: ConfirmRequestOptions = {}, path: string = 'ConfirmRequest::root.'): Error | null => {
|
||||||
|
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||||
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
|
if (typeof o.confirmation_id !== 'string') return new Error(`${path}.confirmation_id: is not a string`)
|
||||||
|
if (opts.confirmation_id_CustomCheck && !opts.confirmation_id_CustomCheck(o.confirmation_id)) return new Error(`${path}.confirmation_id: custom check failed`)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ConfirmResponse = {
|
||||||
|
admin_key: string
|
||||||
|
}
|
||||||
|
export const ConfirmResponseOptionalFields: [] = []
|
||||||
|
export type ConfirmResponseOptions = OptionsBaseMessage & {
|
||||||
|
checkOptionalsAreSet?: []
|
||||||
|
admin_key_CustomCheck?: (v: string) => boolean
|
||||||
|
}
|
||||||
|
export const ConfirmResponseValidate = (o?: ConfirmResponse, opts: ConfirmResponseOptions = {}, path: string = 'ConfirmResponse::root.'): Error | null => {
|
||||||
|
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||||
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
|
if (typeof o.admin_key !== 'string') return new Error(`${path}.admin_key: is not a string`)
|
||||||
|
if (opts.admin_key_CustomCheck && !opts.admin_key_CustomCheck(o.admin_key)) return new Error(`${path}.admin_key: custom check failed`)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Empty = {
|
||||||
|
}
|
||||||
|
export const EmptyOptionalFields: [] = []
|
||||||
|
export type EmptyOptions = OptionsBaseMessage & {
|
||||||
|
checkOptionalsAreSet?: []
|
||||||
|
}
|
||||||
|
export const EmptyValidate = (o?: Empty, opts: EmptyOptions = {}, path: string = 'Empty::root.'): Error | null => {
|
||||||
|
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||||
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StateResponse = {
|
||||||
|
already_initialized: boolean
|
||||||
|
}
|
||||||
|
export const StateResponseOptionalFields: [] = []
|
||||||
|
export type StateResponseOptions = OptionsBaseMessage & {
|
||||||
|
checkOptionalsAreSet?: []
|
||||||
|
already_initialized_CustomCheck?: (v: boolean) => boolean
|
||||||
|
}
|
||||||
|
export const StateResponseValidate = (o?: StateResponse, opts: StateResponseOptions = {}, path: string = 'StateResponse::root.'): Error | null => {
|
||||||
|
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||||
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
|
if (typeof o.already_initialized !== 'boolean') return new Error(`${path}.already_initialized: is not a boolean`)
|
||||||
|
if (opts.already_initialized_CustomCheck && !opts.already_initialized_CustomCheck(o.already_initialized)) return new Error(`${path}.already_initialized: custom check failed`)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ConfigRequest = {
|
||||||
|
source_name: string
|
||||||
|
relay_url: string
|
||||||
|
automate_liquidity: boolean
|
||||||
|
push_backups_to_nostr: boolean
|
||||||
|
}
|
||||||
|
export const ConfigRequestOptionalFields: [] = []
|
||||||
|
export type ConfigRequestOptions = OptionsBaseMessage & {
|
||||||
|
checkOptionalsAreSet?: []
|
||||||
|
source_name_CustomCheck?: (v: string) => boolean
|
||||||
|
relay_url_CustomCheck?: (v: string) => boolean
|
||||||
|
automate_liquidity_CustomCheck?: (v: boolean) => boolean
|
||||||
|
push_backups_to_nostr_CustomCheck?: (v: boolean) => boolean
|
||||||
|
}
|
||||||
|
export const ConfigRequestValidate = (o?: ConfigRequest, opts: ConfigRequestOptions = {}, path: string = 'ConfigRequest::root.'): Error | null => {
|
||||||
|
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
|
||||||
|
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||||
|
|
||||||
|
if (typeof o.source_name !== 'string') return new Error(`${path}.source_name: is not a string`)
|
||||||
|
if (opts.source_name_CustomCheck && !opts.source_name_CustomCheck(o.source_name)) return new Error(`${path}.source_name: custom check failed`)
|
||||||
|
|
||||||
|
if (typeof o.relay_url !== 'string') return new Error(`${path}.relay_url: is not a string`)
|
||||||
|
if (opts.relay_url_CustomCheck && !opts.relay_url_CustomCheck(o.relay_url)) return new Error(`${path}.relay_url: custom check failed`)
|
||||||
|
|
||||||
|
if (typeof o.automate_liquidity !== 'boolean') return new Error(`${path}.automate_liquidity: is not a boolean`)
|
||||||
|
if (opts.automate_liquidity_CustomCheck && !opts.automate_liquidity_CustomCheck(o.automate_liquidity)) return new Error(`${path}.automate_liquidity: custom check failed`)
|
||||||
|
|
||||||
|
if (typeof o.push_backups_to_nostr !== 'boolean') return new Error(`${path}.push_backups_to_nostr: is not a boolean`)
|
||||||
|
if (opts.push_backups_to_nostr_CustomCheck && !opts.push_backups_to_nostr_CustomCheck(o.push_backups_to_nostr)) return new Error(`${path}.push_backups_to_nostr: custom check failed`)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import path from 'path';
|
|
||||||
import { ServerOptions } from "../proto/autogenerated/ts/express_server";
|
import { ServerOptions } from "../proto/autogenerated/ts/express_server";
|
||||||
import { AdminContext, MetricsContext } from "../proto/autogenerated/ts/types";
|
import { AdminContext, MetricsContext } from "../proto/autogenerated/ts/types";
|
||||||
import Main from './services/main'
|
import Main from './services/main'
|
||||||
|
|
@ -8,7 +8,6 @@ const serverOptions = (mainHandler: Main): ServerOptions => {
|
||||||
const log = getLogger({})
|
const log = getLogger({})
|
||||||
return {
|
return {
|
||||||
logger: { log, error: err => log(ERROR, err) },
|
logger: { log, error: err => log(ERROR, err) },
|
||||||
staticFiles: path.resolve('static'),
|
|
||||||
AdminAuthGuard: adminAuth,
|
AdminAuthGuard: adminAuth,
|
||||||
MetricsAuthGuard: metricsAuth,
|
MetricsAuthGuard: metricsAuth,
|
||||||
AppAuthGuard: async (authHeader) => { return { app_id: mainHandler.applicationManager.DecodeAppToken(stripBearer(authHeader)) } },
|
AppAuthGuard: async (authHeader) => { return { app_id: mainHandler.applicationManager.DecodeAppToken(stripBearer(authHeader)) } },
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ import Storage from "../storage/index.js"
|
||||||
import { TypeOrmMigrationRunner } from "../storage/migrations/runner.js"
|
import { TypeOrmMigrationRunner } from "../storage/migrations/runner.js"
|
||||||
import Main from "./index.js"
|
import Main from "./index.js"
|
||||||
import SanityChecker from "./sanityChecker.js"
|
import SanityChecker from "./sanityChecker.js"
|
||||||
import { MainSettings } from "./settings.js"
|
import { LoadMainSettingsFromEnv, MainSettings } from "./settings.js"
|
||||||
import { Utils } from "../helpers/utilsWrapper.js"
|
import { Utils } from "../helpers/utilsWrapper.js"
|
||||||
|
import { Wizard } from "../wizard/index.js"
|
||||||
export type AppData = {
|
export type AppData = {
|
||||||
privateKey: string;
|
privateKey: string;
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
|
|
@ -20,20 +21,30 @@ export const initMainHandler = async (log: PubLogger, mainSettings: MainSettings
|
||||||
if (manualMigration) {
|
if (manualMigration) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let reloadedSettings = mainSettings
|
||||||
|
if (mainSettings.wizard) {
|
||||||
|
const wizard = new Wizard(mainSettings, storageManager)
|
||||||
|
const reload = await wizard.WaitUntilConfigured()
|
||||||
|
if (reload) {
|
||||||
|
reloadedSettings = LoadMainSettingsFromEnv()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const unlocker = new Unlocker(mainSettings, storageManager)
|
const unlocker = new Unlocker(mainSettings, storageManager)
|
||||||
await unlocker.Unlock()
|
await unlocker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
const mainHandler = new Main(mainSettings, storageManager, utils)
|
const mainHandler = new Main(reloadedSettings, storageManager, utils)
|
||||||
await mainHandler.lnd.Warmup()
|
await mainHandler.lnd.Warmup()
|
||||||
if (!mainSettings.skipSanityCheck) {
|
if (!reloadedSettings.skipSanityCheck) {
|
||||||
const sanityChecker = new SanityChecker(storageManager, mainHandler.lnd)
|
const sanityChecker = new SanityChecker(storageManager, mainHandler.lnd)
|
||||||
await sanityChecker.VerifyEventsLog()
|
await sanityChecker.VerifyEventsLog()
|
||||||
}
|
}
|
||||||
const appsData = await mainHandler.storage.applicationStorage.GetApplications()
|
const appsData = await mainHandler.storage.applicationStorage.GetApplications()
|
||||||
const existingWalletApp = await appsData.find(app => app.name === 'wallet' || app.name === 'wallet-test')
|
const defaultNames = ['wallet', 'wallet-test', reloadedSettings.defaultAppName]
|
||||||
|
const existingWalletApp = await appsData.find(app => defaultNames.includes(app.name))
|
||||||
if (!existingWalletApp) {
|
if (!existingWalletApp) {
|
||||||
log("no default wallet app found, creating one...")
|
log("no default wallet app found, creating one...")
|
||||||
const newWalletApp = await mainHandler.storage.applicationStorage.AddApplication('wallet', true)
|
const newWalletApp = await mainHandler.storage.applicationStorage.AddApplication(reloadedSettings.defaultAppName, true)
|
||||||
appsData.push(newWalletApp)
|
appsData.push(newWalletApp)
|
||||||
}
|
}
|
||||||
const apps: AppData[] = await Promise.all(appsData.map(app => {
|
const apps: AppData[] = await Promise.all(appsData.map(app => {
|
||||||
|
|
@ -44,7 +55,7 @@ export const initMainHandler = async (log: PubLogger, mainSettings: MainSettings
|
||||||
return { privateKey: app.nostr_private_key, publicKey: app.nostr_public_key, appId: app.app_id, name: app.name }
|
return { privateKey: app.nostr_private_key, publicKey: app.nostr_public_key, appId: app.app_id, name: app.name }
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
const liquidityProviderApp = apps.find(app => app.name === 'wallet' || app.name === 'wallet-test')
|
const liquidityProviderApp = apps.find(app => defaultNames.includes(app.name))
|
||||||
if (!liquidityProviderApp) {
|
if (!liquidityProviderApp) {
|
||||||
throw new Error("wallet app not initialized correctly")
|
throw new Error("wallet app not initialized correctly")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ export type MainSettings = {
|
||||||
recordPerformance: boolean
|
recordPerformance: boolean
|
||||||
skipSanityCheck: boolean
|
skipSanityCheck: boolean
|
||||||
disableExternalPayments: boolean
|
disableExternalPayments: boolean
|
||||||
|
wizard: boolean
|
||||||
|
defaultAppName: string
|
||||||
|
pushBackupsToNostr: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BitcoinCoreSettings = {
|
export type BitcoinCoreSettings = {
|
||||||
|
|
@ -63,6 +66,9 @@ export const LoadMainSettingsFromEnv = (): MainSettings => {
|
||||||
recordPerformance: process.env.RECORD_PERFORMANCE === 'true' || false,
|
recordPerformance: process.env.RECORD_PERFORMANCE === 'true' || false,
|
||||||
skipSanityCheck: process.env.SKIP_SANITY_CHECK === 'true' || false,
|
skipSanityCheck: process.env.SKIP_SANITY_CHECK === 'true' || false,
|
||||||
disableExternalPayments: process.env.DISABLE_EXTERNAL_PAYMENTS === 'true' || false,
|
disableExternalPayments: process.env.DISABLE_EXTERNAL_PAYMENTS === 'true' || false,
|
||||||
|
wizard: process.env.WIZARD === 'true' || false,
|
||||||
|
defaultAppName: process.env.DEFAULT_APP_NAME || "wallet",
|
||||||
|
pushBackupsToNostr: process.env.PUSH_BACKUPS_TO_NOSTR === 'true' || false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,13 @@ import { InitWalletReq } from '../lnd/initWalletReq.js';
|
||||||
import Storage from '../storage/index.js'
|
import Storage from '../storage/index.js'
|
||||||
import { LightningClient } from '../../../proto/lnd/lightning.client.js';
|
import { LightningClient } from '../../../proto/lnd/lightning.client.js';
|
||||||
const DeadLineMetadata = (deadline = 10 * 1000) => ({ deadline: Date.now() + deadline })
|
const DeadLineMetadata = (deadline = 10 * 1000) => ({ deadline: Date.now() + deadline })
|
||||||
|
type EncryptedData = { iv: string, encrypted: string }
|
||||||
|
type Seed = { plaintextSeed: string[], encryptedSeed: EncryptedData }
|
||||||
export class Unlocker {
|
export class Unlocker {
|
||||||
settings: MainSettings
|
settings: MainSettings
|
||||||
storage: Storage
|
storage: Storage
|
||||||
abortController = new AbortController()
|
abortController = new AbortController()
|
||||||
|
pendingSeed: Record<string, EncryptedData> = {}
|
||||||
log = getLogger({ component: "unlocker" })
|
log = getLogger({ component: "unlocker" })
|
||||||
constructor(settings: MainSettings, storage: Storage) {
|
constructor(settings: MainSettings, storage: Storage) {
|
||||||
this.settings = settings
|
this.settings = settings
|
||||||
|
|
@ -23,7 +26,7 @@ export class Unlocker {
|
||||||
this.abortController.abort()
|
this.abortController.abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
Unlock = async () => {
|
getCreds = () => {
|
||||||
const macroonPath = this.settings.lndSettings.mainNode.lndMacaroonPath
|
const macroonPath = this.settings.lndSettings.mainNode.lndMacaroonPath
|
||||||
const certPath = this.settings.lndSettings.mainNode.lndCertPath
|
const certPath = this.settings.lndSettings.mainNode.lndCertPath
|
||||||
let macaroon = ""
|
let macaroon = ""
|
||||||
|
|
@ -40,6 +43,48 @@ export class Unlocker {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return { lndCert, macaroon }
|
||||||
|
}
|
||||||
|
|
||||||
|
IsInitialized = async () => {
|
||||||
|
const { macaroon } = await this.getCreds()
|
||||||
|
return macaroon !== ''
|
||||||
|
}
|
||||||
|
|
||||||
|
InitInteractive = async (): Promise<{ alreadyInitizialized: true } | { alreadyInitizialized: false, seed: string[], confirmationId: string }> => {
|
||||||
|
const { lndCert, macaroon } = await this.getCreds()
|
||||||
|
if (macaroon !== '') {
|
||||||
|
const { ln, pub } = await this.UnlockFlow(lndCert, macaroon)
|
||||||
|
this.subscribeToBackups(ln, pub)
|
||||||
|
return { alreadyInitizialized: true }
|
||||||
|
}
|
||||||
|
const unlocker = this.GetUnlockerClient(lndCert)
|
||||||
|
const seed = await this.genSeed(unlocker)
|
||||||
|
const confirmationId = crypto.randomBytes(32).toString('hex')
|
||||||
|
this.pendingSeed[confirmationId] = seed.encryptedSeed
|
||||||
|
return { alreadyInitizialized: false, seed: seed.plaintextSeed, confirmationId }
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmInitInteractive = async (confirmationId: string) => {
|
||||||
|
const { lndCert, macaroon } = await this.getCreds()
|
||||||
|
if (macaroon !== '') {
|
||||||
|
const { ln, pub } = await this.UnlockFlow(lndCert, macaroon)
|
||||||
|
this.subscribeToBackups(ln, pub)
|
||||||
|
return { alreadyInitizialized: true }
|
||||||
|
}
|
||||||
|
const seed = this.pendingSeed[confirmationId]
|
||||||
|
if (!seed) {
|
||||||
|
throw new Error("seed not found")
|
||||||
|
}
|
||||||
|
delete this.pendingSeed[confirmationId]
|
||||||
|
const plaintextSeed = this.DecryptWalletSeed(seed)
|
||||||
|
const unlocker = this.GetUnlockerClient(lndCert)
|
||||||
|
const { ln, pub } = await this.initWallet(lndCert, unlocker, { plaintextSeed, encryptedSeed: seed })
|
||||||
|
this.subscribeToBackups(ln, pub)
|
||||||
|
}
|
||||||
|
|
||||||
|
Unlock = async () => {
|
||||||
|
const { lndCert, macaroon } = await this.getCreds()
|
||||||
const { ln, pub } = macaroon === "" ? await this.InitFlow(lndCert) : await this.UnlockFlow(lndCert, macaroon)
|
const { ln, pub } = macaroon === "" ? await this.InitFlow(lndCert) : await this.UnlockFlow(lndCert, macaroon)
|
||||||
this.subscribeToBackups(ln, pub)
|
this.subscribeToBackups(ln, pub)
|
||||||
}
|
}
|
||||||
|
|
@ -69,15 +114,24 @@ export class Unlocker {
|
||||||
InitFlow = async (lndCert: Buffer) => {
|
InitFlow = async (lndCert: Buffer) => {
|
||||||
this.log("macaroon not found, creating wallet...")
|
this.log("macaroon not found, creating wallet...")
|
||||||
const unlocker = this.GetUnlockerClient(lndCert)
|
const unlocker = this.GetUnlockerClient(lndCert)
|
||||||
|
const { plaintextSeed, encryptedSeed } = await this.genSeed(unlocker)
|
||||||
|
return this.initWallet(lndCert, unlocker, { plaintextSeed, encryptedSeed })
|
||||||
|
}
|
||||||
|
|
||||||
|
genSeed = async (unlocker: WalletUnlockerClient): Promise<Seed> => {
|
||||||
const entropy = crypto.randomBytes(16)
|
const entropy = crypto.randomBytes(16)
|
||||||
const seedRes = await unlocker.genSeed({
|
const seedRes = await unlocker.genSeed({
|
||||||
aezeedPassphrase: Buffer.alloc(0),
|
aezeedPassphrase: Buffer.alloc(0),
|
||||||
seedEntropy: entropy
|
seedEntropy: entropy
|
||||||
}, DeadLineMetadata())
|
}, DeadLineMetadata())
|
||||||
this.log("seed created, encrypting and saving...")
|
this.log("seed created")
|
||||||
const { encryptedData } = this.EncryptWalletSeed(seedRes.response.cipherSeedMnemonic)
|
const { encryptedData } = this.EncryptWalletSeed(seedRes.response.cipherSeedMnemonic)
|
||||||
|
return { plaintextSeed: seedRes.response.cipherSeedMnemonic, encryptedSeed: encryptedData }
|
||||||
|
}
|
||||||
|
|
||||||
|
initWallet = async (lndCert: Buffer, unlocker: WalletUnlockerClient, seed: Seed) => {
|
||||||
const walletPw = this.GetWalletPassword()
|
const walletPw = this.GetWalletPassword()
|
||||||
const req = InitWalletReq(walletPw, seedRes.response.cipherSeedMnemonic)
|
const req = InitWalletReq(walletPw, seed.plaintextSeed)
|
||||||
const initRes = await unlocker.initWallet(req, DeadLineMetadata(60 * 1000))
|
const initRes = await unlocker.initWallet(req, DeadLineMetadata(60 * 1000))
|
||||||
const adminMacaroon = Buffer.from(initRes.response.adminMacaroon).toString('hex')
|
const adminMacaroon = Buffer.from(initRes.response.adminMacaroon).toString('hex')
|
||||||
const ln = this.GetLightningClient(lndCert, adminMacaroon)
|
const ln = this.GetLightningClient(lndCert, adminMacaroon)
|
||||||
|
|
@ -94,11 +148,13 @@ export class Unlocker {
|
||||||
if (!info || !info.ok) {
|
if (!info || !info.ok) {
|
||||||
throw new Error("failed to init lnd wallet " + (info ? info.failure : "unknown error"))
|
throw new Error("failed to init lnd wallet " + (info ? info.failure : "unknown error"))
|
||||||
}
|
}
|
||||||
await this.storage.liquidityStorage.SaveNodeSeed(info.pub, JSON.stringify(encryptedData))
|
await this.storage.liquidityStorage.SaveNodeSeed(info.pub, JSON.stringify(seed.encryptedSeed))
|
||||||
this.log("created wallet with pub:", info.pub)
|
this.log("created wallet with pub:", info.pub)
|
||||||
return { ln, pub: info.pub }
|
return { ln, pub: info.pub }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GetLndInfo = async (ln: LightningClient): Promise<{ ok: false, failure: 'locked' | 'unknown' } | { ok: true, pub: string }> => {
|
GetLndInfo = async (ln: LightningClient): Promise<{ ok: false, failure: 'locked' | 'unknown' } | { ok: true, pub: string }> => {
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
131
src/services/wizard/index.ts
Normal file
131
src/services/wizard/index.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path';
|
||||||
|
import { config as loadEnvFile } from 'dotenv'
|
||||||
|
import { getLogger } from "../helpers/logger.js"
|
||||||
|
import NewWizardServer from "../../../proto/wizard_service/autogenerated/ts/express_server.js"
|
||||||
|
import * as WizardTypes from "../../../proto/wizard_service/autogenerated/ts/types.js"
|
||||||
|
import { MainSettings } from "../main/settings.js"
|
||||||
|
import Storage from '../storage/index.js'
|
||||||
|
import { Unlocker } from "../main/unlocker.js"
|
||||||
|
export type WizardSettings = {
|
||||||
|
sourceName: string
|
||||||
|
relayUrl: string
|
||||||
|
automateLiquidity: boolean
|
||||||
|
pushBackupsToNostr: boolean
|
||||||
|
}
|
||||||
|
const defaultProviderPub = ""
|
||||||
|
export class Wizard {
|
||||||
|
log = getLogger({ component: "wizard" })
|
||||||
|
settings: MainSettings
|
||||||
|
unlocker: Unlocker
|
||||||
|
initialized = false
|
||||||
|
configQueue: { res: (reload: boolean) => void }[] = []
|
||||||
|
pendingConfig: WizardSettings | null = null
|
||||||
|
constructor(mainSettings: MainSettings, storage: Storage) {
|
||||||
|
this.settings = mainSettings
|
||||||
|
this.log('Starting wizard...')
|
||||||
|
this.unlocker = new Unlocker(mainSettings, storage)
|
||||||
|
const wizardServer = NewWizardServer({
|
||||||
|
WizardState: async () => { return { already_initialized: await this.IsInitialized() } },
|
||||||
|
WizardConfig: async ({ req }) => { return this.wizardConfig(req) },
|
||||||
|
WizardConfirm: async ({ req }) => { return this.wizardConfirm(req) },
|
||||||
|
}, { GuestAuthGuard: async () => "", metricsCallback: () => { }, staticFiles: 'static' })
|
||||||
|
wizardServer.Listen(mainSettings.servicePort + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
IsInitialized = () => {
|
||||||
|
if (this.initialized) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return this.unlocker.IsInitialized()
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitUntilConfigured = async (): Promise<boolean> => {
|
||||||
|
if (this.initialized) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return new Promise((res) => {
|
||||||
|
this.configQueue.push({ res })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
wizardConfig = async (req: WizardTypes.ConfigRequest): Promise<WizardTypes.ConfigResponse> => {
|
||||||
|
const err = WizardTypes.ConfigRequestValidate(req, {
|
||||||
|
source_name_CustomCheck: source => source !== '',
|
||||||
|
relay_url_CustomCheck: relay => relay !== '',
|
||||||
|
})
|
||||||
|
if (err != null) { throw new Error(err.message) }
|
||||||
|
|
||||||
|
const res = await this.unlocker.InitInteractive()
|
||||||
|
if (res.alreadyInitizialized) {
|
||||||
|
this.initialized = true
|
||||||
|
this.configQueue.forEach(q => q.res(false))
|
||||||
|
return { already_initialized: true, confirmation_id: "", seed: [] }
|
||||||
|
}
|
||||||
|
this.pendingConfig = { sourceName: req.source_name, relayUrl: req.relay_url, automateLiquidity: req.automate_liquidity, pushBackupsToNostr: req.push_backups_to_nostr }
|
||||||
|
return { already_initialized: false, confirmation_id: res.confirmationId, seed: res.seed }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
wizardConfirm = async (req: WizardTypes.ConfirmRequest): Promise<WizardTypes.ConfirmResponse> => {
|
||||||
|
const err = WizardTypes.ConfirmRequestValidate(req, {
|
||||||
|
confirmation_id_CustomCheck: conf => conf !== '',
|
||||||
|
})
|
||||||
|
if (err != null) { throw new Error(err.message) }
|
||||||
|
|
||||||
|
const res = await this.unlocker.ConfirmInitInteractive(req.confirmation_id)
|
||||||
|
if (res?.alreadyInitizialized) {
|
||||||
|
this.initialized = true
|
||||||
|
this.configQueue.forEach(q => q.res(false))
|
||||||
|
return { admin_key: "" }
|
||||||
|
}
|
||||||
|
this.initialized = true
|
||||||
|
this.updateEnvFile()
|
||||||
|
this.configQueue.forEach(q => q.res(true))
|
||||||
|
return { admin_key: process.env.ADMIN_TOKEN || "" }
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEnvFile = () => {
|
||||||
|
if (!this.pendingConfig) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let envFileContent: string[] = []
|
||||||
|
try {
|
||||||
|
envFileContent = fs.readFileSync('.env', 'utf-8').split('\n')
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.code !== 'ENOENT') {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toMerge: string[] = []
|
||||||
|
const sourceNameIndex = envFileContent.findIndex(line => line.startsWith('DEFAULT_APP_NAME'))
|
||||||
|
if (sourceNameIndex === -1) {
|
||||||
|
toMerge.push(`DEFAULT_APP_NAME=${this.pendingConfig.sourceName}`)
|
||||||
|
} else {
|
||||||
|
envFileContent[sourceNameIndex] = `DEFAULT_APP_NAME=${this.pendingConfig.sourceName}`
|
||||||
|
}
|
||||||
|
const relayUrlIndex = envFileContent.findIndex(line => line.startsWith('RELAY_URL'))
|
||||||
|
if (relayUrlIndex === -1) {
|
||||||
|
toMerge.push(`RELAY_URL=${this.pendingConfig.relayUrl}`)
|
||||||
|
} else {
|
||||||
|
envFileContent[relayUrlIndex] = `RELAY_URL=${this.pendingConfig.relayUrl}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const automateLiquidityIndex = envFileContent.findIndex(line => line.startsWith('LIQUIDITY_PROVIDER_PUB'))
|
||||||
|
if (automateLiquidityIndex === -1) {
|
||||||
|
toMerge.push(`LIQUIDITY_PROVIDER_PUB=${this.pendingConfig.automateLiquidity ? defaultProviderPub : ""}`)
|
||||||
|
} else {
|
||||||
|
envFileContent[automateLiquidityIndex] = `LIQUIDITY_PROVIDER_PUB=`
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushBackupsToNostrIndex = envFileContent.findIndex(line => line.startsWith('PUSH_BACKUPS_TO_NOSTR'))
|
||||||
|
if (pushBackupsToNostrIndex === -1) {
|
||||||
|
toMerge.push(`PUSH_BACKUPS_TO_NOSTR=${this.pendingConfig.pushBackupsToNostr ? 'true' : 'false'}`)
|
||||||
|
} else {
|
||||||
|
envFileContent[pushBackupsToNostrIndex] = `PUSH_BACKUPS_TO_NOSTR=${this.pendingConfig.pushBackupsToNostr ? 'true' : 'false'}`
|
||||||
|
}
|
||||||
|
const merged = [...envFileContent, ...toMerge].join('\n')
|
||||||
|
fs.writeFileSync('.env', merged)
|
||||||
|
loadEnvFile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title></title>
|
<title></title>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat" />
|
||||||
rel="stylesheet"
|
|
||||||
href="https://fonts.googleapis.com/css?family=Montserrat"
|
|
||||||
/>
|
|
||||||
<link rel="stylesheet" href="css/styles.css" />
|
<link rel="stylesheet" href="css/styles.css" />
|
||||||
<link rel="stylesheet" href="css/backup.css" />
|
<link rel="stylesheet" href="css/backup.css" />
|
||||||
<!-- HTML Meta Tags -->
|
<!-- HTML Meta Tags -->
|
||||||
|
|
@ -16,14 +14,10 @@
|
||||||
<meta name="description" content="Lightning for Everyone" />
|
<meta name="description" content="Lightning for Everyone" />
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<img
|
<img src="img/pub_logo.png" width="38px" height="auto" alt="Lightning Pub logo" />
|
||||||
src="img/pub_logo.png"
|
|
||||||
width="38px"
|
|
||||||
height="auto"
|
|
||||||
alt="Lightning Pub logo"
|
|
||||||
/>
|
|
||||||
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -44,11 +38,13 @@
|
||||||
<section class="setup-content">
|
<section class="setup-content">
|
||||||
<div class="description-box">
|
<div class="description-box">
|
||||||
<div class="description">
|
<div class="description">
|
||||||
In addition to your seed phrase, you also need channel details to recover funds should your node experience a hardware failure.
|
In addition to your seed phrase, you also need channel details to recover funds should your node experience a
|
||||||
|
hardware failure.
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="description">
|
<div class="description">
|
||||||
It's important always to have the latest version of this file. Fortunately, it's small enough to automatically store on the Nostr relay.
|
It's important always to have the latest version of this file. Fortunately, it's small enough to automatically
|
||||||
|
store on the Nostr relay.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="warning-text">
|
<div class="warning-text">
|
||||||
|
|
@ -73,12 +69,10 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div>
|
||||||
class="push-button hidden-button"
|
<p id="errorText" style="color:red"></p>
|
||||||
onclick="location.href='seed.html'"
|
</div>
|
||||||
style="margin-top: 60px;"
|
<button class="push-button hidden-button" style="margin-top: 60px;" id="next-button">
|
||||||
id="next-button"
|
|
||||||
>
|
|
||||||
Next
|
Next
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -98,5 +92,26 @@
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="js/backup.js"></script>
|
<script src="js/backup.js"></script>
|
||||||
|
<script>
|
||||||
|
document.getElementById("next-button").onclick = (e) => {
|
||||||
|
const backup = document.getElementById('backup').checked
|
||||||
|
const manual = document.getElementById('manual-backup').checked
|
||||||
|
if (!backup && !manual) {
|
||||||
|
document.getElementById('errorText').innerText = 'Please select an option'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (backup && manual) {
|
||||||
|
document.getElementById('errorText').innerText = 'Please select only one option'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (backup) {
|
||||||
|
localStorage.setItem('wizard/backup', 'backup')
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('wizard/backup', 'manual')
|
||||||
|
}
|
||||||
|
location.href = 'seed.html'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,27 +1,21 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title></title>
|
<title></title>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat" />
|
||||||
rel="stylesheet"
|
|
||||||
href="https://fonts.googleapis.com/css?family=Montserrat"
|
|
||||||
/>
|
|
||||||
<link rel="stylesheet" href="css/styles.css" />
|
<link rel="stylesheet" href="css/styles.css" />
|
||||||
<!-- HTML Meta Tags -->
|
<!-- HTML Meta Tags -->
|
||||||
<title>Lightning.Pub</title>
|
<title>Lightning.Pub</title>
|
||||||
<meta name="description" content="Lightning for Everyone" />
|
<meta name="description" content="Lightning for Everyone" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<img
|
<img src="img/pub_logo.png" width="38px" height="auto" alt="Lightning Pub logo" />
|
||||||
src="img/pub_logo.png"
|
|
||||||
width="38px"
|
|
||||||
height="auto"
|
|
||||||
alt="Lightning Pub logo"
|
|
||||||
/>
|
|
||||||
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -37,21 +31,12 @@
|
||||||
<section class="setup-content">
|
<section class="setup-content">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span>Give this node a name that wallet users will see:</span>
|
<span>Give this node a name that wallet users will see:</span>
|
||||||
<input
|
<input type="text" placeholder="Nodey McNodeFace" value="" style="width: 100%" id="nodeName" />
|
||||||
type="text"
|
|
||||||
placeholder="Nodey McNodeFace"
|
|
||||||
value=""
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-group" style="margin-top: 38px">
|
<div class="input-group" style="margin-top: 38px">
|
||||||
<span>If you want to use a specific Nostr relay, enter it now:</span>
|
<span>If you want to use a specific Nostr relay, enter it now:</span>
|
||||||
<input
|
<input type="text" placeholder="wss://relay.lightning.pub" style="width: 100%" id="relayUrl" />
|
||||||
type="text"
|
|
||||||
placeholder="wss://relay.lightning.pub"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkbox" style="margin-top: 12px">
|
<div class="checkbox" style="margin-top: 12px">
|
||||||
|
|
@ -63,11 +48,11 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<div>
|
||||||
class="push-button"
|
<p id="errorText" style="color:red"></p>
|
||||||
onclick="location.href='liquidity.html'"
|
</div>
|
||||||
style="margin-top: 60px"
|
|
||||||
>
|
<button class="push-button" style="margin-top: 60px" id="liquidityBtn">
|
||||||
Next
|
Next
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -85,5 +70,42 @@
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
<a href="https://docs.shock.network" class="marked need-help">Need Help?</a>
|
<a href="https://docs.shock.network" class="marked need-help">Need Help?</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
document.getElementById("liquidityBtn").onclick = (e) => {
|
||||||
|
const nodeName = document.getElementById("nodeName").value;
|
||||||
|
const relayUrl = document.getElementById("relayUrl").value;
|
||||||
|
const checked = document.getElementById("customCheckbox").checked;
|
||||||
|
if (!nodeName) {
|
||||||
|
document.getElementById("errorText").innerText = "Please enter a node name";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!checked && !relayUrl) {
|
||||||
|
document.getElementById("errorText").innerText = "Please enter a relay URL or check the default relay box";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localStorage.setItem("wizard/nodeName", nodeName);
|
||||||
|
if (checked) {
|
||||||
|
localStorage.setItem("wizard/relayUrl", "wss://relay.lightning.pub");
|
||||||
|
} else {
|
||||||
|
localStorage.setItem("wizard/relayUrl", relayUrl);
|
||||||
|
}
|
||||||
|
location.href = 'liquidity.html'
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch("/wizard/state").then((res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
res.json().then((data) => {
|
||||||
|
if (data.already_initialized) {
|
||||||
|
location.href = 'status.html'
|
||||||
|
console.log("already init")
|
||||||
|
} else {
|
||||||
|
console.log("ready to initialize")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title></title>
|
<title></title>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat" />
|
||||||
rel="stylesheet"
|
|
||||||
href="https://fonts.googleapis.com/css?family=Montserrat"
|
|
||||||
/>
|
|
||||||
<link rel="stylesheet" href="css/styles.css" />
|
<link rel="stylesheet" href="css/styles.css" />
|
||||||
<link rel="stylesheet" href="css/liquidity.css" />
|
<link rel="stylesheet" href="css/liquidity.css" />
|
||||||
<!-- HTML Meta Tags -->
|
<!-- HTML Meta Tags -->
|
||||||
|
|
@ -16,14 +14,10 @@
|
||||||
<meta name="description" content="Lightning for Everyone" />
|
<meta name="description" content="Lightning for Everyone" />
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<img
|
<img src="img/pub_logo.png" width="38px" height="auto" alt="Lightning Pub logo" />
|
||||||
src="img/pub_logo.png"
|
|
||||||
width="38px"
|
|
||||||
height="auto"
|
|
||||||
alt="Lightning Pub logo"
|
|
||||||
/>
|
|
||||||
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -52,7 +46,8 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="question-content" id="question-content">
|
<div class="question-content" id="question-content">
|
||||||
Automation helps reduce the fees you pay by trusting peers temporarily until your node balance is sufficient to open a balanced Lightning channel.
|
Automation helps reduce the fees you pay by trusting peers temporarily until your node balance is sufficient
|
||||||
|
to open a balanced Lightning channel.
|
||||||
<button class="icon-button close-button" id="close-question">
|
<button class="icon-button close-button" id="close-question">
|
||||||
<img src="img/close.svg" alt="" />
|
<img src="img/close.svg" alt="" />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -65,11 +60,10 @@
|
||||||
<div class="checkbox-shape"></div>
|
<div class="checkbox-shape"></div>
|
||||||
<label for="manual">Manage my channels manually</label>
|
<label for="manual">Manage my channels manually</label>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div>
|
||||||
class="push-button"
|
<p id="errorText" style="color:red"></p>
|
||||||
onclick="location.href='backup.html'"
|
</div>
|
||||||
style="margin-top: 60px"
|
<button class="push-button" style="margin-top: 60px" id="backupBtn">
|
||||||
>
|
|
||||||
Next
|
Next
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -87,7 +81,28 @@
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
<a href="https://docs.shock.network" class="marked need-help">Need Help?</a>
|
<a href="https://docs.shock.network" class="marked need-help">Need Help?</a>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="js/liquidity.js"></script>
|
<script src="js/liquidity.js"></script>
|
||||||
|
<script>
|
||||||
|
document.getElementById("backupBtn").onclick = (e) => {
|
||||||
|
const automate = document.getElementById('automate').checked
|
||||||
|
const manual = document.getElementById('manual').checked
|
||||||
|
if (!automate && !manual) {
|
||||||
|
document.getElementById('errorText').innerText = 'Please select an option'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (automate && manual) {
|
||||||
|
document.getElementById('errorText').innerText = 'Please select only one option'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (automate) {
|
||||||
|
localStorage.setItem('wizard/liquidity', 'automate')
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('wizard/liquidity', 'manual')
|
||||||
|
}
|
||||||
|
location.href = 'backup.html'
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
172
static/seed.html
172
static/seed.html
|
|
@ -1,14 +1,12 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title></title>
|
<title></title>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat" />
|
||||||
rel="stylesheet"
|
|
||||||
href="https://fonts.googleapis.com/css?family=Montserrat"
|
|
||||||
/>
|
|
||||||
<link rel="stylesheet" href="css/styles.css" />
|
<link rel="stylesheet" href="css/styles.css" />
|
||||||
<link rel="stylesheet" href="css/seed.css" />
|
<link rel="stylesheet" href="css/seed.css" />
|
||||||
<!-- HTML Meta Tags -->
|
<!-- HTML Meta Tags -->
|
||||||
|
|
@ -16,14 +14,10 @@
|
||||||
<meta name="description" content="Lightning for Everyone" />
|
<meta name="description" content="Lightning for Everyone" />
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<img
|
<img src="img/pub_logo.png" width="38px" height="auto" alt="Lightning Pub logo" />
|
||||||
src="img/pub_logo.png"
|
|
||||||
width="38px"
|
|
||||||
height="auto"
|
|
||||||
alt="Lightning Pub logo"
|
|
||||||
/>
|
|
||||||
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
@ -42,102 +36,6 @@
|
||||||
|
|
||||||
<section class="setup-content">
|
<section class="setup-content">
|
||||||
<div class="seed-box-container blur-filter" id="seed-box-container">
|
<div class="seed-box-container blur-filter" id="seed-box-container">
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
<div class="seed-box">
|
|
||||||
<span>1</span>
|
|
||||||
<span>albert</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="reveal-button" id="reveal-button">Click To Reveal</button>
|
<button class="reveal-button" id="reveal-button">Click To Reveal</button>
|
||||||
|
|
@ -151,12 +49,7 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button id="next-button" class="push-button hidden-button" style="margin-top: 60px">
|
||||||
id="next-button"
|
|
||||||
class="push-button hidden-button"
|
|
||||||
onclick="location.href='connect.html'"
|
|
||||||
style="margin-top: 60px"
|
|
||||||
>
|
|
||||||
Next
|
Next
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -176,5 +69,60 @@
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="js/seed.js"></script>
|
<script src="js/seed.js"></script>
|
||||||
|
<script>
|
||||||
|
let latestConfirmationId = ""
|
||||||
|
document.getElementById("next-button").onclick = (e) => {
|
||||||
|
if (!latestConfirmationId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fetch("/wizard/confirm", {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
confirmation_id: latestConfirmationId,
|
||||||
|
})
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
location.href = 'connect.html'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fetch("/wizard/config", {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
source_name: localStorage.getItem("wizard/nodeName"),
|
||||||
|
relay_url: localStorage.getItem("wizard/relayUrl"),
|
||||||
|
automate_liquidity: localStorage.getItem("wizard/liquidity") === 'automate',
|
||||||
|
push_backups_to_nostr: localStorage.getItem("wizard/backup") === 'backup',
|
||||||
|
})
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
res.json().then((data) => {
|
||||||
|
if (data.already_initialized) {
|
||||||
|
location.href = 'status.html'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const seedContainer = document.getElementById("seed-box-container")
|
||||||
|
latestConfirmationId = data.confirmation_id
|
||||||
|
console.log(latestConfirmationId)
|
||||||
|
data.seed.forEach((word, index) => {
|
||||||
|
const seedBox = document.createElement('div')
|
||||||
|
seedBox.classList.add('seed-box')
|
||||||
|
seedBox.innerHTML = `
|
||||||
|
<span>${index + 1}</span>
|
||||||
|
<span>${word}</span>
|
||||||
|
`
|
||||||
|
seedContainer.appendChild(seedBox)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
145
static/status.html
Normal file
145
static/status.html
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title></title>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Montserrat" />
|
||||||
|
<link rel="stylesheet" href="css/styles.css" />
|
||||||
|
<link rel="stylesheet" href="css/status.css" />
|
||||||
|
<!-- HTML Meta Tags -->
|
||||||
|
<title>Lightning.Pub</title>
|
||||||
|
<meta name="description" content="Lightning for Everyone" />
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<img src="img/pub_logo.png" width="38px" height="auto" alt="Lightning Pub logo" />
|
||||||
|
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="setup-header">
|
||||||
|
<h2>Node Status</h2>
|
||||||
|
<p class="header-title"></p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="line" style="width: 100%;"></div>
|
||||||
|
|
||||||
|
<section class="node-status">
|
||||||
|
<div>
|
||||||
|
<div class="status-element" style="margin-top: 15px;">
|
||||||
|
<div style="text-align: left;">Public Node Name:</div>
|
||||||
|
<div class="fc-grey editable-content">
|
||||||
|
<div class="show-nodey" style="display: flex; flex-direction: column; display: none;">
|
||||||
|
<input type="text" value="" name="show-nodey" placeholder="Nodey McNodeFace" />
|
||||||
|
<div style="display: flex;justify-content: end;">
|
||||||
|
<button class="small-btn" id="cancel-show-nodey">Cancel</button>
|
||||||
|
<button class="small-btn" id="save-show-nodey">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="show-nodey-text">Nodey McNodeFace</div>
|
||||||
|
<div class="question-box">
|
||||||
|
<button class="icon-button" id="show-nodey">
|
||||||
|
<img src="img/pencil.svg" style="cursor: pointer;" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-element" style="margin-top: 15px;">
|
||||||
|
<div style="text-align: left;">Nostr Relay:</div>
|
||||||
|
<div class="fc-grey editable-content">
|
||||||
|
<div class="show-nostr" style="display: flex; flex-direction: column; display: none;">
|
||||||
|
<input type="text" value="" name="show-nostr" placeholder="wss://relay.lightning.pub" />
|
||||||
|
<div style="display: flex;justify-content: end;">
|
||||||
|
<button class="small-btn" id="cancel-show-nostr">Cancel</button>
|
||||||
|
<button class="small-btn" id="save-show-nostr">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="show-nostr-text">wss://relay.lightning.pub</div>
|
||||||
|
<div class="question-box">
|
||||||
|
<button class="icon-button" id="show-nostr">
|
||||||
|
<img src="img/pencil.svg" style="cursor: pointer;" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-element" style="margin-top: 15px;">
|
||||||
|
<div>Administrator:</div>
|
||||||
|
<div>
|
||||||
|
npub12334556677889990
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; justify-content: end;padding-right: 12px;">
|
||||||
|
<div class="marked" id="show-reset" style="text-decoration: underline; margin-top: 5px;position: relative;">
|
||||||
|
Reset
|
||||||
|
<div class="watchdog-status">
|
||||||
|
<button class="icon-button" id="show-question">
|
||||||
|
<img src="img/question.svg" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="reset-box">
|
||||||
|
<div style="width: 100%;height: 100%;position: relative;">
|
||||||
|
<button class="icon-button close-button" id="close-reset-box">
|
||||||
|
<img src="img/close.svg" alt="">
|
||||||
|
</button>
|
||||||
|
<div class="reset-box-content" id="reset-content">
|
||||||
|
</div>
|
||||||
|
<div class="continue-button-container">
|
||||||
|
<div class="continue-button" id="">Continue</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 40px;">
|
||||||
|
<div class="status-element">
|
||||||
|
<div>Relay Status:</div>
|
||||||
|
<div>
|
||||||
|
<span class="green-dot">●</span> Connected
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-element">
|
||||||
|
<div>Lightning Status:</div>
|
||||||
|
<div>
|
||||||
|
<span class="yellow-dot">●</span> Syncing
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-element">
|
||||||
|
<div style="position: relative;">
|
||||||
|
Watchdog Status:
|
||||||
|
<div class="watchdog-status">
|
||||||
|
<button class="icon-button" id="show-question">
|
||||||
|
<img src="img/question.svg" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="green-dot">●</span> No Alarms
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 20px;">
|
||||||
|
<div style="font-size: 13px; text-align: left;">Guest Invitation Link:</div>
|
||||||
|
<a href="https://my.shockwallet.app/invite/nprofile12345678899988" target="_blank" style="font-size: 11px;"
|
||||||
|
class="invite-link">
|
||||||
|
https://my.shockwallet.app/invite/nprofile12345678899988
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="footer-text" style="width: 80%">
|
||||||
|
<div class="line"></div>
|
||||||
|
<a href="https://docs.shock.network" class="marked need-help">Need Help?</a>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="js/status.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title></title>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://fonts.googleapis.com/css?family=Montserrat"
|
|
||||||
/>
|
|
||||||
<link rel="stylesheet" href="css/styles.css" />
|
|
||||||
<link rel="stylesheet" href="css/status.css" />
|
|
||||||
<!-- HTML Meta Tags -->
|
|
||||||
<title>Lightning.Pub</title>
|
|
||||||
<meta name="description" content="Lightning for Everyone" />
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<img
|
|
||||||
src="img/pub_logo.png"
|
|
||||||
width="38px"
|
|
||||||
height="auto"
|
|
||||||
alt="Lightning Pub logo"
|
|
||||||
/>
|
|
||||||
<img src="img/LightningPub.png" height="33px" alt="Lightning Pub logo" />
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<section class="setup-header">
|
|
||||||
<h2>Node Status</h2>
|
|
||||||
<p class="header-title"></p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="line" style="width: 100%;"></div>
|
|
||||||
|
|
||||||
<section class="node-status">
|
|
||||||
<div>
|
|
||||||
<div class="status-element" style="margin-top: 15px;">
|
|
||||||
<div style="text-align: left;">Public Node Name:</div>
|
|
||||||
<div class="fc-grey editable-content">
|
|
||||||
<div class="show-nodey" style="display: flex; flex-direction: column; display: none;">
|
|
||||||
<input type="text" value="" name="show-nodey" placeholder="Nodey McNodeFace" />
|
|
||||||
<div style="display: flex;justify-content: end;">
|
|
||||||
<button class="small-btn" id="cancel-show-nodey">Cancel</button>
|
|
||||||
<button class="small-btn" id="save-show-nodey">Save</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="show-nodey-text">Nodey McNodeFace</div>
|
|
||||||
<div class="question-box">
|
|
||||||
<button class="icon-button" id="show-nodey">
|
|
||||||
<img src="img/pencil.svg" style="cursor: pointer;" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="status-element" style="margin-top: 15px;">
|
|
||||||
<div style="text-align: left;">Nostr Relay:</div>
|
|
||||||
<div class="fc-grey editable-content">
|
|
||||||
<div class="show-nostr" style="display: flex; flex-direction: column; display: none;">
|
|
||||||
<input type="text" value="" name="show-nostr" placeholder="wss://relay.lightning.pub" />
|
|
||||||
<div style="display: flex;justify-content: end;">
|
|
||||||
<button class="small-btn" id="cancel-show-nostr">Cancel</button>
|
|
||||||
<button class="small-btn" id="save-show-nostr">Save</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="show-nostr-text">wss://relay.lightning.pub</div>
|
|
||||||
<div class="question-box">
|
|
||||||
<button class="icon-button" id="show-nostr">
|
|
||||||
<img src="img/pencil.svg" style="cursor: pointer;" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="status-element" style="margin-top: 15px;">
|
|
||||||
<div>Administrator:</div>
|
|
||||||
<div>
|
|
||||||
npub12334556677889990
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; justify-content: end;padding-right: 12px;">
|
|
||||||
<div class="marked" id="show-reset" style="text-decoration: underline; margin-top: 5px;position: relative;">Reset
|
|
||||||
<div class="watchdog-status">
|
|
||||||
<button class="icon-button" id="show-question">
|
|
||||||
<img src="img/question.svg" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="reset-box">
|
|
||||||
<div style="width: 100%;height: 100%;position: relative;">
|
|
||||||
<button class="icon-button close-button" id="close-reset-box">
|
|
||||||
<img src="img/close.svg" alt="">
|
|
||||||
</button>
|
|
||||||
<div class="reset-box-content" id="reset-content">
|
|
||||||
</div>
|
|
||||||
<div class="continue-button-container">
|
|
||||||
<div class="continue-button" id="">Continue</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 40px;">
|
|
||||||
<div class="status-element">
|
|
||||||
<div>Relay Status:</div>
|
|
||||||
<div>
|
|
||||||
<span class="green-dot">●</span> Connected
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="status-element">
|
|
||||||
<div>Lightning Status:</div>
|
|
||||||
<div>
|
|
||||||
<span class="yellow-dot">●</span> Syncing
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="status-element">
|
|
||||||
<div style="position: relative;">
|
|
||||||
Watchdog Status:
|
|
||||||
<div class="watchdog-status">
|
|
||||||
<button class="icon-button" id="show-question">
|
|
||||||
<img src="img/question.svg" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="green-dot">●</span> No Alarms
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 20px;">
|
|
||||||
<div style="font-size: 13px; text-align: left;">Guest Invitation Link:</div>
|
|
||||||
<a href="https://my.shockwallet.app/invite/nprofile12345678899988" target="_blank" style="font-size: 11px;" class="invite-link">
|
|
||||||
https://my.shockwallet.app/invite/nprofile12345678899988
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<div class="footer-text" style="width: 80%">
|
|
||||||
<div class="line"></div>
|
|
||||||
<a href="https://docs.shock.network" class="marked need-help">Need Help?</a>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script src="js/status.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue