feat(extensions): add LNURL-withdraw extension #6
2 changed files with 69 additions and 35 deletions
71
src/index.ts
71
src/index.ts
|
|
@ -35,41 +35,8 @@ const start = async () => {
|
|||
|
||||
const { mainHandler, localProviderClient, wizard, adminManager } = keepOn
|
||||
const serverMethods = GetServerMethods(mainHandler)
|
||||
log("initializing nostr middleware")
|
||||
const relays = settingsManager.getSettings().nostrRelaySettings.relays
|
||||
const maxEventContentLength = settingsManager.getSettings().nostrRelaySettings.maxEventContentLength
|
||||
const apps: AppInfo[] = keepOn.apps.map(app => {
|
||||
return {
|
||||
appId: app.appId,
|
||||
privateKey: app.privateKey,
|
||||
publicKey: app.publicKey,
|
||||
name: app.name,
|
||||
provider: app.publicKey === localProviderClient.publicKey ? {
|
||||
clientId: `client_${localProviderClient.appId}`,
|
||||
pubkey: settingsManager.getSettings().liquiditySettings.liquidityProviderPub,
|
||||
relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl
|
||||
} : undefined
|
||||
}
|
||||
})
|
||||
const { Send, Stop, Ping, Reset } = nostrMiddleware(serverMethods, mainHandler,
|
||||
{
|
||||
relays, maxEventContentLength, apps
|
||||
},
|
||||
(e, p) => mainHandler.liquidityProvider.onEvent(e, p)
|
||||
)
|
||||
exitHandler(() => { Stop(); mainHandler.Stop() })
|
||||
log("starting server")
|
||||
mainHandler.attachNostrSend(Send)
|
||||
mainHandler.attachNostrProcessPing(Ping)
|
||||
mainHandler.attachNostrReset(Reset)
|
||||
mainHandler.StartBeacons()
|
||||
const appNprofile = nprofileEncode({ pubkey: localProviderClient.publicKey, relays })
|
||||
if (wizard) {
|
||||
wizard.AddConnectInfo(appNprofile, relays)
|
||||
}
|
||||
adminManager.setAppNprofile(appNprofile)
|
||||
|
||||
// Initialize extension system
|
||||
// Initialize extension system BEFORE nostrMiddleware so RPC methods are available
|
||||
let extensionLoader: ExtensionLoader | null = null
|
||||
const mainPort = settingsManager.getSettings().serviceSettings.servicePort
|
||||
const extensionPort = mainPort + 1
|
||||
|
|
@ -103,6 +70,42 @@ const start = async () => {
|
|||
log(`extension system initialization failed: ${e}`)
|
||||
}
|
||||
|
||||
// Initialize nostr middleware with extension loader for RPC routing
|
||||
log("initializing nostr middleware")
|
||||
const relays = settingsManager.getSettings().nostrRelaySettings.relays
|
||||
const maxEventContentLength = settingsManager.getSettings().nostrRelaySettings.maxEventContentLength
|
||||
const apps: AppInfo[] = keepOn.apps.map(app => {
|
||||
return {
|
||||
appId: app.appId,
|
||||
privateKey: app.privateKey,
|
||||
publicKey: app.publicKey,
|
||||
name: app.name,
|
||||
provider: app.publicKey === localProviderClient.publicKey ? {
|
||||
clientId: `client_${localProviderClient.appId}`,
|
||||
pubkey: settingsManager.getSettings().liquiditySettings.liquidityProviderPub,
|
||||
relayUrl: settingsManager.getSettings().liquiditySettings.providerRelayUrl
|
||||
} : undefined
|
||||
}
|
||||
})
|
||||
const { Send, Stop, Ping, Reset } = nostrMiddleware(serverMethods, mainHandler,
|
||||
{
|
||||
relays, maxEventContentLength, apps
|
||||
},
|
||||
(e, p) => mainHandler.liquidityProvider.onEvent(e, p),
|
||||
{ extensionLoader: extensionLoader || undefined }
|
||||
)
|
||||
exitHandler(() => { Stop(); mainHandler.Stop() })
|
||||
log("starting server")
|
||||
mainHandler.attachNostrSend(Send)
|
||||
mainHandler.attachNostrProcessPing(Ping)
|
||||
mainHandler.attachNostrReset(Reset)
|
||||
mainHandler.StartBeacons()
|
||||
const appNprofile = nprofileEncode({ pubkey: localProviderClient.publicKey, relays })
|
||||
if (wizard) {
|
||||
wizard.AddConnectInfo(appNprofile, relays)
|
||||
}
|
||||
adminManager.setAppNprofile(appNprofile)
|
||||
|
||||
// Create Express app for extension HTTP routes
|
||||
const extensionApp = express()
|
||||
extensionApp.use(cors()) // Enable CORS for all origins (ATM apps, wallets, etc.)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,15 @@ import * as Types from '../proto/autogenerated/ts/types.js'
|
|||
import NewNostrTransport, { NostrRequest } from '../proto/autogenerated/ts/nostr_transport.js';
|
||||
import { ERROR, getLogger } from "./services/helpers/logger.js";
|
||||
import { NdebitData, NofferData, NmanageRequest } from "@shocknet/clink-sdk";
|
||||
import type { ExtensionLoader } from "./extensions/loader.js"
|
||||
type ExportedCalls = { Stop: () => void, Send: NostrSend, Ping: () => Promise<void>, Reset: (settings: NostrSettings) => void }
|
||||
type ClientEventCallback = (e: { requestId: string }, fromPub: string) => void
|
||||
export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSettings: NostrSettings, onClientEvent: ClientEventCallback): ExportedCalls => {
|
||||
|
||||
export type NostrMiddlewareOptions = {
|
||||
extensionLoader?: ExtensionLoader
|
||||
}
|
||||
|
||||
export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSettings: NostrSettings, onClientEvent: ClientEventCallback, options?: NostrMiddlewareOptions): ExportedCalls => {
|
||||
const log = getLogger({})
|
||||
const nostrTransport = NewNostrTransport(serverMethods, {
|
||||
NostrUserAuthGuard: async (appId, pub) => {
|
||||
|
|
@ -95,6 +101,31 @@ export default (serverMethods: Types.ServerMethods, mainHandler: Main, nostrSett
|
|||
log(ERROR, "authIdentifier does not match", j.authIdentifier || "--", event.pub)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if this is an extension RPC method
|
||||
const extensionLoader = options?.extensionLoader
|
||||
if (extensionLoader && j.rpcName && extensionLoader.hasMethod(j.rpcName)) {
|
||||
// Route to extension
|
||||
log(`[Nostr] Routing to extension method: ${j.rpcName}`)
|
||||
extensionLoader.callMethod(j.rpcName, j.body || {}, event.appId, event.pub)
|
||||
.then(result => {
|
||||
const response = { status: 'OK', requestId: j.requestId, ...result }
|
||||
nostr.Send(
|
||||
{ type: 'app', appId: event.appId },
|
||||
{ type: 'content', pub: event.pub, content: JSON.stringify(response) }
|
||||
)
|
||||
})
|
||||
.catch(err => {
|
||||
log(ERROR, `Extension method ${j.rpcName} failed:`, err.message)
|
||||
const response = { status: 'ERROR', requestId: j.requestId, reason: err.message }
|
||||
nostr.Send(
|
||||
{ type: 'app', appId: event.appId },
|
||||
{ type: 'content', pub: event.pub, content: JSON.stringify(response) }
|
||||
)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
nostrTransport({ ...j, appId: event.appId }, res => {
|
||||
nostr.Send({ type: 'app', appId: event.appId }, { type: 'content', pub: event.pub, content: JSON.stringify({ ...res, requestId: j.requestId }) })
|
||||
}, event.startAtNano, event.startAtMs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue