feat: route Nostr RPC to extension methods
Initialize extension system before nostrMiddleware so registered RPC methods are available. Extension methods (e.g. withdraw.createLink) are intercepted and routed to the extension loader before falling through to the standard nostrTransport. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c429b53f39
commit
f1d8eb5383
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