commit
24fa94824a
29 changed files with 1608 additions and 804 deletions
|
|
@ -15,6 +15,7 @@ message ConfigRequest {
|
|||
string relay_url = 2;
|
||||
bool automate_liquidity = 3;
|
||||
bool push_backups_to_nostr = 4;
|
||||
string avatar_url = 5;
|
||||
}
|
||||
message AdminConnectInfoResponse {
|
||||
string nprofile = 1;
|
||||
|
|
@ -37,4 +38,10 @@ message ServiceStateResponse {
|
|||
bool watchdog_ok = 6;
|
||||
string http_url = 7;
|
||||
string nprofile = 8;
|
||||
string source_name = 9;
|
||||
string relay_url = 10;
|
||||
bool automate_liquidity = 11;
|
||||
bool push_backups_to_nostr = 12;
|
||||
string avatar_url = 13;
|
||||
string app_id = 14;
|
||||
}
|
||||
|
|
@ -24,20 +24,6 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
## 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)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- GetAdminConnectInfo
|
||||
- auth type: __Guest__
|
||||
- http method: __get__
|
||||
|
|
@ -52,40 +38,61 @@ The nostr server will send back a message response, and inside the body there wi
|
|||
- This methods has an __empty__ __request__ body
|
||||
- output: [ServiceStateResponse](#ServiceStateResponse)
|
||||
|
||||
- WizardConfig
|
||||
- auth type: __Guest__
|
||||
- http method: __post__
|
||||
- http route: __/wizard/config__
|
||||
- input: [ConfigRequest](#ConfigRequest)
|
||||
- This methods has an __empty__ __response__ body
|
||||
|
||||
- WizardState
|
||||
- auth type: __Guest__
|
||||
- http method: __get__
|
||||
- http route: __/wizard/state__
|
||||
- This methods has an __empty__ __request__ body
|
||||
- output: [StateResponse](#StateResponse)
|
||||
|
||||
# INPUTS AND OUTPUTS
|
||||
|
||||
## Messages
|
||||
### The content of requests and response from the methods
|
||||
|
||||
### StateResponse
|
||||
- __config_sent__: _boolean_
|
||||
- __admin_linked__: _boolean_
|
||||
### AdminConnectInfoResponse
|
||||
- __connect_info__: _[AdminConnectInfoResponse_connect_info](#AdminConnectInfoResponse_connect_info)_
|
||||
- __nprofile__: _string_
|
||||
|
||||
### ConfigRequest
|
||||
- __source_name__: _string_
|
||||
- __relay_url__: _string_
|
||||
- __automate_liquidity__: _boolean_
|
||||
- __avatar_url__: _string_
|
||||
- __push_backups_to_nostr__: _boolean_
|
||||
|
||||
### AdminConnectInfoResponse
|
||||
- __nprofile__: _string_
|
||||
- __connect_info__: _AdminConnectInfoResponse_connect_info_
|
||||
|
||||
### ServiceStateResponse
|
||||
- __http_url__: _string_
|
||||
- __nprofile__: _string_
|
||||
- __provider_name__: _string_
|
||||
- __relays__: ARRAY of: _string_
|
||||
- __admin_npub__: _string_
|
||||
- __relay_connected__: _boolean_
|
||||
- __lnd_state__: _[LndState](#LndState)_
|
||||
- __watchdog_ok__: _boolean_
|
||||
- __relay_url__: _string_
|
||||
- __source_name__: _string_
|
||||
|
||||
### Empty
|
||||
|
||||
### ServiceStateResponse
|
||||
- __admin_npub__: _string_
|
||||
- __app_id__: _string_
|
||||
- __automate_liquidity__: _boolean_
|
||||
- __avatar_url__: _string_
|
||||
- __http_url__: _string_
|
||||
- __lnd_state__: _[LndState](#LndState)_
|
||||
- __nprofile__: _string_
|
||||
- __provider_name__: _string_
|
||||
- __push_backups_to_nostr__: _boolean_
|
||||
- __relay_connected__: _boolean_
|
||||
- __relay_url__: _string_
|
||||
- __relays__: ARRAY of: _string_
|
||||
- __source_name__: _string_
|
||||
- __watchdog_ok__: _boolean_
|
||||
|
||||
### StateResponse
|
||||
- __admin_linked__: _boolean_
|
||||
- __config_sent__: _boolean_
|
||||
## Enums
|
||||
### The enumerators used in the messages
|
||||
|
||||
### LndState
|
||||
- __OFFLINE__
|
||||
- __SYNCING__
|
||||
- __ONLINE__
|
||||
- __SYNCING__
|
||||
|
|
|
|||
|
|
@ -1,61 +1,5 @@
|
|||
([]*main.Method) (len=4 cap=4) {
|
||||
(*main.Method)(0xc00022a280)({
|
||||
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)(0xc00009a9c0)({
|
||||
authType: (*main.supportedAuth)(0xc0003c9aa0)({
|
||||
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)(0xc00022a2d0)({
|
||||
in: (main.MethodMessage) {
|
||||
name: (string) (len=13) "ConfigRequest",
|
||||
hasZeroFields: (bool) false
|
||||
},
|
||||
name: (string) (len=12) "WizardConfig",
|
||||
out: (main.MethodMessage) {
|
||||
name: (string) (len=5) "Empty",
|
||||
hasZeroFields: (bool) true
|
||||
},
|
||||
opts: (*main.methodOptions)(0xc00009ab40)({
|
||||
authType: (*main.supportedAuth)(0xc0003c9b60)({
|
||||
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)(0xc00022a640)({
|
||||
(*main.Method)(0xc0002b00f0)({
|
||||
in: (main.MethodMessage) {
|
||||
name: (string) (len=5) "Empty",
|
||||
hasZeroFields: (bool) true
|
||||
|
|
@ -65,12 +9,11 @@
|
|||
name: (string) (len=24) "AdminConnectInfoResponse",
|
||||
hasZeroFields: (bool) false
|
||||
},
|
||||
opts: (*main.methodOptions)(0xc00009acc0)({
|
||||
authType: (*main.supportedAuth)(0xc0003c9c20)({
|
||||
opts: (*main.methodOptions)(0xc0006907e0)({
|
||||
authType: (*main.supportedAuth)(0xc000347380)({
|
||||
id: (string) (len=5) "guest",
|
||||
name: (string) (len=5) "Guest",
|
||||
context: (map[string]string) {
|
||||
}
|
||||
context: ([]*main.authContext) <nil>
|
||||
}),
|
||||
method: (string) (len=3) "get",
|
||||
route: (main.decodedRoute) {
|
||||
|
|
@ -83,7 +26,7 @@
|
|||
}),
|
||||
serverStream: (bool) false
|
||||
}),
|
||||
(*main.Method)(0xc00022a690)({
|
||||
(*main.Method)(0xc0002b0140)({
|
||||
in: (main.MethodMessage) {
|
||||
name: (string) (len=5) "Empty",
|
||||
hasZeroFields: (bool) true
|
||||
|
|
@ -93,12 +36,11 @@
|
|||
name: (string) (len=20) "ServiceStateResponse",
|
||||
hasZeroFields: (bool) false
|
||||
},
|
||||
opts: (*main.methodOptions)(0xc00009ae40)({
|
||||
authType: (*main.supportedAuth)(0xc0003c9ce0)({
|
||||
opts: (*main.methodOptions)(0xc000690960)({
|
||||
authType: (*main.supportedAuth)(0xc000347440)({
|
||||
id: (string) (len=5) "guest",
|
||||
name: (string) (len=5) "Guest",
|
||||
context: (map[string]string) {
|
||||
}
|
||||
context: ([]*main.authContext) <nil>
|
||||
}),
|
||||
method: (string) (len=3) "get",
|
||||
route: (main.decodedRoute) {
|
||||
|
|
@ -110,68 +52,100 @@
|
|||
batch: (bool) false
|
||||
}),
|
||||
serverStream: (bool) false
|
||||
}),
|
||||
(*main.Method)(0xc0002b00a0)({
|
||||
in: (main.MethodMessage) {
|
||||
name: (string) (len=13) "ConfigRequest",
|
||||
hasZeroFields: (bool) false
|
||||
},
|
||||
name: (string) (len=12) "WizardConfig",
|
||||
out: (main.MethodMessage) {
|
||||
name: (string) (len=5) "Empty",
|
||||
hasZeroFields: (bool) true
|
||||
},
|
||||
opts: (*main.methodOptions)(0xc000690660)({
|
||||
authType: (*main.supportedAuth)(0xc0003472c0)({
|
||||
id: (string) (len=5) "guest",
|
||||
name: (string) (len=5) "Guest",
|
||||
context: ([]*main.authContext) <nil>
|
||||
}),
|
||||
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)(0xc0002b0050)({
|
||||
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)(0xc0006904e0)({
|
||||
authType: (*main.supportedAuth)(0xc000347200)({
|
||||
id: (string) (len=5) "guest",
|
||||
name: (string) (len=5) "Guest",
|
||||
context: ([]*main.authContext) <nil>
|
||||
}),
|
||||
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.Enum) (len=1 cap=1) {
|
||||
(*main.Enum)(0xc0003c9680)({
|
||||
(*main.Enum)(0xc000443d40)({
|
||||
name: (string) (len=8) "LndState",
|
||||
values: ([]main.EnumValue) (len=3 cap=4) {
|
||||
(main.EnumValue) {
|
||||
number: (int64) 0,
|
||||
name: (string) (len=7) "OFFLINE"
|
||||
},
|
||||
(main.EnumValue) {
|
||||
number: (int64) 1,
|
||||
name: (string) (len=7) "SYNCING"
|
||||
},
|
||||
(main.EnumValue) {
|
||||
number: (int64) 2,
|
||||
name: (string) (len=6) "ONLINE"
|
||||
},
|
||||
(main.EnumValue) {
|
||||
number: (int64) 1,
|
||||
name: (string) (len=7) "SYNCING"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
(map[string]*main.Message) (len=5) {
|
||||
(string) (len=5) "Empty": (*main.Message)(0xc0003c94a0)({
|
||||
fullName: (string) (len=5) "Empty",
|
||||
name: (string) (len=5) "Empty",
|
||||
fields: (map[string]*main.Field) {
|
||||
}
|
||||
}),
|
||||
(string) (len=13) "StateResponse": (*main.Message)(0xc0003c9500)({
|
||||
fullName: (string) (len=13) "StateResponse",
|
||||
name: (string) (len=13) "StateResponse",
|
||||
fields: (map[string]*main.Field) (len=2) {
|
||||
(string) (len=11) "config_sent": (*main.Field)(0xc0003ee440)({
|
||||
name: (string) (len=11) "config_sent",
|
||||
kind: (string) (len=4) "bool",
|
||||
([]*main.SortableMessage) (len=5 cap=8) {
|
||||
(*main.SortableMessage)(0xc0003475c0)({
|
||||
fullName: (string) (len=24) "AdminConnectInfoResponse",
|
||||
name: (string) (len=24) "AdminConnectInfoResponse",
|
||||
fields: ([]*main.Field) (len=2 cap=2) {
|
||||
(*main.Field)(0xc000347480)({
|
||||
name: (string) (len=12) "connect_info",
|
||||
kind: (string) (len=37) "AdminConnectInfoResponse_connect_info",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isMessage: (bool) true,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
oneOfName: (string) (len=12) "connect_info"
|
||||
}),
|
||||
(string) (len=12) "admin_linked": (*main.Field)(0xc0003ee480)({
|
||||
name: (string) (len=12) "admin_linked",
|
||||
kind: (string) (len=4) "bool",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
})
|
||||
}
|
||||
}),
|
||||
(string) (len=13) "ConfigRequest": (*main.Message)(0xc0003c9560)({
|
||||
fullName: (string) (len=13) "ConfigRequest",
|
||||
name: (string) (len=13) "ConfigRequest",
|
||||
fields: (map[string]*main.Field) (len=4) {
|
||||
(string) (len=9) "relay_url": (*main.Field)(0xc0003ee500)({
|
||||
name: (string) (len=9) "relay_url",
|
||||
(*main.Field)(0xc000346bc0)({
|
||||
name: (string) (len=8) "nprofile",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
|
|
@ -179,8 +153,14 @@
|
|||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
})
|
||||
}
|
||||
}),
|
||||
(string) (len=18) "automate_liquidity": (*main.Field)(0xc0003ee540)({
|
||||
(*main.SortableMessage)(0xc000347580)({
|
||||
fullName: (string) (len=13) "ConfigRequest",
|
||||
name: (string) (len=13) "ConfigRequest",
|
||||
fields: ([]*main.Field) (len=5 cap=8) {
|
||||
(*main.Field)(0xc000346b00)({
|
||||
name: (string) (len=18) "automate_liquidity",
|
||||
kind: (string) (len=4) "bool",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -190,7 +170,17 @@
|
|||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(string) (len=21) "push_backups_to_nostr": (*main.Field)(0xc0003ee580)({
|
||||
(*main.Field)(0xc000346b80)({
|
||||
name: (string) (len=10) "avatar_url",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346b40)({
|
||||
name: (string) (len=21) "push_backups_to_nostr",
|
||||
kind: (string) (len=4) "bool",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -200,7 +190,17 @@
|
|||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(string) (len=11) "source_name": (*main.Field)(0xc0003ee4c0)({
|
||||
(*main.Field)(0xc000346ac0)({
|
||||
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,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346a80)({
|
||||
name: (string) (len=11) "source_name",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -212,37 +212,16 @@
|
|||
})
|
||||
}
|
||||
}),
|
||||
(string) (len=24) "AdminConnectInfoResponse": (*main.Message)(0xc0003c95c0)({
|
||||
fullName: (string) (len=24) "AdminConnectInfoResponse",
|
||||
name: (string) (len=24) "AdminConnectInfoResponse",
|
||||
fields: (map[string]*main.Field) (len=2) {
|
||||
(string) (len=8) "nprofile": (*main.Field)(0xc0003ee5c0)({
|
||||
name: (string) (len=8) "nprofile",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
(*main.SortableMessage)(0xc0003474c0)({
|
||||
fullName: (string) (len=5) "Empty",
|
||||
name: (string) (len=5) "Empty",
|
||||
fields: ([]*main.Field) <nil>
|
||||
}),
|
||||
(string) (len=37) "AdminConnectInfoResponse_connect_info": (*main.Field)(0xc0003eea80)({
|
||||
name: (string) (len=12) "connect_info",
|
||||
kind: (string) (len=37) "AdminConnectInfoResponse_connect_info",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) (len=12) "connect_info"
|
||||
})
|
||||
}
|
||||
}),
|
||||
(string) (len=20) "ServiceStateResponse": (*main.Message)(0xc0003c9620)({
|
||||
(*main.SortableMessage)(0xc000347640)({
|
||||
fullName: (string) (len=20) "ServiceStateResponse",
|
||||
name: (string) (len=20) "ServiceStateResponse",
|
||||
fields: (map[string]*main.Field) (len=8) {
|
||||
(string) (len=10) "admin_npub": (*main.Field)(0xc0003ee700)({
|
||||
fields: ([]*main.Field) (len=14 cap=16) {
|
||||
(*main.Field)(0xc000346d00)({
|
||||
name: (string) (len=10) "admin_npub",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -252,8 +231,18 @@
|
|||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(string) (len=15) "relay_connected": (*main.Field)(0xc0003ee740)({
|
||||
name: (string) (len=15) "relay_connected",
|
||||
(*main.Field)(0xc000346fc0)({
|
||||
name: (string) (len=6) "app_id",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346f00)({
|
||||
name: (string) (len=18) "automate_liquidity",
|
||||
kind: (string) (len=4) "bool",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
|
|
@ -262,19 +251,9 @@
|
|||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(string) (len=9) "lnd_state": (*main.Field)(0xc0003ee780)({
|
||||
name: (string) (len=9) "lnd_state",
|
||||
kind: (string) (len=8) "LndState",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) true,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(string) (len=11) "watchdog_ok": (*main.Field)(0xc0003ee7c0)({
|
||||
name: (string) (len=11) "watchdog_ok",
|
||||
kind: (string) (len=4) "bool",
|
||||
(*main.Field)(0xc000346f80)({
|
||||
name: (string) (len=10) "avatar_url",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
|
|
@ -282,7 +261,7 @@
|
|||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(string) (len=8) "http_url": (*main.Field)(0xc0003ee800)({
|
||||
(*main.Field)(0xc000346e00)({
|
||||
name: (string) (len=8) "http_url",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -292,7 +271,17 @@
|
|||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(string) (len=8) "nprofile": (*main.Field)(0xc0003ee840)({
|
||||
(*main.Field)(0xc000346d80)({
|
||||
name: (string) (len=9) "lnd_state",
|
||||
kind: (string) (len=8) "LndState",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) true,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346e40)({
|
||||
name: (string) (len=8) "nprofile",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -302,7 +291,7 @@
|
|||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(string) (len=13) "provider_name": (*main.Field)(0xc0003ee680)({
|
||||
(*main.Field)(0xc000346c80)({
|
||||
name: (string) (len=13) "provider_name",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -312,7 +301,37 @@
|
|||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(string) (len=6) "relays": (*main.Field)(0xc0003ee6c0)({
|
||||
(*main.Field)(0xc000346f40)({
|
||||
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,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346d40)({
|
||||
name: (string) (len=15) "relay_connected",
|
||||
kind: (string) (len=4) "bool",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346ec0)({
|
||||
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,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346cc0)({
|
||||
name: (string) (len=6) "relays",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -321,14 +340,62 @@
|
|||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346e80)({
|
||||
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,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346dc0)({
|
||||
name: (string) (len=11) "watchdog_ok",
|
||||
kind: (string) (len=4) "bool",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
})
|
||||
}
|
||||
}),
|
||||
(*main.SortableMessage)(0xc000347500)({
|
||||
fullName: (string) (len=13) "StateResponse",
|
||||
name: (string) (len=13) "StateResponse",
|
||||
fields: ([]*main.Field) (len=2 cap=2) {
|
||||
(*main.Field)(0xc000346a40)({
|
||||
name: (string) (len=12) "admin_linked",
|
||||
kind: (string) (len=4) "bool",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
}),
|
||||
(*main.Field)(0xc000346a00)({
|
||||
name: (string) (len=11) "config_sent",
|
||||
kind: (string) (len=4) "bool",
|
||||
isMap: (bool) false,
|
||||
isArray: (bool) false,
|
||||
isEnum: (bool) false,
|
||||
isMessage: (bool) false,
|
||||
isOptional: (bool) false,
|
||||
oneOfName: (string) ""
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
(map[string][]*main.Field) (len=1) {
|
||||
(string) (len=37) "AdminConnectInfoResponse_connect_info": ([]*main.Field) (len=2 cap=2) {
|
||||
(*main.Field)(0xc0003ee600)({
|
||||
([]*main.OneOf) (len=1 cap=1) {
|
||||
(*main.OneOf)(0xc0000e31a0)({
|
||||
kind: (string) (len=37) "AdminConnectInfoResponse_connect_info",
|
||||
fields: ([]*main.Field) (len=2 cap=2) {
|
||||
(*main.Field)(0xc000346c00)({
|
||||
name: (string) (len=11) "admin_token",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -338,7 +405,7 @@
|
|||
isOptional: (bool) false,
|
||||
oneOfName: (string) (len=12) "connect_info"
|
||||
}),
|
||||
(*main.Field)(0xc0003ee640)({
|
||||
(*main.Field)(0xc000346c40)({
|
||||
name: (string) (len=13) "enrolled_npub",
|
||||
kind: (string) (len=6) "string",
|
||||
isMap: (bool) false,
|
||||
|
|
@ -349,11 +416,12 @@
|
|||
oneOfName: (string) (len=12) "connect_info"
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
parsing file: wizard_structs 5
|
||||
parsing file: wizard_methods 2
|
||||
-> [{guest Guest map[]}]
|
||||
-> [{guest Guest []}]
|
||||
|
||||
([]interface {}) <nil>
|
||||
|
||||
|
|
|
|||
151
proto/wizard_service/autogenerated/go/http_client.go
Normal file
151
proto/wizard_service/autogenerated/go/http_client.go
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
// This file was autogenerated from a .proto file, DO NOT EDIT!
|
||||
|
||||
package lightning_pub
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func doPostRequest(url string, body []byte, auth string) ([]byte, error) {
|
||||
bodyReader := bytes.NewReader(body)
|
||||
req, err := http.NewRequest(http.MethodPost, url, bodyReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("authorization", auth)
|
||||
return doRequest(req)
|
||||
}
|
||||
|
||||
func doGetRequest(url string, auth string) ([]byte, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("authorization", auth)
|
||||
return doRequest(req)
|
||||
}
|
||||
|
||||
func doRequest(req *http.Request) ([]byte, error) {
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resBody, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resBody, nil
|
||||
}
|
||||
|
||||
type ClientParams struct {
|
||||
BaseURL string
|
||||
RetrieveGuestAuth func() (string, error)
|
||||
}
|
||||
type Client struct {
|
||||
GetAdminConnectInfo func() (*AdminConnectInfoResponse, error)
|
||||
GetServiceState func() (*ServiceStateResponse, error)
|
||||
WizardConfig func(req ConfigRequest) error
|
||||
WizardState func() (*StateResponse, error)
|
||||
}
|
||||
|
||||
func NewClient(params ClientParams) *Client {
|
||||
return &Client{
|
||||
GetAdminConnectInfo: func() (*AdminConnectInfoResponse, error) {
|
||||
auth, err := params.RetrieveGuestAuth()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalRoute := "/wizard/admin_connect_info"
|
||||
resBody, err := doGetRequest(params.BaseURL+finalRoute, auth)
|
||||
result := ResultError{}
|
||||
err = json.Unmarshal(resBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Status == "ERROR" {
|
||||
return nil, fmt.Errorf(result.Reason)
|
||||
}
|
||||
res := AdminConnectInfoResponse{}
|
||||
err = json.Unmarshal(resBody, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
},
|
||||
GetServiceState: func() (*ServiceStateResponse, error) {
|
||||
auth, err := params.RetrieveGuestAuth()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalRoute := "/wizard/service_state"
|
||||
resBody, err := doGetRequest(params.BaseURL+finalRoute, auth)
|
||||
result := ResultError{}
|
||||
err = json.Unmarshal(resBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Status == "ERROR" {
|
||||
return nil, fmt.Errorf(result.Reason)
|
||||
}
|
||||
res := ServiceStateResponse{}
|
||||
err = json.Unmarshal(resBody, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
},
|
||||
WizardConfig: func(req ConfigRequest) error {
|
||||
auth, err := params.RetrieveGuestAuth()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
finalRoute := "/wizard/config"
|
||||
body, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resBody, err := doPostRequest(params.BaseURL+finalRoute, body, auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result := ResultError{}
|
||||
err = json.Unmarshal(resBody, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.Status == "ERROR" {
|
||||
return fmt.Errorf(result.Reason)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
WizardState: func() (*StateResponse, error) {
|
||||
auth, err := params.RetrieveGuestAuth()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finalRoute := "/wizard/state"
|
||||
resBody, err := doGetRequest(params.BaseURL+finalRoute, auth)
|
||||
result := ResultError{}
|
||||
err = json.Unmarshal(resBody, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Status == "ERROR" {
|
||||
return nil, fmt.Errorf(result.Reason)
|
||||
}
|
||||
res := StateResponse{}
|
||||
err = json.Unmarshal(resBody, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
63
proto/wizard_service/autogenerated/go/types.go
Normal file
63
proto/wizard_service/autogenerated/go/types.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// This file was autogenerated from a .proto file, DO NOT EDIT!
|
||||
|
||||
package lightning_pub
|
||||
|
||||
type ResultError struct {
|
||||
Status string `json:"status"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
type GuestContext struct {
|
||||
}
|
||||
type LndState string
|
||||
|
||||
const (
|
||||
OFFLINE LndState = "OFFLINE"
|
||||
ONLINE LndState = "ONLINE"
|
||||
SYNCING LndState = "SYNCING"
|
||||
)
|
||||
|
||||
type AdminConnectInfoResponse struct {
|
||||
Connect_info *AdminConnectInfoResponse_connect_info `json:"connect_info"`
|
||||
Nprofile string `json:"nprofile"`
|
||||
}
|
||||
type ConfigRequest struct {
|
||||
Automate_liquidity bool `json:"automate_liquidity"`
|
||||
Avatar_url string `json:"avatar_url"`
|
||||
Push_backups_to_nostr bool `json:"push_backups_to_nostr"`
|
||||
Relay_url string `json:"relay_url"`
|
||||
Source_name string `json:"source_name"`
|
||||
}
|
||||
type Empty struct {
|
||||
}
|
||||
type ServiceStateResponse struct {
|
||||
Admin_npub string `json:"admin_npub"`
|
||||
App_id string `json:"app_id"`
|
||||
Automate_liquidity bool `json:"automate_liquidity"`
|
||||
Avatar_url string `json:"avatar_url"`
|
||||
Http_url string `json:"http_url"`
|
||||
Lnd_state LndState `json:"lnd_state"`
|
||||
Nprofile string `json:"nprofile"`
|
||||
Provider_name string `json:"provider_name"`
|
||||
Push_backups_to_nostr bool `json:"push_backups_to_nostr"`
|
||||
Relay_connected bool `json:"relay_connected"`
|
||||
Relay_url string `json:"relay_url"`
|
||||
Relays []string `json:"relays"`
|
||||
Source_name string `json:"source_name"`
|
||||
Watchdog_ok bool `json:"watchdog_ok"`
|
||||
}
|
||||
type StateResponse struct {
|
||||
Admin_linked bool `json:"admin_linked"`
|
||||
Config_sent bool `json:"config_sent"`
|
||||
}
|
||||
type AdminConnectInfoResponse_connect_info_type string
|
||||
|
||||
const (
|
||||
ADMIN_TOKEN AdminConnectInfoResponse_connect_info_type = "admin_token"
|
||||
ENROLLED_NPUB AdminConnectInfoResponse_connect_info_type = "enrolled_npub"
|
||||
)
|
||||
|
||||
type AdminConnectInfoResponse_connect_info struct {
|
||||
Type AdminConnectInfoResponse_connect_info_type `json:"type"`
|
||||
Admin_token *string `json:"admin_token"`
|
||||
Enrolled_npub *string `json:"enrolled_npub"`
|
||||
}
|
||||
|
|
@ -29,47 +29,6 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
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
|
||||
await methods.WizardConfig({rpcName:'WizardConfig', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK'})
|
||||
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.GetAdminConnectInfo) throw new Error('method: GetAdminConnectInfo is not implemented')
|
||||
app.get('/wizard/admin_connect_info', async (req, res) => {
|
||||
const info: Types.RequestInfo = { rpcName: 'GetAdminConnectInfo', batch: false, nostr: false, batchSize: 0}
|
||||
|
|
@ -108,6 +67,47 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
|
|||
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
|
||||
await methods.WizardConfig({rpcName:'WizardConfig', ctx:authContext , req: request})
|
||||
stats.handle = process.hrtime.bigint()
|
||||
res.json({status: 'OK'})
|
||||
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.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.staticFiles) {
|
||||
app.use(express.static(opts.staticFiles))
|
||||
app.get('*', function (_, res) { res.sendFile('index.html', { root: opts.staticFiles })})
|
||||
|
|
|
|||
|
|
@ -12,31 +12,6 @@ export type ClientParams = {
|
|||
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' })> => {
|
||||
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') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
GetAdminConnectInfo: async (): Promise<ResultError | ({ status: 'OK' }& Types.AdminConnectInfoResponse)> => {
|
||||
const auth = await params.retrieveGuestAuth()
|
||||
if (auth === null) throw new Error('retrieveGuestAuth() returned null')
|
||||
|
|
@ -65,4 +40,29 @@ export default (params: ClientParams) => ({
|
|||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
WizardConfig: async (request: Types.ConfigRequest): Promise<ResultError | ({ status: 'OK' })> => {
|
||||
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') {
|
||||
return data
|
||||
}
|
||||
return { status: 'ERROR', reason: 'invalid response' }
|
||||
},
|
||||
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' }
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,33 +6,33 @@ export type RequestStats = { startMs:number, start:bigint, parse: bigint, guard:
|
|||
export type RequestMetric = AuthContext & RequestInfo & RequestStats & { error?: string }
|
||||
export type GuestContext = {
|
||||
}
|
||||
export type GuestMethodInputs = WizardState_Input | WizardConfig_Input | GetAdminConnectInfo_Input | GetServiceState_Input
|
||||
export type GuestMethodOutputs = WizardState_Output | WizardConfig_Output | GetAdminConnectInfo_Output | GetServiceState_Output
|
||||
export type GuestMethodInputs = GetAdminConnectInfo_Input | GetServiceState_Input | WizardConfig_Input | WizardState_Input
|
||||
export type GuestMethodOutputs = GetAdminConnectInfo_Output | GetServiceState_Output | WizardConfig_Output | WizardState_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' }
|
||||
|
||||
export type GetAdminConnectInfo_Input = {rpcName:'GetAdminConnectInfo'}
|
||||
export type GetAdminConnectInfo_Output = ResultError | ({ status: 'OK' } & AdminConnectInfoResponse)
|
||||
|
||||
export type GetServiceState_Input = {rpcName:'GetServiceState'}
|
||||
export type GetServiceState_Output = ResultError | ({ status: 'OK' } & ServiceStateResponse)
|
||||
|
||||
export type WizardConfig_Input = {rpcName:'WizardConfig', req: ConfigRequest}
|
||||
export type WizardConfig_Output = ResultError | { status: 'OK' }
|
||||
|
||||
export type WizardState_Input = {rpcName:'WizardState'}
|
||||
export type WizardState_Output = ResultError | ({ status: 'OK' } & StateResponse)
|
||||
|
||||
export type ServerMethods = {
|
||||
WizardState?: (req: WizardState_Input & {ctx: GuestContext }) => Promise<StateResponse>
|
||||
WizardConfig?: (req: WizardConfig_Input & {ctx: GuestContext }) => Promise<void>
|
||||
GetAdminConnectInfo?: (req: GetAdminConnectInfo_Input & {ctx: GuestContext }) => Promise<AdminConnectInfoResponse>
|
||||
GetServiceState?: (req: GetServiceState_Input & {ctx: GuestContext }) => Promise<ServiceStateResponse>
|
||||
WizardConfig?: (req: WizardConfig_Input & {ctx: GuestContext }) => Promise<void>
|
||||
WizardState?: (req: WizardState_Input & {ctx: GuestContext }) => Promise<StateResponse>
|
||||
}
|
||||
|
||||
export enum LndState {
|
||||
OFFLINE = 'OFFLINE',
|
||||
SYNCING = 'SYNCING',
|
||||
ONLINE = 'ONLINE',
|
||||
SYNCING = 'SYNCING',
|
||||
}
|
||||
export const enumCheckLndState = (e?: LndState): boolean => {
|
||||
for (const v in LndState) if (e === v) return true
|
||||
|
|
@ -43,6 +43,68 @@ export type OptionsBaseMessage = {
|
|||
allOptionalsAreSet?: true
|
||||
}
|
||||
|
||||
export type AdminConnectInfoResponse = {
|
||||
connect_info: AdminConnectInfoResponse_connect_info
|
||||
nprofile: string
|
||||
}
|
||||
export const AdminConnectInfoResponseOptionalFields: [] = []
|
||||
export type AdminConnectInfoResponseOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
connect_info_Options?: AdminConnectInfoResponse_connect_infoOptions
|
||||
nprofile_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
export const AdminConnectInfoResponseValidate = (o?: AdminConnectInfoResponse, opts: AdminConnectInfoResponseOptions = {}, path: string = 'AdminConnectInfoResponse::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')
|
||||
|
||||
const connect_infoErr = AdminConnectInfoResponse_connect_infoValidate(o.connect_info, opts.connect_info_Options, `${path}.connect_info`)
|
||||
if (connect_infoErr !== null) return connect_infoErr
|
||||
|
||||
|
||||
if (typeof o.nprofile !== 'string') return new Error(`${path}.nprofile: is not a string`)
|
||||
if (opts.nprofile_CustomCheck && !opts.nprofile_CustomCheck(o.nprofile)) return new Error(`${path}.nprofile: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type ConfigRequest = {
|
||||
automate_liquidity: boolean
|
||||
avatar_url: string
|
||||
push_backups_to_nostr: boolean
|
||||
relay_url: string
|
||||
source_name: string
|
||||
}
|
||||
export const ConfigRequestOptionalFields: [] = []
|
||||
export type ConfigRequestOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
automate_liquidity_CustomCheck?: (v: boolean) => boolean
|
||||
avatar_url_CustomCheck?: (v: string) => boolean
|
||||
push_backups_to_nostr_CustomCheck?: (v: boolean) => boolean
|
||||
relay_url_CustomCheck?: (v: string) => boolean
|
||||
source_name_CustomCheck?: (v: string) => 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.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.avatar_url !== 'string') return new Error(`${path}.avatar_url: is not a string`)
|
||||
if (opts.avatar_url_CustomCheck && !opts.avatar_url_CustomCheck(o.avatar_url)) return new Error(`${path}.avatar_url: 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`)
|
||||
|
||||
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.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`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type Empty = {
|
||||
}
|
||||
export const EmptyOptionalFields: [] = []
|
||||
|
|
@ -56,138 +118,111 @@ export const EmptyValidate = (o?: Empty, opts: EmptyOptions = {}, path: string =
|
|||
return null
|
||||
}
|
||||
|
||||
export type StateResponse = {
|
||||
config_sent: boolean
|
||||
admin_linked: boolean
|
||||
}
|
||||
export const StateResponseOptionalFields: [] = []
|
||||
export type StateResponseOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
config_sent_CustomCheck?: (v: boolean) => boolean
|
||||
admin_linked_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.config_sent !== 'boolean') return new Error(`${path}.config_sent: is not a boolean`)
|
||||
if (opts.config_sent_CustomCheck && !opts.config_sent_CustomCheck(o.config_sent)) return new Error(`${path}.config_sent: custom check failed`)
|
||||
|
||||
if (typeof o.admin_linked !== 'boolean') return new Error(`${path}.admin_linked: is not a boolean`)
|
||||
if (opts.admin_linked_CustomCheck && !opts.admin_linked_CustomCheck(o.admin_linked)) return new Error(`${path}.admin_linked: 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?: []
|
||||
push_backups_to_nostr_CustomCheck?: (v: boolean) => boolean
|
||||
source_name_CustomCheck?: (v: string) => boolean
|
||||
relay_url_CustomCheck?: (v: string) => boolean
|
||||
automate_liquidity_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
|
||||
}
|
||||
|
||||
export type AdminConnectInfoResponse = {
|
||||
nprofile: string
|
||||
connect_info: AdminConnectInfoResponse_connect_info
|
||||
}
|
||||
export const AdminConnectInfoResponseOptionalFields: [] = []
|
||||
export type AdminConnectInfoResponseOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
nprofile_CustomCheck?: (v: string) => boolean
|
||||
connect_info_CustomCheck?: (v: AdminConnectInfoResponse_connect_info) => boolean
|
||||
}
|
||||
export const AdminConnectInfoResponseValidate = (o?: AdminConnectInfoResponse, opts: AdminConnectInfoResponseOptions = {}, path: string = 'AdminConnectInfoResponse::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.nprofile !== 'string') return new Error(`${path}.nprofile: is not a string`)
|
||||
if (opts.nprofile_CustomCheck && !opts.nprofile_CustomCheck(o.nprofile)) return new Error(`${path}.nprofile: custom check failed`)
|
||||
|
||||
const connect_infoErr = AdminConnectInfoResponse_connect_infoValidate(o.connect_info,{}, `${path}.connect_info`)
|
||||
if (connect_infoErr !== null) return connect_infoErr
|
||||
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export type ServiceStateResponse = {
|
||||
admin_npub: string
|
||||
app_id: string
|
||||
automate_liquidity: boolean
|
||||
avatar_url: string
|
||||
http_url: string
|
||||
lnd_state: LndState
|
||||
nprofile: string
|
||||
provider_name: string
|
||||
relays: string[]
|
||||
admin_npub: string
|
||||
push_backups_to_nostr: boolean
|
||||
relay_connected: boolean
|
||||
lnd_state: LndState
|
||||
relay_url: string
|
||||
relays: string[]
|
||||
source_name: string
|
||||
watchdog_ok: boolean
|
||||
}
|
||||
export const ServiceStateResponseOptionalFields: [] = []
|
||||
export type ServiceStateResponseOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
admin_npub_CustomCheck?: (v: string) => boolean
|
||||
app_id_CustomCheck?: (v: string) => boolean
|
||||
automate_liquidity_CustomCheck?: (v: boolean) => boolean
|
||||
avatar_url_CustomCheck?: (v: string) => boolean
|
||||
http_url_CustomCheck?: (v: string) => boolean
|
||||
lnd_state_CustomCheck?: (v: LndState) => boolean
|
||||
nprofile_CustomCheck?: (v: string) => boolean
|
||||
provider_name_CustomCheck?: (v: string) => boolean
|
||||
relays_CustomCheck?: (v: string[]) => boolean
|
||||
admin_npub_CustomCheck?: (v: string) => boolean
|
||||
push_backups_to_nostr_CustomCheck?: (v: boolean) => boolean
|
||||
relay_connected_CustomCheck?: (v: boolean) => boolean
|
||||
lnd_state_CustomCheck?: (v: LndState) => boolean
|
||||
relay_url_CustomCheck?: (v: string) => boolean
|
||||
relays_CustomCheck?: (v: string[]) => boolean
|
||||
source_name_CustomCheck?: (v: string) => boolean
|
||||
watchdog_ok_CustomCheck?: (v: boolean) => boolean
|
||||
}
|
||||
export const ServiceStateResponseValidate = (o?: ServiceStateResponse, opts: ServiceStateResponseOptions = {}, path: string = 'ServiceStateResponse::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_npub !== 'string') return new Error(`${path}.admin_npub: is not a string`)
|
||||
if (opts.admin_npub_CustomCheck && !opts.admin_npub_CustomCheck(o.admin_npub)) return new Error(`${path}.admin_npub: custom check failed`)
|
||||
|
||||
if (typeof o.app_id !== 'string') return new Error(`${path}.app_id: is not a string`)
|
||||
if (opts.app_id_CustomCheck && !opts.app_id_CustomCheck(o.app_id)) return new Error(`${path}.app_id: 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.avatar_url !== 'string') return new Error(`${path}.avatar_url: is not a string`)
|
||||
if (opts.avatar_url_CustomCheck && !opts.avatar_url_CustomCheck(o.avatar_url)) return new Error(`${path}.avatar_url: custom check failed`)
|
||||
|
||||
if (typeof o.http_url !== 'string') return new Error(`${path}.http_url: is not a string`)
|
||||
if (opts.http_url_CustomCheck && !opts.http_url_CustomCheck(o.http_url)) return new Error(`${path}.http_url: custom check failed`)
|
||||
|
||||
if (!enumCheckLndState(o.lnd_state)) return new Error(`${path}.lnd_state: is not a valid LndState`)
|
||||
if (opts.lnd_state_CustomCheck && !opts.lnd_state_CustomCheck(o.lnd_state)) return new Error(`${path}.lnd_state: custom check failed`)
|
||||
|
||||
if (typeof o.nprofile !== 'string') return new Error(`${path}.nprofile: is not a string`)
|
||||
if (opts.nprofile_CustomCheck && !opts.nprofile_CustomCheck(o.nprofile)) return new Error(`${path}.nprofile: custom check failed`)
|
||||
|
||||
if (typeof o.provider_name !== 'string') return new Error(`${path}.provider_name: is not a string`)
|
||||
if (opts.provider_name_CustomCheck && !opts.provider_name_CustomCheck(o.provider_name)) return new Error(`${path}.provider_name: 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`)
|
||||
|
||||
if (typeof o.relay_connected !== 'boolean') return new Error(`${path}.relay_connected: is not a boolean`)
|
||||
if (opts.relay_connected_CustomCheck && !opts.relay_connected_CustomCheck(o.relay_connected)) return new Error(`${path}.relay_connected: 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 (!Array.isArray(o.relays)) return new Error(`${path}.relays: is not an array`)
|
||||
for (let index = 0; index < o.relays.length; index++) {
|
||||
if (typeof o.relays[index] !== 'string') return new Error(`${path}.relays[${index}]: is not a string`)
|
||||
}
|
||||
if (opts.relays_CustomCheck && !opts.relays_CustomCheck(o.relays)) return new Error(`${path}.relays: custom check failed`)
|
||||
|
||||
if (typeof o.admin_npub !== 'string') return new Error(`${path}.admin_npub: is not a string`)
|
||||
if (opts.admin_npub_CustomCheck && !opts.admin_npub_CustomCheck(o.admin_npub)) return new Error(`${path}.admin_npub: custom check failed`)
|
||||
|
||||
if (typeof o.relay_connected !== 'boolean') return new Error(`${path}.relay_connected: is not a boolean`)
|
||||
if (opts.relay_connected_CustomCheck && !opts.relay_connected_CustomCheck(o.relay_connected)) return new Error(`${path}.relay_connected: custom check failed`)
|
||||
|
||||
if (!enumCheckLndState(o.lnd_state)) return new Error(`${path}.lnd_state: is not a valid LndState`)
|
||||
if (opts.lnd_state_CustomCheck && !opts.lnd_state_CustomCheck(o.lnd_state)) return new Error(`${path}.lnd_state: custom check failed`)
|
||||
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.watchdog_ok !== 'boolean') return new Error(`${path}.watchdog_ok: is not a boolean`)
|
||||
if (opts.watchdog_ok_CustomCheck && !opts.watchdog_ok_CustomCheck(o.watchdog_ok)) return new Error(`${path}.watchdog_ok: custom check failed`)
|
||||
|
||||
if (typeof o.http_url !== 'string') return new Error(`${path}.http_url: is not a string`)
|
||||
if (opts.http_url_CustomCheck && !opts.http_url_CustomCheck(o.http_url)) return new Error(`${path}.http_url: custom check failed`)
|
||||
return null
|
||||
}
|
||||
|
||||
if (typeof o.nprofile !== 'string') return new Error(`${path}.nprofile: is not a string`)
|
||||
if (opts.nprofile_CustomCheck && !opts.nprofile_CustomCheck(o.nprofile)) return new Error(`${path}.nprofile: custom check failed`)
|
||||
export type StateResponse = {
|
||||
admin_linked: boolean
|
||||
config_sent: boolean
|
||||
}
|
||||
export const StateResponseOptionalFields: [] = []
|
||||
export type StateResponseOptions = OptionsBaseMessage & {
|
||||
checkOptionalsAreSet?: []
|
||||
admin_linked_CustomCheck?: (v: boolean) => boolean
|
||||
config_sent_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.provider_name !== 'string') return new Error(`${path}.provider_name: is not a string`)
|
||||
if (opts.provider_name_CustomCheck && !opts.provider_name_CustomCheck(o.provider_name)) return new Error(`${path}.provider_name: custom check failed`)
|
||||
if (typeof o.admin_linked !== 'boolean') return new Error(`${path}.admin_linked: is not a boolean`)
|
||||
if (opts.admin_linked_CustomCheck && !opts.admin_linked_CustomCheck(o.admin_linked)) return new Error(`${path}.admin_linked: custom check failed`)
|
||||
|
||||
if (typeof o.config_sent !== 'boolean') return new Error(`${path}.config_sent: is not a boolean`)
|
||||
if (opts.config_sent_CustomCheck && !opts.config_sent_CustomCheck(o.config_sent)) return new Error(`${path}.config_sent: custom check failed`)
|
||||
|
||||
return null
|
||||
}
|
||||
|
|
@ -196,19 +231,34 @@ export enum AdminConnectInfoResponse_connect_info_type {
|
|||
ADMIN_TOKEN = 'admin_token',
|
||||
ENROLLED_NPUB = 'enrolled_npub',
|
||||
}
|
||||
export const enumCheckAdminConnectInfoResponse_connect_info_type = (e?: AdminConnectInfoResponse_connect_info_type): boolean => {
|
||||
for (const v in AdminConnectInfoResponse_connect_info_type) if (e === v) return true
|
||||
return false
|
||||
}
|
||||
export type AdminConnectInfoResponse_connect_info =
|
||||
{type:AdminConnectInfoResponse_connect_info_type.ADMIN_TOKEN, admin_token:string}|
|
||||
{type:AdminConnectInfoResponse_connect_info_type.ENROLLED_NPUB, enrolled_npub:string}
|
||||
|
||||
export const AdminConnectInfoResponse_connect_infoValidate = (o?: AdminConnectInfoResponse_connect_info, opts = {}, path: string = 'AdminConnectInfoResponse_connect_info::root.'): Error | null => {
|
||||
export type AdminConnectInfoResponse_connect_infoOptions = {
|
||||
admin_token_CustomCheck?: (v: string) => boolean
|
||||
enrolled_npub_CustomCheck?: (v: string) => boolean
|
||||
}
|
||||
export const AdminConnectInfoResponse_connect_infoValidate = (o?: AdminConnectInfoResponse_connect_info, opts:AdminConnectInfoResponse_connect_infoOptions = {}, path: string = 'AdminConnectInfoResponse_connect_info::root.'): Error | null => {
|
||||
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
|
||||
const stringType: string = o.type
|
||||
switch (o.type) {
|
||||
case 'admin_token':
|
||||
case AdminConnectInfoResponse_connect_info_type.ADMIN_TOKEN:
|
||||
if (typeof o.admin_token !== 'string') return new Error(`${path}.admin_token: is not a string`)
|
||||
if (opts.admin_token_CustomCheck && !opts.admin_token_CustomCheck(o.admin_token)) return new Error(`${path}.admin_token: custom check failed`)
|
||||
|
||||
break
|
||||
case 'enrolled_npub':
|
||||
case AdminConnectInfoResponse_connect_info_type.ENROLLED_NPUB:
|
||||
if (typeof o.enrolled_npub !== 'string') return new Error(`${path}.enrolled_npub: is not a string`)
|
||||
if (opts.enrolled_npub_CustomCheck && !opts.enrolled_npub_CustomCheck(o.enrolled_npub)) return new Error(`${path}.enrolled_npub: custom check failed`)
|
||||
|
||||
break
|
||||
default:
|
||||
return new Error(path + ': unknown type '+ stringType)
|
||||
}
|
||||
return new Error(path + ': unknown type'+ o.type)
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ log() {
|
|||
echo -e "$(echo "$message" | sed 's/\\e\[[0-9;]*m//g')" >> "$TMP_LOG_FILE"
|
||||
}
|
||||
|
||||
SCRIPT_VERSION="0.2.1"
|
||||
SCRIPT_VERSION="0.2.2"
|
||||
REPO="shocknet/Lightning.Pub"
|
||||
BRANCH="master"
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,11 @@ install_lnd() {
|
|||
# Check for and add default settings only if the keys are missing.
|
||||
grep -q "^bitcoin.mainnet=" $USER_HOME/.lnd/lnd.conf || echo "bitcoin.mainnet=true" >> $USER_HOME/.lnd/lnd.conf
|
||||
grep -q "^bitcoin.node=" $USER_HOME/.lnd/lnd.conf || echo "bitcoin.node=neutrino" >> $USER_HOME/.lnd/lnd.conf
|
||||
grep -q "^neutrino.addpeer=" $USER_HOME/.lnd/lnd.conf || echo "neutrino.addpeer=neutrino.shock.network" >> $USER_HOME/.lnd/lnd.conf
|
||||
grep -q "^neutrino.addpeer=neutrino.shock.network" $USER_HOME/.lnd/lnd.conf || echo "neutrino.addpeer=neutrino.shock.network" >> $USER_HOME/.lnd/lnd.conf
|
||||
grep -q "^neutrino.addpeer=asia.blixtwallet.com" $USER_HOME/.lnd/lnd.conf || echo "neutrino.addpeer=asia.blixtwallet.com" >> $USER_HOME/.lnd/lnd.conf
|
||||
grep -q "^neutrino.addpeer=europe.blixtwallet.com" $USER_HOME/.lnd/lnd.conf || echo "neutrino.addpeer=europe.blixtwallet.com" >> $USER_HOME/.lnd/lnd.conf
|
||||
grep -q "^neutrino.addpeer=btcd.lnolymp.us" $USER_HOME/.lnd/lnd.conf || echo "neutrino.addpeer=btcd.lnolymp.us" >> $USER_HOME/.lnd/lnd.conf
|
||||
grep -q "^neutrino.addpeer=btcd-mainnet.lightning.computer" $USER_HOME/.lnd/lnd.conf || echo "neutrino.addpeer=btcd-mainnet.lightning.computer" >> $USER_HOME/.lnd/lnd.conf
|
||||
grep -q "^fee.url=" $USER_HOME/.lnd/lnd.conf || echo "fee.url=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json" >> $USER_HOME/.lnd/lnd.conf
|
||||
|
||||
chmod 600 $USER_HOME/.lnd/lnd.conf
|
||||
|
|
|
|||
|
|
@ -80,7 +80,10 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
|||
}, event.startAtNano, event.startAtMs)
|
||||
})
|
||||
|
||||
return { Stop: () => nostr.Stop, Send: (...args) => nostr.Send(...args), Ping: () => nostr.Ping() }
|
||||
// Mark nostr connected/ready after initial subscription tick
|
||||
mainHandler.adminManager.setNostrConnected(true)
|
||||
|
||||
return { Stop: () => { mainHandler.adminManager.setNostrConnected(false); return nostr.Stop }, Send: (...args) => nostr.Send(...args), Ping: () => nostr.Ping() }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export class AdminManager {
|
|||
interval: NodeJS.Timer
|
||||
appNprofile: string
|
||||
lnd: LND
|
||||
nostrConnected: boolean = false
|
||||
constructor(mainSettings: MainSettings, storage: Storage) {
|
||||
this.storage = storage
|
||||
this.dataDir = mainSettings.storageSettings.dataDir
|
||||
|
|
@ -42,6 +43,14 @@ export class AdminManager {
|
|||
this.lnd = lnd
|
||||
}
|
||||
|
||||
setNostrConnected = (connected: boolean) => {
|
||||
this.nostrConnected = connected
|
||||
}
|
||||
|
||||
GetNostrConnected = () => {
|
||||
return this.nostrConnected
|
||||
}
|
||||
|
||||
setAppNprofile = (nprofile: string) => {
|
||||
this.appNprofile = nprofile
|
||||
const enrollToken = this.ReadAdminEnrollToken()
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ export default class {
|
|||
|
||||
StartBeacons() {
|
||||
this.applicationManager.StartAppsServiceBeacon(app => {
|
||||
this.UpdateBeacon(app, { type: 'service', name: app.name })
|
||||
this.UpdateBeacon(app, { type: 'service', name: app.name, avatarUrl: (app as any).avatar_url })
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -386,7 +386,7 @@ export default class {
|
|||
})
|
||||
}
|
||||
|
||||
async UpdateBeacon(app: Application, content: { type: 'service', name: string }) {
|
||||
async UpdateBeacon(app: Application, content: { type: 'service', name: string, avatarUrl?: string }) {
|
||||
if (!app.nostr_public_key) {
|
||||
getLogger({ appName: app.name })("cannot update beacon, public key not set")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ export class Application {
|
|||
@Column({ nullable: true, unique: true })
|
||||
nostr_public_key?: string
|
||||
|
||||
@Column({ nullable: true })
|
||||
avatar_url?: string
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class ApplicationAvatarUrl1761000001000 implements MigrationInterface {
|
||||
name = 'ApplicationAvatarUrl1761000001000'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "application" ADD COLUMN "avatar_url" varchar`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
// SQLite has limited ALTER TABLE support; we can recreate table if needed, but for now just keep it simple
|
||||
// No-op: dropping a column is non-trivial; leave column in place on downgrade
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -25,12 +25,13 @@ import { OldSomethingLeftover1753106599604 } from './1753106599604-old_something
|
|||
import { UserReceivingInvoiceIdx1753109184611 } from './1753109184611-user_receiving_invoice_idx.js'
|
||||
import { UserAccess1759426050669 } from './1759426050669-user_access.js'
|
||||
import { AddBlindToUserOffer1760000000000 } from './1760000000000-add_blind_to_user_offer.js'
|
||||
import { ApplicationAvatarUrl1761000001000 } from './1761000001000-application_avatar_url.js'
|
||||
|
||||
|
||||
export const allMigrations = [Initial1703170309875, LspOrder1718387847693, LiquidityProvider1719335699480, LndNodeInfo1720187506189,
|
||||
TrackedProvider1720814323679, CreateInviteTokenTable1721751414878, PaymentIndex1721760297610, DebitAccess1726496225078, DebitAccessFixes1726685229264,
|
||||
DebitToPub1727105758354, UserCbUrl1727112281043, UserOffer1733502626042, ManagementGrant1751307732346, ManagementGrantBanned1751989251513,
|
||||
InvoiceCallbackUrls1752425992291, OldSomethingLeftover1753106599604, UserReceivingInvoiceIdx1753109184611, AppUserDevice1753285173175, UserAccess1759426050669, AddBlindToUserOffer1760000000000]
|
||||
InvoiceCallbackUrls1752425992291, OldSomethingLeftover1753106599604, UserReceivingInvoiceIdx1753109184611, AppUserDevice1753285173175, UserAccess1759426050669, AddBlindToUserOffer1760000000000, ApplicationAvatarUrl1761000001000]
|
||||
|
||||
export const allMetricsMigrations = [LndMetrics1703170330183, ChannelRouting1709316653538, HtlcCount1724266887195, BalanceEvents1724860966825, RootOps1732566440447, RootOpsTime1745428134124, ChannelEvents1750777346411]
|
||||
/* export const TypeOrmMigrationRunner = async (log: PubLogger, storageManager: Storage, settings: DbSettings, arg: string | undefined): Promise<boolean> => {
|
||||
|
|
|
|||
|
|
@ -40,19 +40,63 @@ export class Wizard {
|
|||
}
|
||||
|
||||
GetServiceState = async (): Promise<WizardTypes.ServiceStateResponse> => {
|
||||
try {
|
||||
const apps = await this.storage.applicationStorage.GetApplications()
|
||||
const appNamesList = apps.map(app => app.name).join(', ')
|
||||
const relays = this.settings.nostrRelaySettings ? this.settings.nostrRelaySettings.relays : [];
|
||||
const relayUrl = (relays && relays.length > 0) ? relays[0] : '';
|
||||
const defaultApp = apps.find(a => a.name === this.settings.defaultAppName) || apps[0]
|
||||
// Determine LND state and watchdog
|
||||
let lndState: WizardTypes.LndState = WizardTypes.LndState.OFFLINE
|
||||
let watchdogOk = false
|
||||
try {
|
||||
const info = await this.adminManager.LndGetInfo()
|
||||
const online = info.synced_to_chain && info.synced_to_graph
|
||||
lndState = online ? WizardTypes.LndState.ONLINE : WizardTypes.LndState.SYNCING
|
||||
watchdogOk = !info.watchdog_barking
|
||||
} catch {
|
||||
lndState = WizardTypes.LndState.OFFLINE
|
||||
watchdogOk = false
|
||||
}
|
||||
return {
|
||||
admin_npub: this.adminManager.GetAdminNpub(),
|
||||
http_url: this.settings.serviceUrl,
|
||||
lnd_state: WizardTypes.LndState.OFFLINE,
|
||||
lnd_state: lndState,
|
||||
nprofile: this.nprofile,
|
||||
provider_name: appNamesList,
|
||||
relay_connected: false,
|
||||
provider_name: defaultApp?.name || appNamesList,
|
||||
relay_connected: this.adminManager.GetNostrConnected(),
|
||||
relays: this.relays,
|
||||
watchdog_ok: false
|
||||
watchdog_ok: watchdogOk,
|
||||
source_name: defaultApp?.name || this.settings.defaultAppName || appNamesList,
|
||||
relay_url: relayUrl,
|
||||
automate_liquidity: this.settings.liquiditySettings.liquidityProviderPub !== 'null',
|
||||
push_backups_to_nostr: this.settings.pushBackupsToNostr,
|
||||
avatar_url: defaultApp?.avatar_url || '',
|
||||
app_id: defaultApp?.app_id || ''
|
||||
}
|
||||
} catch (e) {
|
||||
this.log(`Error in GetServiceState: ${(e as Error).message}`)
|
||||
// Return a default/error state that is still valid JSON to prevent client-side parse errors
|
||||
return {
|
||||
admin_npub: '',
|
||||
http_url: '',
|
||||
lnd_state: WizardTypes.LndState.OFFLINE,
|
||||
nprofile: '',
|
||||
provider_name: 'Error loading state',
|
||||
relay_connected: false,
|
||||
relays: [],
|
||||
watchdog_ok: false,
|
||||
source_name: 'Error',
|
||||
relay_url: '',
|
||||
automate_liquidity: false,
|
||||
push_backups_to_nostr: false,
|
||||
avatar_url: '',
|
||||
app_id: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WizardState = async (): Promise<WizardTypes.StateResponse> => {
|
||||
return {
|
||||
config_sent: this.pendingConfig !== null,
|
||||
|
|
@ -118,10 +162,30 @@ export class Wizard {
|
|||
relay_url_CustomCheck: relay => relay !== '',
|
||||
})
|
||||
if (err != null) { throw new Error(err.message) }
|
||||
if (this.IsInitialized() || this.pendingConfig !== null) {
|
||||
throw new Error("already initialized")
|
||||
}
|
||||
const pendingConfig = { sourceName: req.source_name, relayUrl: req.relay_url, automateLiquidity: req.automate_liquidity, pushBackupsToNostr: req.push_backups_to_nostr }
|
||||
|
||||
// Persist app name/avatar to DB regardless (idempotent behavior)
|
||||
try {
|
||||
const appsList = await this.storage.applicationStorage.GetApplications()
|
||||
const defaultNames = ['wallet', 'wallet-test', this.settings.defaultAppName]
|
||||
const existingDefaultApp = appsList.find(app => defaultNames.includes(app.name)) || appsList[0]
|
||||
if (existingDefaultApp) {
|
||||
await this.storage.applicationStorage.UpdateApplication(existingDefaultApp, { name: req.source_name, avatar_url: (req as any).avatar_url || existingDefaultApp.avatar_url })
|
||||
}
|
||||
} catch (e) {
|
||||
this.log(`Error updating app info: ${(e as Error).message}`)
|
||||
}
|
||||
|
||||
// If already initialized, treat as idempotent update for env and exit
|
||||
if (this.IsInitialized()) {
|
||||
this.updateEnvFile(pendingConfig)
|
||||
return
|
||||
}
|
||||
|
||||
// First-time configuration flow
|
||||
if (this.pendingConfig !== null) {
|
||||
throw new Error("already initializing")
|
||||
}
|
||||
this.updateEnvFile(pendingConfig)
|
||||
this.configQueue.forEach(q => q.res(true))
|
||||
this.configQueue = []
|
||||
|
|
@ -153,11 +217,17 @@ export class Wizard {
|
|||
}
|
||||
|
||||
const automateLiquidityIndex = envFileContent.findIndex(line => line.startsWith('LIQUIDITY_PROVIDER_PUB'))
|
||||
if (pendingConfig.automateLiquidity) {
|
||||
if (automateLiquidityIndex !== -1) {
|
||||
envFileContent.splice(automateLiquidityIndex, 1)
|
||||
}
|
||||
} else {
|
||||
if (automateLiquidityIndex === -1) {
|
||||
toMerge.push(`LIQUIDITY_PROVIDER_PUB=${pendingConfig.automateLiquidity ? defaultProviderPub : ""}`)
|
||||
toMerge.push(`LIQUIDITY_PROVIDER_PUB=null`)
|
||||
} else {
|
||||
envFileContent[automateLiquidityIndex] = `LIQUIDITY_PROVIDER_PUB=null`
|
||||
}
|
||||
}
|
||||
|
||||
const pushBackupsToNostrIndex = envFileContent.findIndex(line => line.startsWith('PUSH_BACKUPS_TO_NOSTR'))
|
||||
if (pushBackupsToNostrIndex === -1) {
|
||||
|
|
|
|||
|
|
@ -1,143 +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/backup.css" />
|
||||
<!-- HTML Meta Tags -->
|
||||
<title>Lightning.Pub</title>
|
||||
<meta name="description" content="Lightning for Everyone" />
|
||||
<link rel="icon" type="image/png" href="img/pub_logo.png" />
|
||||
<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">
|
||||
<button class="icon-button back-button" onclick="history.back()">
|
||||
<img src="img/back.svg" alt="" />
|
||||
</button>
|
||||
<h2>Choose a Recovery Method</h2>
|
||||
<p class="header-title">
|
||||
<span style="font-weight: bold">New Node! 🎉</span> It's important
|
||||
to backup your keys.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="line"></div>
|
||||
|
||||
<section class="setup-content">
|
||||
<div class="description-box">
|
||||
<div class="description">
|
||||
In addition to your seed phrase, you also need channel details to recover funds should your node experience a
|
||||
hardware failure.
|
||||
</div>
|
||||
<br />
|
||||
<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.
|
||||
</div>
|
||||
</div>
|
||||
<div class="warning-text">
|
||||
If you did not choose the developers relay, be sure your relay has
|
||||
adequate storage policies to hold NIP78 events.
|
||||
</div>
|
||||
<div class="checkbox-container">
|
||||
<div class="checkbox" style="margin-top: 12px">
|
||||
<input type="checkbox" id="backup" />
|
||||
<div class="checkbox-shape"></div>
|
||||
<label for="backup">
|
||||
Encrypted Backup to Nostr Relay
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox-container">
|
||||
<div class="checkbox manual-checkbox" style="margin-top: 12px">
|
||||
<input type="checkbox" id="manual-backup" />
|
||||
<div class="checkbox-shape"></div>
|
||||
<label for="manual-backup">
|
||||
DO NOT store on relay (Manual Backups)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p id="errorText" style="color:red"></p>
|
||||
</div>
|
||||
<button class="push-button hidden-button" style="margin-top: 60px;" id="next-button">
|
||||
Next
|
||||
</button>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-text">
|
||||
<div>By proceeding you acknowledge that this is</div>
|
||||
<div>bleeding-edge software, and agree to the providers</div>
|
||||
<div>
|
||||
<span style="color: #c434e0">terms</span> regarding any services
|
||||
herein.
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<a href="https://docs.shock.network" class="marked need-help">Need Help?</a>
|
||||
</footer>
|
||||
|
||||
<script src="js/backup.js"></script>
|
||||
<script>
|
||||
const sendConfig = async () => {
|
||||
const req = {
|
||||
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',
|
||||
}
|
||||
const res = await fetch("/wizard/config", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(req)
|
||||
})
|
||||
if (res.status !== 200) {
|
||||
document.getElementById('errorText').innerText = "failed to start service"
|
||||
return
|
||||
}
|
||||
const j = await res.json()
|
||||
if (j.status !== 'OK') {
|
||||
document.getElementById('errorText').innerText = "failed to start service" + j.reason
|
||||
return
|
||||
}
|
||||
location.href = 'connect.html'
|
||||
}
|
||||
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')
|
||||
}
|
||||
sendConfig()
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<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" />
|
||||
<img src="img/LightningPub.png" height="26px" alt="Lightning Pub logo" />
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
<div style="text-align: center; color: #c434e0;" id="click-text">Click to reveal</div>
|
||||
</div>
|
||||
<div id="qrcode"></div>
|
||||
<div style="color: #a3a3a3; font-size: 11px;">
|
||||
<div style="color: #a3a3a3; font-size: 11px; word-break: break-all; overflow-wrap: anywhere; max-width: 220px; margin: 0 auto;">
|
||||
<div id="connectString"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -104,13 +104,20 @@
|
|||
height: 157,
|
||||
// correctLevel : QRCode.CorrectLevel.H
|
||||
});
|
||||
document.getElementById('connectString').innerHTML = connectString
|
||||
document.getElementById('connectString').innerText = connectString
|
||||
}
|
||||
}
|
||||
try {
|
||||
|
||||
fetchInfo()
|
||||
} catch (e) { console.log({ e }) }
|
||||
// Continue to status
|
||||
const btn = document.createElement('button')
|
||||
btn.className = 'push-button'
|
||||
btn.style.marginTop = '20px'
|
||||
btn.innerText = 'Continue'
|
||||
btn.onclick = () => { location.href = 'status.html' }
|
||||
document.querySelector('.setup-content').appendChild(btn)
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,8 @@
|
|||
display: block;
|
||||
position: relative;
|
||||
width: 253px;
|
||||
height: 241px;
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
min-height: 241px;
|
||||
height: auto;
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
transition: background-color 0.5s;
|
||||
|
|
@ -26,6 +25,60 @@
|
|||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
/* prevent overlap: hide connect string until revealed; click surface on box */
|
||||
.qrcode-box #connectString { display: none; margin-top: 6px; }
|
||||
.qrcode-box.revealed #connectString { display: block; }
|
||||
.qrcode-box.revealed #connectString {
|
||||
-webkit-user-select: text !important;
|
||||
-moz-user-select: text !important;
|
||||
-ms-user-select: text !important;
|
||||
user-select: text !important;
|
||||
pointer-events: auto !important;
|
||||
position: relative; /* Ensure z-index is respected */
|
||||
z-index: 10; /* Bring to the front */
|
||||
}
|
||||
.qrcode-box { cursor: pointer; }
|
||||
.qrcode-box.revealed { cursor: default; }
|
||||
|
||||
/* QR viewport and veil overlay */
|
||||
.qrcode-viewport {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 175px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.qrcode-viewport #qrcode {
|
||||
position: absolute;
|
||||
width: 157px;
|
||||
height: 157px;
|
||||
}
|
||||
/* Remove global QR blur after reveal */
|
||||
.qrcode-box.revealed #qrcode { filter: none !important; }
|
||||
|
||||
.qrcode-viewport .qr-veil {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(7px);
|
||||
-webkit-backdrop-filter: blur(7px);
|
||||
background: rgba(0,0,0,0.25);
|
||||
color: #c434e0;
|
||||
font-size: 12px;
|
||||
pointer-events: none; /* allow clicks to pass through if needed */
|
||||
}
|
||||
.qrcode-box.revealed .qr-veil { display: none; }
|
||||
.qrcode-viewport.revealed .qr-veil { display: none; }
|
||||
.qrcode-box.revealed .qrcode-viewport { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; pointer-events: none; }
|
||||
|
||||
/* hide helper text when revealed to avoid overlap */
|
||||
.qrcode-box.revealed #click-text { display: none; }
|
||||
|
||||
.qrcode-box-clicked::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -133,6 +133,10 @@ a {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.checkbox input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Create a new box */
|
||||
.checkbox label {
|
||||
padding-left: 32px;
|
||||
|
|
@ -157,6 +161,31 @@ a {
|
|||
z-index: -1;
|
||||
}
|
||||
|
||||
.checkbox input[type="radio"] + label::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid #a3a3a3;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.checkbox input[type="radio"]:checked + label::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: #a012c7;
|
||||
}
|
||||
|
||||
/* Display a checkmark when the checkbox is checked */
|
||||
.checkbox input[type="checkbox"]:checked + .checkbox-shape::before {
|
||||
content: "✓";
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 6.1 KiB |
|
|
@ -8,6 +8,10 @@
|
|||
<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/liquidity.css" />
|
||||
<link rel="stylesheet" href="css/backup.css" />
|
||||
<link rel="stylesheet" href="css/connect.css" />
|
||||
<link rel="stylesheet" href="css/status.css" />
|
||||
<!-- HTML Meta Tags -->
|
||||
<title>Lightning.Pub</title>
|
||||
<meta name="description" content="Lightning for Everyone" />
|
||||
|
|
@ -17,30 +21,38 @@
|
|||
<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" />
|
||||
<img src="img/LightningPub.png" height="26px" alt="Lightning Pub logo" />
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="setup-header">
|
||||
<section id="page-node">
|
||||
<div class="setup-header">
|
||||
<h2>Setup your Pub</h2>
|
||||
<p class="header-title">
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="line"></div>
|
||||
|
||||
<section class="setup-content">
|
||||
<div class="setup-content">
|
||||
<div class="input-group">
|
||||
<span>Give this node a name that wallet users will see:</span>
|
||||
<input type="text" placeholder="Nodey McNodeFace" value="" style="width: 100%" id="nodeName" />
|
||||
</div>
|
||||
|
||||
<div class="input-group" style="margin-top: 38px">
|
||||
<span>If you want to use a specific Nostr relay, enter it now:</span>
|
||||
<input type="text" placeholder="wss://relay.lightning.pub" style="width: 100%" id="relayUrl" />
|
||||
<div class="input-group" style="margin-top: 20px">
|
||||
<span>Avatar (shown in wallet):</span>
|
||||
<div style="display:flex; align-items:center; gap:12px; width:100%;">
|
||||
<img id="avatarPreview" src="" alt="avatar" style="width:56px;height:56px;border-radius:6px;object-fit:cover;box-shadow:0 0 2px rgba(0,0,0,1);" />
|
||||
<input type="text" placeholder="" style="flex:1" id="avatarUrl" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkbox" style="margin-top: 12px">
|
||||
<div class="input-group" style="margin-top: 28px">
|
||||
<span>If you want to use a specific Nostr relay, enter it now:</span>
|
||||
<div style="display:flex; flex-direction: column; gap:10px; width:100%;">
|
||||
<input type="text" placeholder="wss://relay.lightning.pub" style="width: 100%" id="relayUrl" />
|
||||
<div class="checkbox" style="margin-top: 0">
|
||||
<input type="checkbox" id="customCheckbox" />
|
||||
<div class="checkbox-shape"></div>
|
||||
<label for="customCheckbox">
|
||||
|
|
@ -48,6 +60,8 @@
|
|||
per month to support developers
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p id="errorText" style="color:red"></p>
|
||||
|
|
@ -56,6 +70,261 @@
|
|||
<button class="push-button" style="margin-top: 60px" id="liquidityBtn">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="page-liquidity" style="display: none;">
|
||||
<div class="setup-header">
|
||||
<button class="icon-button back-button" id="back-to-node">
|
||||
<img src="img/back.svg" alt="" />
|
||||
</button>
|
||||
<h2>Manage Node Liquidity</h2>
|
||||
<p class="header-title">
|
||||
How do you want to manage Lightning channels?
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="line"></div>
|
||||
|
||||
<div class="setup-content">
|
||||
<div class="checkbox" style="margin-top: 60px">
|
||||
<input type="radio" id="automate" name="service" data-group="service" />
|
||||
<label for="automate" class="automate">
|
||||
Use Automation Service
|
||||
<div class="question-box">
|
||||
<button class="icon-button" id="show-question">
|
||||
<img src="img/question.svg" />
|
||||
</button>
|
||||
</div>
|
||||
<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.
|
||||
<button class="icon-button close-button" id="close-question">
|
||||
<img src="img/close.svg" alt="" />
|
||||
</button>
|
||||
<a href="https://docs.shock.network/" target="_blank" class="marked question-more">Learn More</a>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox" style="margin-top: 30px">
|
||||
<input type="radio" id="manual" name="service" data-group="service" />
|
||||
<label for="manual">Manage my channels manually</label>
|
||||
</div>
|
||||
<div>
|
||||
<p id="errorTextLiquidity" style="color:red"></p>
|
||||
</div>
|
||||
<button class="push-button" style="margin-top: 60px" id="backupBtn">
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="page-backup" style="display: none;">
|
||||
<div class="setup-header">
|
||||
<button class="icon-button back-button" id="back-to-liquidity">
|
||||
<img src="img/back.svg" alt="" />
|
||||
</button>
|
||||
<h2>Choose a Recovery Method</h2>
|
||||
<p class="header-title">
|
||||
<span style="font-weight: bold">New Node! 🎉</span> It's important
|
||||
to backup your keys.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="line"></div>
|
||||
|
||||
<div class="setup-content">
|
||||
<div class="description-box">
|
||||
<div class="description">
|
||||
In addition to your seed phrase, you also need channel details to recover funds should your node experience a
|
||||
hardware failure.
|
||||
</div>
|
||||
<br />
|
||||
<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.
|
||||
</div>
|
||||
</div>
|
||||
<div class="warning-text">
|
||||
If you did not choose the developers relay, be sure your relay has
|
||||
adequate storage policies to hold NIP78 events.
|
||||
</div>
|
||||
<div class="checkbox-container">
|
||||
<div class="checkbox" style="margin-top: 12px">
|
||||
<input type="radio" id="backup" name="backup-option" />
|
||||
<label for="backup">
|
||||
Encrypted Backup to Nostr Relay
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox-container">
|
||||
<div class="checkbox manual-checkbox" style="margin-top: 12px">
|
||||
<input type="radio" id="manual-backup" name="backup-option" />
|
||||
<label for="manual-backup">
|
||||
DO NOT store on relay (Manual Backups)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p id="errorTextBackup" style="color:red"></p>
|
||||
</div>
|
||||
<button class="push-button" style="margin-top: 60px;" id="next-button">
|
||||
Finish Setup
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="page-connect" style="display: none;">
|
||||
<div class="setup-header">
|
||||
<button class="icon-button back-button" id="back-to-backup">
|
||||
<img src="img/back.svg" alt="" />
|
||||
</button>
|
||||
<h2>Connect</h2>
|
||||
<p class="header-title">You can now manage your node remotely</p>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="setup-content">
|
||||
<div style="font-size: 13px; margin-top: 5px;">Scan the QR or Copy-Paste the string to establish the connection.</div>
|
||||
<div style="display: flex; justify-content: center;">
|
||||
<div class="qrcode-box" id="codebox" aria-label="Pairing QR and string">
|
||||
<div style="font-size: 11px;">
|
||||
<div style="text-align: center; color: #a3a3a3;">Code contains a one-time pairing secret</div>
|
||||
<div style="text-align: center; color: #c434e0;" id="click-text">Click to reveal</div>
|
||||
</div>
|
||||
<div class="qrcode-viewport">
|
||||
<div id="qrcode"></div>
|
||||
<div class="qr-veil"><span>Click to reveal</span></div>
|
||||
</div>
|
||||
<div style="color: #a3a3a3; font-size: 11px; word-break: break-all; overflow-wrap: anywhere; max-width: 220px; margin: 0 auto;">
|
||||
<div id="connectString"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="push-button" style="margin-top: 20px" id="to-status">OK, I've connected my wallet</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="page-status" style="display: none;">
|
||||
<div class="setup-header">
|
||||
<button class="icon-button back-button" id="back-to-connect">
|
||||
<img src="img/back.svg" alt="" />
|
||||
</button>
|
||||
<h2>Node Status</h2>
|
||||
<p class="header-title"></p>
|
||||
</div>
|
||||
<div class="line" style="width: 100%;"></div>
|
||||
<section class="node-status">
|
||||
<p id="errorText" style="color:red"></p>
|
||||
<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="" />
|
||||
<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 id="adminNpub" style="line-break: anywhere;">Loading...</div>
|
||||
</div>
|
||||
<div class="status-element" style="margin-top: 15px;">
|
||||
<div style="text-align: left;">Avatar:</div>
|
||||
<div class="fc-grey editable-content">
|
||||
<div class="show-avatar" style="display: flex; flex-direction: column; display: none;">
|
||||
<input type="text" value="" name="show-avatar" placeholder="" />
|
||||
<div style="display: flex;justify-content: end;">
|
||||
<button class="small-btn" id="cancel-show-avatar">Cancel</button>
|
||||
<button class="small-btn" id="save-show-avatar">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:12px;">
|
||||
<img id="avatarImg" src="" alt="avatar" style="width:48px;height:48px;border-radius:6px;object-fit:cover;box-shadow:0 0 2px rgba(0,0,0,1);" />
|
||||
<div class="question-box">
|
||||
<button class="icon-button" id="show-avatar">
|
||||
<img src="img/pencil.svg" style="cursor: pointer;" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</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; display: none;">
|
||||
Reset
|
||||
<div class="watchdog-status">
|
||||
<a href="https://docs.shock.network/pub/watchdog" target="_blank">
|
||||
<button class="icon-button" id="show-question">
|
||||
<img src="img/question.svg" />
|
||||
</button>
|
||||
</a>
|
||||
</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 id="relayStatus"><span class="yellow-dot">●</span> Loading...</div>
|
||||
</div>
|
||||
<div class="status-element">
|
||||
<div>Lightning Status:</div>
|
||||
<div id="lndStatus"><span class="yellow-dot">●</span> Loading...</div>
|
||||
</div>
|
||||
<div class="status-element">
|
||||
<div style="position: relative;">Watchdog Status:
|
||||
<div class="watchdog-status">
|
||||
<a href="https://docs.shock.network/pub/watchdog" target="_blank">
|
||||
<button class="icon-button" id="show-question"><img src="img/question.svg" /></button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="watchdog-status"><span class="green-dot">●</span> Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<div style="font-size: 13px; text-align: left;">Guest Invitation Link:</div>
|
||||
<a href="#" target="_blank" style="font-size: 11px;line-break: anywhere;" id="inviteLinkHttp" class="invite-link"></a>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
|
|
@ -71,43 +340,10 @@
|
|||
<div class="line"></div>
|
||||
<a href="https://docs.shock.network" class="marked need-help">Need Help?</a>
|
||||
</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.admin_linked) {
|
||||
location.href = 'status.html'
|
||||
} else if (data.config_sent) {
|
||||
location.href = 'connect.html'
|
||||
} else {
|
||||
console.log("ready to initialize")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="https://cdn.rawgit.com/davidshimjs/qrcodejs/gh-pages/qrcode.min.js"></script>
|
||||
<script src="js/status.js"></script>
|
||||
<script src="js/wizard.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
$(() => {
|
||||
let backup; let manual_backup;
|
||||
$("#backup").click(() => {
|
||||
backup = $("#backup").prop("checked");
|
||||
$('#manual-backup').prop("checked",false);
|
||||
const nextButton = $("#next-button");
|
||||
if (backup) {
|
||||
nextButton.removeClass("hidden-button");
|
||||
} else {
|
||||
nextButton.addClass("hidden-button");
|
||||
}
|
||||
});
|
||||
$("#manual-backup").click(()=>{
|
||||
manual_backup = $('#manual-backup').prop("checked");
|
||||
$("#backup").prop("checked",false);
|
||||
const nextButton = $("#next-button");
|
||||
if(manual_backup) {
|
||||
nextButton.removeClass("hidden-button");
|
||||
} else {
|
||||
nextButton.addClass("hidden-button");
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
$(() => {
|
||||
$("#show-question").click(() => {
|
||||
$("#question-content").show();
|
||||
});
|
||||
|
||||
$("#close-question").click(() => {
|
||||
$("#question-content").hide();
|
||||
});
|
||||
|
||||
$("#automate").click(() => {
|
||||
$('[data-group="service"]').prop("checked", false);
|
||||
$("#automate").prop("checked", true);
|
||||
});
|
||||
|
||||
$("#manual").click(() => {
|
||||
$('[data-group="service"]').prop("checked", false);
|
||||
$("#manual").prop("checked", true);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,4 +1,27 @@
|
|||
$(() => {
|
||||
const postConfig = async (updates) => {
|
||||
try {
|
||||
const stateRes = await fetch('/wizard/service_state')
|
||||
if (stateRes.status !== 200) return false
|
||||
const s = await stateRes.json()
|
||||
const body = {
|
||||
source_name: updates.source_name ?? (s.source_name || s.provider_name || ''),
|
||||
relay_url: updates.relay_url ?? (s.relay_url || (s.relays && s.relays[0]) || ''),
|
||||
automate_liquidity: s.automate_liquidity || false,
|
||||
push_backups_to_nostr: s.push_backups_to_nostr || false,
|
||||
avatar_url: s.avatar_url || ''
|
||||
}
|
||||
const res = await fetch('/wizard/config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
if (res.status !== 200) return false
|
||||
const j = await res.json().catch(() => ({}))
|
||||
if (j && j.status && j.status !== 'OK') return false
|
||||
return true
|
||||
} catch { return false }
|
||||
}
|
||||
$("#show-reset").click(() => {
|
||||
$("#reset-content").text('Reset the administrator account if you lost access via the Dashboard.');
|
||||
$("#reset-box").show();
|
||||
|
|
@ -11,6 +34,11 @@ $(() => {
|
|||
$("#reset-box").show();
|
||||
$('.continue-button').attr('id', 'set-show-nostr');
|
||||
});
|
||||
$("#show-avatar").click(() => {
|
||||
$('.show-avatar').show()
|
||||
$('#show-avatar-text').hide()
|
||||
$('input[name="show-avatar"]').focus();
|
||||
});
|
||||
$("#show-nodey").click(() => {
|
||||
$('.show-nodey').show()
|
||||
$('#show-nodey-text').hide()
|
||||
|
|
@ -18,10 +46,14 @@ $(() => {
|
|||
});
|
||||
$("#save-show-nodey").click(() => {
|
||||
var targetInputVal = $('input[name="show-nodey"]').val()
|
||||
postConfig({ source_name: targetInputVal }).then(ok => {
|
||||
if (ok) {
|
||||
$('#show-nodey-text').text(targetInputVal)
|
||||
}
|
||||
$('.show-nodey').hide()
|
||||
$('#show-nodey-text').show()
|
||||
})
|
||||
})
|
||||
$("#cancel-show-nodey").click(() => {
|
||||
$('.show-nodey').hide()
|
||||
$('#show-nodey-text').show()
|
||||
|
|
@ -38,10 +70,29 @@ $(() => {
|
|||
});
|
||||
$("#save-show-nostr").click(() => {
|
||||
var targetInputVal = $('input[name="show-nostr"]').val()
|
||||
postConfig({ relay_url: targetInputVal }).then(ok => {
|
||||
if (ok) {
|
||||
$('#show-nostr-text').text(targetInputVal)
|
||||
}
|
||||
$('.show-nostr').hide()
|
||||
$('#show-nostr-text').show()
|
||||
})
|
||||
})
|
||||
$("#save-show-avatar").click(() => {
|
||||
var targetInputVal = $('input[name="show-avatar"]').val()
|
||||
postConfig({ avatar_url: targetInputVal }).then(ok => {
|
||||
if (ok) {
|
||||
$('#show-avatar-text').text(targetInputVal || '—')
|
||||
if (targetInputVal) { $('#avatarImg').attr('src', targetInputVal) }
|
||||
}
|
||||
$('.show-avatar').hide()
|
||||
$('#show-avatar-text').show()
|
||||
})
|
||||
})
|
||||
$("#cancel-show-avatar").click(() => {
|
||||
$('.show-avatar').hide()
|
||||
$('#show-avatar-text').show()
|
||||
})
|
||||
$("#cancel-show-nostr").click(() => {
|
||||
$('.show-nostr').hide()
|
||||
$('#show-nostr-text').show()
|
||||
|
|
|
|||
241
static/js/wizard.js
Normal file
241
static/js/wizard.js
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
$(() => {
|
||||
// Page sections
|
||||
const pages = {
|
||||
node: $('#page-node'),
|
||||
liquidity: $('#page-liquidity'),
|
||||
backup: $('#page-backup'),
|
||||
connect: $('#page-connect'),
|
||||
status: $('#page-status')
|
||||
};
|
||||
|
||||
// Inputs
|
||||
const nodeNameInput = $("#nodeName");
|
||||
const relayUrlInput = $("#relayUrl");
|
||||
const avatarUrlInput = $("#avatarUrl");
|
||||
const avatarPreview = $("#avatarPreview");
|
||||
const customCheckbox = $("#customCheckbox");
|
||||
const automateLiquidityRadio = $("#automate");
|
||||
const manualLiquidityRadio = $("#manual");
|
||||
const backupNostrRadio = $("#backup");
|
||||
const manualBackupRadio = $("#manual-backup");
|
||||
|
||||
// Buttons
|
||||
const toLiquidityBtn = $("#liquidityBtn");
|
||||
const toBackupBtn = $("#backupBtn");
|
||||
const toStatusBtn = $("#to-status");
|
||||
const finishBtn = $("#next-button");
|
||||
const backToNodeBtn = $("#back-to-node");
|
||||
const backToLiquidityBtn = $("#back-to-liquidity");
|
||||
|
||||
// Error text
|
||||
const errorTextNode = $("#errorText");
|
||||
const errorTextLiquidity = $("#errorTextLiquidity");
|
||||
const errorTextBackup = $("#errorTextBackup");
|
||||
|
||||
// Liquidity question mark
|
||||
$("#show-question").click(() => $("#question-content").show());
|
||||
$("#close-question").click(() => $("#question-content").hide());
|
||||
|
||||
const showPage = (pageToShow) => {
|
||||
Object.values(pages).forEach(page => page.hide());
|
||||
pageToShow.show();
|
||||
};
|
||||
|
||||
const populateStatus = async () => {
|
||||
try {
|
||||
const res = await fetch('/wizard/service_state');
|
||||
if (res.status !== 200) return;
|
||||
const s = await res.json();
|
||||
const name = s.source_name || s.provider_name || '';
|
||||
const relay = s.relay_url || (s.relays && s.relays[0]) || '';
|
||||
const admin = s.admin_npub || '';
|
||||
const avatar = s.avatar_url || (s.app_id ? `https://robohash.org/${encodeURIComponent(s.app_id)}.png?size=128x128&set=set3` : '');
|
||||
|
||||
const lndState = s.lnd_state; // 0 OFFLINE, 1 SYNCING, 2 ONLINE (per enum)
|
||||
const watchdogOk = !!s.watchdog_ok;
|
||||
const relayConnected = !!s.relay_connected;
|
||||
|
||||
$('#show-nodey-text').text(name || '—');
|
||||
$('#show-nostr-text').text(relay || '—');
|
||||
$('#adminNpub').text(admin || '—');
|
||||
if (avatar) { $('#avatarImg').attr('src', avatar); }
|
||||
|
||||
const mkDot = (ok) => ok ? '<span class="green-dot">●</span>' : '<span class="yellow-dot">●</span>';
|
||||
const lndTxt = lndState === 2 ? 'Online' : (lndState === 1 ? 'Syncing' : 'Offline');
|
||||
$('#lndStatus').html(`${mkDot(lndState === 2)} ${lndTxt}`);
|
||||
$('#watchdog-status').html(`${mkDot(watchdogOk)} ${watchdogOk ? 'OK' : 'Alert'}`);
|
||||
$('#relayStatus').html(`${mkDot(relayConnected)} ${relayConnected ? 'Connected' : 'Disconnected'}`);
|
||||
} catch { /* noop */ }
|
||||
};
|
||||
|
||||
// Navigation
|
||||
toLiquidityBtn.click(() => {
|
||||
const nodeName = nodeNameInput.val();
|
||||
const relayUrl = relayUrlInput.val();
|
||||
const useDefaultRelay = customCheckbox.prop('checked');
|
||||
if (!nodeName) {
|
||||
errorTextNode.text("Please enter a node name");
|
||||
return;
|
||||
}
|
||||
if (!useDefaultRelay && !relayUrl) {
|
||||
errorTextNode.text("Please enter a relay URL or check the default relay box");
|
||||
return;
|
||||
}
|
||||
errorTextNode.text("");
|
||||
showPage(pages.liquidity);
|
||||
});
|
||||
|
||||
toBackupBtn.click(() => {
|
||||
if (!automateLiquidityRadio.prop('checked') && !manualLiquidityRadio.prop('checked')) {
|
||||
errorTextLiquidity.text('Please select an option');
|
||||
return;
|
||||
}
|
||||
errorTextLiquidity.text("");
|
||||
showPage(pages.backup);
|
||||
});
|
||||
|
||||
backToNodeBtn.click(() => showPage(pages.node));
|
||||
backToLiquidityBtn.click(() => showPage(pages.liquidity));
|
||||
|
||||
// Final submission
|
||||
finishBtn.click(async () => {
|
||||
if (!backupNostrRadio.prop('checked') && !manualBackupRadio.prop('checked')) {
|
||||
errorTextBackup.text('Please select an option');
|
||||
return;
|
||||
}
|
||||
errorTextBackup.text("");
|
||||
|
||||
const relayUrl = customCheckbox.prop('checked') ? 'wss://relay.lightning.pub' : relayUrlInput.val();
|
||||
|
||||
const req = {
|
||||
source_name: nodeNameInput.val(),
|
||||
relay_url: relayUrl,
|
||||
automate_liquidity: automateLiquidityRadio.prop('checked'),
|
||||
push_backups_to_nostr: backupNostrRadio.prop('checked'),
|
||||
avatar_url: avatarUrlInput.val()
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch("/wizard/config", {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(req)
|
||||
});
|
||||
if (!res.ok) {
|
||||
const j = await res.json();
|
||||
throw new Error(j.reason || "Failed to start service");
|
||||
}
|
||||
// Move to in-page connect step
|
||||
showPage(pages.connect);
|
||||
// fetch and prepare connect info
|
||||
(async () => {
|
||||
const res = await fetch('/wizard/admin_connect_info');
|
||||
if (res.status !== 200) return;
|
||||
const j = await res.json();
|
||||
if (j.connect_info && j.connect_info.enrolled_npub) {
|
||||
showPage(pages.status);
|
||||
await populateStatus();
|
||||
return;
|
||||
}
|
||||
const connectString = j.nprofile + ':' + j.connect_info.admin_token;
|
||||
const qrElement = document.getElementById('qrcode');
|
||||
const codebox = $('#codebox');
|
||||
const clickText = $('#click-text');
|
||||
const cs = $('#connectString');
|
||||
|
||||
// Reset visual state
|
||||
codebox.removeClass('revealed');
|
||||
cs.text('');
|
||||
codebox.find('.qr-veil').show();
|
||||
clickText.show();
|
||||
if (qrElement) {
|
||||
while (qrElement.firstChild) qrElement.removeChild(qrElement.firstChild);
|
||||
// Pre-generate QR behind veil to entice reveal
|
||||
new QRCode(qrElement, { text: connectString, colorDark: '#000000', colorLight: '#ffffff', width: 157, height: 157 });
|
||||
}
|
||||
|
||||
// Reveal on click: show string below and remove veil/heading
|
||||
codebox.off('click').on('click', (e) => {
|
||||
if (!codebox.hasClass('revealed')) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
cs.text(connectString);
|
||||
codebox.addClass('revealed');
|
||||
|
||||
// Remove the veil from the DOM entirely to kill the blur
|
||||
codebox.find('.qr-veil').remove();
|
||||
|
||||
clickText.hide();
|
||||
|
||||
// Unbind to allow text selection and normal behavior after reveal
|
||||
codebox.off('click');
|
||||
|
||||
// Force text to be selectable on top
|
||||
cs.css({
|
||||
'user-select': 'text',
|
||||
'-webkit-user-select': 'text',
|
||||
'pointer-events': 'auto'
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
} catch (err) {
|
||||
errorTextBackup.text(err.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Navigate from connect to status
|
||||
toStatusBtn && toStatusBtn.click(async () => {
|
||||
showPage(pages.status);
|
||||
await populateStatus();
|
||||
})
|
||||
|
||||
const syncRelayState = () => {
|
||||
relayUrlInput.prop('disabled', customCheckbox.prop('checked'));
|
||||
if (customCheckbox.prop('checked')) {
|
||||
relayUrlInput.val('');
|
||||
}
|
||||
};
|
||||
|
||||
customCheckbox.on('change', syncRelayState);
|
||||
relayUrlInput.on('input', () => {
|
||||
if (relayUrlInput.val()) {
|
||||
customCheckbox.prop('checked', false);
|
||||
syncRelayState();
|
||||
}
|
||||
});
|
||||
|
||||
// Initial state load (no redirects; SPA only)
|
||||
console.log('Wizard script version: REVEAL_FIX_3 activated');
|
||||
fetch("/wizard/service_state").then(res => res.json()).then(state => {
|
||||
nodeNameInput.val(state.source_name);
|
||||
if (state.relay_url === 'wss://relay.lightning.pub') {
|
||||
customCheckbox.prop('checked', true);
|
||||
} else {
|
||||
relayUrlInput.val(state.relay_url);
|
||||
}
|
||||
const robo = state.app_id ? `https://robohash.org/${encodeURIComponent(state.app_id)}.png?size=128x128&set=set3` : ''
|
||||
if (state.avatar_url) {
|
||||
avatarUrlInput.val(state.avatar_url);
|
||||
avatarPreview.attr('src', state.avatar_url)
|
||||
} else if (robo) {
|
||||
avatarPreview.attr('src', robo)
|
||||
}
|
||||
if (robo) {
|
||||
avatarUrlInput.attr('placeholder', robo)
|
||||
}
|
||||
syncRelayState();
|
||||
|
||||
if (state.automate_liquidity) {
|
||||
automateLiquidityRadio.prop('checked', true);
|
||||
} else {
|
||||
manualLiquidityRadio.prop('checked', true);
|
||||
}
|
||||
|
||||
if (state.push_backups_to_nostr) {
|
||||
backupNostrRadio.prop('checked', true);
|
||||
} else {
|
||||
manualBackupRadio.prop('checked', true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1,109 +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/liquidity.css" />
|
||||
<!-- HTML Meta Tags -->
|
||||
<title>Lightning.Pub</title>
|
||||
<meta name="description" content="Lightning for Everyone" />
|
||||
<link rel="icon" type="image/png" href="img/pub_logo.png" />
|
||||
<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">
|
||||
<button class="icon-button back-button" onclick="history.back()">
|
||||
<img src="img/back.svg" alt="" />
|
||||
</button>
|
||||
<h2>Manage Node Liquidity</h2>
|
||||
<p class="header-title">
|
||||
How do you want to manage Lightning channels?
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="line"></div>
|
||||
|
||||
<section class="setup-content">
|
||||
<div class="checkbox" style="margin-top: 60px">
|
||||
<input type="checkbox" id="automate" data-group="service" />
|
||||
<div class="checkbox-shape"></div>
|
||||
<label for="automate" class="automate">
|
||||
Use Automation Service
|
||||
<div class="question-box">
|
||||
<button class="icon-button" id="show-question">
|
||||
<img src="img/question.svg" />
|
||||
</button>
|
||||
</div>
|
||||
<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.
|
||||
<button class="icon-button close-button" id="close-question">
|
||||
<img src="img/close.svg" alt="" />
|
||||
</button>
|
||||
<a href="https://docs.shock.network/" target="_blank" class="marked question-more">Learn More</a>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox" style="margin-top: 30px">
|
||||
<input type="checkbox" id="manual" data-group="service" />
|
||||
<div class="checkbox-shape"></div>
|
||||
<label for="manual">Manage my channels manually</label>
|
||||
</div>
|
||||
<div>
|
||||
<p id="errorText" style="color:red"></p>
|
||||
</div>
|
||||
<button class="push-button" style="margin-top: 60px" id="backupBtn">
|
||||
Next
|
||||
</button>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div class="footer-text">
|
||||
<div>By proceeding you acknowledge that this is</div>
|
||||
<div>bleeding-edge software, and agree to the providers</div>
|
||||
<div>
|
||||
<span style="color: #c434e0">terms</span> regarding any services
|
||||
herein.
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<a href="https://docs.shock.network" class="marked need-help">Need Help?</a>
|
||||
</footer>
|
||||
<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>
|
||||
|
||||
</html>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<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" />
|
||||
<img src="img/LightningPub.png" height="26px" alt="Lightning Pub logo" />
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
|
@ -69,6 +69,26 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-element" style="margin-top: 15px;">
|
||||
<div style="text-align: left;">Avatar:</div>
|
||||
<div class="fc-grey editable-content">
|
||||
<div class="show-avatar" style="display: flex; flex-direction: column; display: none;">
|
||||
<input type="text" value="" name="show-avatar" placeholder="" />
|
||||
<div style="display: flex;justify-content: end;">
|
||||
<button class="small-btn" id="cancel-show-avatar">Cancel</button>
|
||||
<button class="small-btn" id="save-show-avatar">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:12px;">
|
||||
<img id="avatarImg" src="" alt="avatar" style="width:48px;height:48px;border-radius:6px;object-fit:cover;box-shadow:0 0 2px rgba(0,0,0,1);" />
|
||||
<div class="question-box">
|
||||
<button class="icon-button" id="show-avatar">
|
||||
<img src="img/pencil.svg" style="cursor: pointer;" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-element" style="margin-top: 15px;">
|
||||
<div>Administrator:</div>
|
||||
<div id="adminNpub" style="line-break: anywhere;">
|
||||
|
|
@ -157,20 +177,29 @@
|
|||
}
|
||||
const j = await res.json()
|
||||
console.log(j)
|
||||
if (j.status !== 'OK') {
|
||||
document.getElementById('errorText').innerText = "failed to get state info" + j.reason
|
||||
return
|
||||
}
|
||||
document.getElementById("show-nodey-text").innerHTML = j.provider_name
|
||||
document.getElementById("show-nostr-text").innerHTML = j.relays[0]
|
||||
document.getElementById("adminNpub").innerText = j.admin_npub
|
||||
document.getElementById("relayStatus").innerHTML = `<span class="${j.relay_connected ? 'green-dot' : 'red-dot'}">●</span> ${j.relay_connected ? 'Connected' : 'Disconnected'}`
|
||||
document.getElementById("lndStatus").innerHTML = `<span class="${j.lnd_state === 'ONLINE' ? 'green-dot' : 'red-dot'}">●</span> ${j.lnd_state}`
|
||||
// Map fields from service state
|
||||
const name = j.source_name || j.provider_name || 'Unknown'
|
||||
const relayUrl = j.relay_url || (j.relays && j.relays[0]) || ''
|
||||
const admin = j.admin_npub || ''
|
||||
// lnd_state may arrive as enum number or string; normalize to text
|
||||
const lndText = (typeof j.lnd_state === 'string')
|
||||
? j.lnd_state
|
||||
: (j.lnd_state === 2 ? 'ONLINE' : (j.lnd_state === 1 ? 'SYNCING' : 'OFFLINE'))
|
||||
const lndDot = (lndText === 'ONLINE') ? 'green-dot' : (lndText === 'SYNCING' ? 'yellow-dot' : 'red-dot')
|
||||
// use actual relay_connected flag from backend
|
||||
const relayConnected = !!j.relay_connected
|
||||
const avatar = j.avatar_url || (j.app_id ? `https://robohash.org/${encodeURIComponent(j.app_id)}.png?size=128x128&set=set3` : '')
|
||||
document.getElementById("show-nodey-text").innerHTML = name
|
||||
document.getElementById("show-nostr-text").innerHTML = relayUrl || '—'
|
||||
if (avatar) { document.getElementById("avatarImg").src = avatar }
|
||||
document.getElementById("adminNpub").innerText = admin
|
||||
document.getElementById("relayStatus").innerHTML = `<span class="${relayConnected ? 'green-dot' : 'red-dot'}">●</span> ${relayConnected ? 'Connected' : 'Disconnected'}`
|
||||
document.getElementById("lndStatus").innerHTML = `<span class="${lndDot}">●</span> ${lndText}`
|
||||
document.getElementById("watchdog-status").innerHTML = `<span class="${j.watchdog_ok ? 'green-dot' : 'red-dot'}">●</span> ${j.watchdog_ok ? 'No Alerts' : 'ALERT!!'}`
|
||||
document.getElementById("inviteLinkHttp").href = `https://my.shockwallet.app/#/sources?addSource=${j.nprofile}`
|
||||
document.getElementById("inviteLinkHttp").innerHTML = `https://my.shockwallet.app/#/sources?addSource=${j.nprofile}`
|
||||
document.querySelector('input[name="show-nodey"]').placeholder = j.provider_name;
|
||||
document.title = j.provider_name;
|
||||
document.querySelector('input[name="show-nodey"]').placeholder = name;
|
||||
document.title = name;
|
||||
}
|
||||
try {
|
||||
fetchInfo()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue