Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: socketdock implementation #26

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ POSTGRES_ADMIN_USER=
POSTGRES_ADMIN_PASSWORD=

USE_PUSH_NOTIFICATIONS='true'
NOTIFICATION_WEBHOOK_URL=
NOTIFICATION_WEBHOOK_URL=
USE_SOCKETDOCK='false'
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

1 change: 0 additions & 1 deletion dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ void connect({
// eslint-disable-next-line no-console
console.log('Got ngrok url:', app.url())
const url = app.url()

process.env.NODE_ENV = 'development'
process.env.AGENT_PORT = `${port}`
process.env.AGENT_ENDPOINTS = `${url},${url?.replace('http', 'ws')}`
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
"postinstall": "npx patch-package"
},
"dependencies": {
"@credo-ts/askar": "0.5.6",
"@credo-ts/core": "0.5.6",
"@credo-ts/node": "0.5.6",
"@hyperledger/aries-askar-nodejs": "0.2.1",
"@credo-ts/askar": "^0.5.0",
"@credo-ts/core": "^0.5.0",
"@credo-ts/node": "^0.5.0",
"@hyperledger/aries-askar-nodejs": "^0.2.0",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"prettier": "^2.8.4",
Expand Down
27 changes: 20 additions & 7 deletions src/agent.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { AskarModule, AskarMultiWalletDatabaseScheme } from '@credo-ts/askar'
import {
Agent,
CacheModule,
ConnectionsModule,
DidCommMimeType,
HttpOutboundTransport,
InMemoryLruCache,
MediatorModule,
OutOfBandRole,
OutOfBandState,
Expand All @@ -18,8 +16,18 @@ import type { Socket } from 'net'

import express from 'express'
import { Server } from 'ws'
import { SocketDockInboundTransport } from './transport/SocketDockInboundTransport'

import { AGENT_ENDPOINTS, AGENT_NAME, AGENT_PORT, LOG_LEVEL, POSTGRES_HOST, WALLET_KEY, WALLET_NAME } from './constants'
import {
AGENT_ENDPOINTS,
AGENT_NAME,
AGENT_PORT,
LOG_LEVEL,
POSTGRES_HOST,
USE_SOCKETDOCK,
WALLET_KEY,
WALLET_NAME,
} from './constants'
import { askarPostgresConfig } from './database'
import { Logger } from './logger'
import { StorageMessageQueueModule } from './storage/StorageMessageQueueModule'
Expand Down Expand Up @@ -92,14 +100,19 @@ export async function createAgent() {
// Create all transports
const httpInboundTransport = new HttpInboundTransport({ app, port: AGENT_PORT })
const httpOutboundTransport = new HttpOutboundTransport()
const wsInboundTransport = new WsInboundTransport({ server: socketServer })
const wsOutboundTransport = new WsOutboundTransport()

// Register all Transports
agent.registerInboundTransport(httpInboundTransport)
agent.registerOutboundTransport(httpOutboundTransport)
agent.registerInboundTransport(wsInboundTransport)
agent.registerOutboundTransport(wsOutboundTransport)

if (USE_SOCKETDOCK === 'false') {
pallavighule marked this conversation as resolved.
Show resolved Hide resolved
const wsInboundTransport = new WsInboundTransport({ server: socketServer })
const wsOutboundTransport = new WsOutboundTransport()
agent.registerInboundTransport(wsInboundTransport)
agent.registerOutboundTransport(wsOutboundTransport)
} else {
agent.registerInboundTransport(new SocketDockInboundTransport({ app }))
}

// Added health check endpoint
httpInboundTransport.app.get('/health', async (_req, res) => {
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const POSTGRES_ADMIN_PASSWORD = process.env.POSTGRES_ADMIN_PASSWORD

export const INVITATION_URL = process.env.INVITATION_URL

export const USE_SOCKETDOCK = process.env.USE_SOCKETDOCK || 'false'

export const LOG_LEVEL = LogLevel.debug

export const IS_DEV = process.env.NODE_ENV === 'development'
Expand Down
91 changes: 91 additions & 0 deletions src/transport/SocketDockInboundTransport.ts
pallavighule marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type { Express } from 'express'
import { Agent, AgentEventTypes, AgentMessageReceivedEvent, InboundTransport, TransportService } from '@credo-ts/core'
import { SocketDockTransportSession } from './SocketDockTransportSession'
import express from 'express'

export class SocketDockInboundTransport implements InboundTransport {
private app: Express
private activeConnections: Record<string, string> = {}

public constructor({ app }: { app: Express }) {
this.app = app

this.app.use(express.json())
}

public async start(agent: Agent) {
this.app.post('/connect', async (req, res) => {
agent.config.logger.info('SocketDockInboundTransport.connect')
const { connection_id: connectionId } = req.body.meta
if (!connectionId) {
throw new Error('ConnectionId is not sent from socketDock server')
}

const socketId = this.activeConnections[connectionId]
if (!socketId) {
this.activeConnections[connectionId] = connectionId
agent.config.logger.debug(`Saving new socketId : ${connectionId}`)
}

try {
res.status(200).send(`connection with socketId : ${connectionId} added successfully`)
} catch (error) {
res.status(500).send('Error sending response to send URL')
}
})

this.app.post('/message', async (req, res) => {
agent.config.logger.info('SocketDockInboundTransport.message')

const { connection_id: connectionId } = req.body.meta
if (!connectionId) {
throw new Error('ConnectionId is not sent from socketDock server')
}

try {
const socketId = this.activeConnections[connectionId]
agent.config.logger.debug(`activeConnections transport session : ${socketId} ${connectionId}`)
const sendUrl = req.body.meta.send
const requestMimeType = req.headers['content-type'] as string
const session = new SocketDockTransportSession(socketId, res, sendUrl, requestMimeType)
const message = req.body.message
const encryptedMessage = JSON.parse(message)

agent.config.logger.debug(`Session value for transport session : ${session.id}`)

agent.events.emit<AgentMessageReceivedEvent>(agent.context, {
type: AgentEventTypes.AgentMessageReceived,
payload: {
message: encryptedMessage,
session: session,
},
})
} catch (error) {
if (!res.headersSent) {
res.status(500).send('Error processing message')
}
}
})

this.app.post('/disconnect', async (req, res) => {
agent.config.logger.info('SocketDockInboundTransport.disconnect')
const transportService = agent.dependencyManager.resolve(TransportService)
const { connection_id } = req.body
if (!connection_id) {
throw new Error('ConnectionId is not sent from socketDock server')
}

delete this.activeConnections[connection_id]
const session = transportService.findSessionById(connection_id)
agent.config.logger.debug(`Got session from transportService ${session?.id}`)
if (session) {
transportService.removeSession(session)
}

agent.config.logger.debug(`removed mapping of socketId : ${connection_id}`)
res.status(200).send(`Mapping with socketId : ${connection_id} removed successfully`)
})
}

public async stop(): Promise<void> {}
}
50 changes: 50 additions & 0 deletions src/transport/SocketDockTransportSession.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { AgentContext, DidCommMimeType, EncryptedMessage, TransportSession } from '@credo-ts/core'
import type { Response } from 'express'
import { CredoError } from '@credo-ts/core'

import { agentDependencies } from '@credo-ts/node'

export const supportedContentTypes: string[] = [DidCommMimeType.V0, DidCommMimeType.V1]

export class SocketDockTransportSession implements TransportSession {
public id: string
public readonly type = 'socketdock'
pallavighule marked this conversation as resolved.
Show resolved Hide resolved
public res: Response
public sendUrl: string
public requestMimeType: any

public constructor(id: string, res: Response, sendUrl: string, requestMimeType: string) {
this.id = id
this.res = res
this.sendUrl = sendUrl
this.requestMimeType = requestMimeType
}

public async close() {
if (!this.res.headersSent) {
this.res.status(200).end()
}
}

public async send(agentContext: AgentContext, encryptedMessage: EncryptedMessage): Promise<void> {
if (this.res.headersSent) {
throw new CredoError(`${this.type} transport session has been closed.`)
}

// By default we take the agent config's default DIDComm content-type
let responseMimeType = agentContext.config.didCommMimeType

if (this.requestMimeType && supportedContentTypes.includes(this.requestMimeType)) {
responseMimeType = this.requestMimeType
}

const requestOptions = {
method: 'POST',
headers: {
'Content-Type': responseMimeType,
},
body: JSON.stringify(encryptedMessage),
}
await agentDependencies.fetch(this.sendUrl, requestOptions)
}
}
3 changes: 2 additions & 1 deletion tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
"emitDecoratorMetadata": true,
"skipLibCheck": true
},
"exclude": [
"node_modules",
Expand Down
Loading