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: add support for configurable persistence postgres methods in Didcomm Mediator #10

Merged
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
55 changes: 36 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@

At the moment, all configuration is done by environment variables. All of them are optional

| Variable | Description | Default value |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| AGENT_NAME | Label to show to other DIDComm agents | Test Cloud Agent |
| AGENT_ENDPOINTS | Comma-separated public endpoint list where agent DIDComm endpoints will be accessible (including protocol and port) | ws://localhost:4000 |
| AGENT_PUBLIC_DID | Agent's public DID (in did:web format) | None |
| AGENT_PORT | Port where DIDComm agent will be running | 4000 |
| AGENT_LOG_LEVEL | Agent log level | 2 (debug) |
| HTTP_SUPPORT | Enable support of incoming DIDComm messages through HTTP transport | true |
| WS_SUPPORT | Enable support of incoming DIDComm messages through WebSocket transport | true |
| WALLET_NAME | Wallet (database) name | test-cloud-agent |
| WALLET_KEY | Wallet base encryption key | 'Test Cloud Agent' |
| KEY_DERIVATION_METHOD | Wallet key derivation method | ARGON2I_MOD |
| POSTGRES_HOST | PosgreSQL database host | None (use SQLite) |
| POSTGRES_USER | PosgreSQL database username | None |
| POSTGRES_PASSWORD | PosgreSQL database password | None |
| POSTGRES_ADMIN_USER | PosgreSQL database admin user | None |
| POSTGRES_ADMIN_PASSWORD | PosgreSQL database admin password | None |
| MPR_WS_URL | Message Pickup Repository server WebSocket URL. If not defined, it will use internal Message Pickup management (for single-instance, local development only). | none |
| MPR_MAX_RECEIVE_BYTES | Message Pickup Repository Optional byte size limit for retrieving messages | none |
| Variable | Description | Default value |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| AGENT_NAME | Label to show to other DIDComm agents | Test Cloud Agent |
| AGENT_ENDPOINTS | Comma-separated public endpoint list where agent DIDComm endpoints will be accessible (including protocol and port) | ws://localhost:4000 |
| AGENT_PUBLIC_DID | Agent's public DID (in did:web format) | None |
| AGENT_PORT | Port where DIDComm agent will be running | 4000 |
| AGENT_LOG_LEVEL | Agent log level | 2 (debug) |
| HTTP_SUPPORT | Enable support of incoming DIDComm messages through HTTP transport | true |
| WS_SUPPORT | Enable support of incoming DIDComm messages through WebSocket transport | true |
| WALLET_NAME | Wallet (database) name | test-cloud-agent |
| WALLET_KEY | Wallet base encryption key | 'Test Cloud Agent' |
| KEY_DERIVATION_METHOD | Wallet key derivation method | ARGON2I_MOD |
| POSTGRES_HOST | PosgreSQL database host | None (use SQLite) |
| POSTGRES_USER | PosgreSQL database username | None |
| POSTGRES_PASSWORD | PosgreSQL database password | None |
| POSTGRES_ADMIN_USER | PosgreSQL database admin user | None |
| POSTGRES_ADMIN_PASSWORD | PosgreSQL database admin password | None |
| MPR_WS_URL | Message Pickup Repository server WebSocket URL. If not defined, it will use internal Message Pickup management (for single-instance, local development only). | none |
| MPR_MAX_RECEIVE_BYTES | Message Pickup Repository Optional byte size limit for retrieving messages | none |
| FIREBASE_CFG_FILE | Defines the path to the Firebase project configuration file used to initialize the Firebase Admin SDK. This file must be a JSON file containing the service account credentials for the Firebase project. If the variable is not set, Firebase-based notifications will be disabled. This applies to both the PostgresMessagePickupRepository and InMemoryMessagePickupRepository modes. | `./firebase.cfg.json` |

These variables might be set also in `.env` file in the form of KEY=VALUE (one per line).

Expand Down Expand Up @@ -95,3 +96,19 @@ server IP-HOST:4002;
```bash
docker compose -f docker-compose-lb.yml up --build
```

## Didcomm Mediator: Configurable Message Pickup Repository

The Didcomm Mediator now supports flexible configuration for message pickup repositories, allowing users to choose between different persistence methods depending on their needs. This enhancement provides seamless integration with the following repository options:

- MessagePickupRepositoryClient: A WebSocket-based repository for distributed environments.
- PostgresMessagePickupRepository: A PostgreSQL-based repository for persistent storage. It is meant for simplicity, so it uses the same Postgres host than mediator's wallet.
- InMemoryMessagePickupRepository: An in-memory repository for lightweight setups or testing purposes. It only works when SQLite is used for mediator wallet.

### How to configure

The repository configuration is controlled by these environment variables. The mediator will automatically detect the active variable and initialize the appropriate repository.

1. WebSocket-Based Repository (MessagePickupRepositoryClient): Set the `MPR_WS_URL` environment variable to the WebSocket server URL.
2. PostgreSQL-Based Repository (PostgresMessagePickupRepository): Set the `POSTGRES_HOST` environment variable to the PostgreSQL connection string and `MPR_WS_URL` is null
3. In-Memory Repository (InMemoryMessagePickupRepository): If neither `MPR_WS_URL` and `POSTGRES_HOST` is set, the mediator will default to InMemoryMessagePickupRepository.
10 changes: 7 additions & 3 deletions docker-compose-lb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
ports:
- 5432:5432
volumes:
- ~/data/postgres:/var/lib/postgresql/data
- ~/data/dm/postgres:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=cloud-agent
- POSTGRES_USER=cloud-agent
Expand All @@ -26,7 +26,9 @@ services:
- POSTGRES_USER=cloud-agent
- POSTGRES_PASSWORD=cloud-agent
- KEY_DERIVATION_METHOD=ARGON2I_MOD
- MPR_WS_URL=ws://192.168.10.13:3100
#- MPR_WS_URL=ws://192.168.10.13:3100
#- MAX_RECEIVE_BYTES=1000000
- FCM_SERVICE_BASE_URL=http://192.168.100.84:3000/test
ports:
- 3001:3000
- 4001:4000
Expand All @@ -52,7 +54,9 @@ services:
- POSTGRES_USER=cloud-agent
- POSTGRES_PASSWORD=cloud-agent
- KEY_DERIVATION_METHOD=ARGON2I_MOD
- MPR_WS_URL=ws://192.168.10.13:3100
#- MPR_WS_URL=ws://192.168.10.13:3100
#- MAX_RECEIVE_BYTES=1000000
- FCM_SERVICE_BASE_URL=http://192.168.100.84:3000/test
restart: always
ports:
- 3002:3000
Expand Down
18 changes: 14 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
ports:
- 5432:5432
volumes:
- ~/data/postgres:/var/lib/postgresql/data
- ~/data/dm/postgres:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=cloud-agent
- POSTGRES_USER=cloud-agent
Expand All @@ -26,13 +26,23 @@ services:
- POSTGRES_USER=cloud-agent
- POSTGRES_PASSWORD=cloud-agent
- KEY_DERIVATION_METHOD=ARGON2I_MOD
- MPR_WS_URL=ws://192.168.10.13:3100
- FIREBASE_CFG_FILE=/config/firebase-cfg.json
#- MPR_WS_URL=ws://192.168.10.13:3100
ports:
- 3001:3000
- 4001:4000
- 3000:3000
- 4000:4000
volumes:
- .afj:/root/.afj
- ./firebase-cfg.json:/config/firebase-cfg.json
depends_on:
- postgres
links:
- postgres

adminer:
image: adminer
restart: always
ports:
- 8080:8080
depends_on:
- postgres
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@2060.io/credo-ts-message-pickup-repository-pg": "^0.0.7",
"@2060.io/message-pickup-repository-client": "^0.0.7",
"@credo-ts/askar": "0.5.11",
"@credo-ts/core": "0.5.11",
Expand Down
4 changes: 4 additions & 0 deletions src/agent/CloudAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export interface CloudAgentOptions {
dependencies: AgentDependencies
messagePickupRepositoryWebSocketUrl?: string
messagePickupMaxReceiveBytes?: number
postgresUser?: string
postgresPassword?: string
postgresHost?: string
postgresMessagePickupDatabaseName?: string
}

export const createCloudAgent = (
Expand Down
51 changes: 48 additions & 3 deletions src/agent/initCloudAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
MediationStateChangedEvent,
RoutingEventTypes,
HangupMessage,
MessagePickupRepository,
} from '@credo-ts/core'
import WebSocket from 'ws'
import { Socket } from 'net'
Expand All @@ -42,16 +43,37 @@ import { InMemoryMessagePickupRepository } from '../storage/InMemoryMessagePicku
import { LocalFcmNotificationSender } from '../notifications/LocalFcmNotificationSender'
import { MessagePickupRepositoryClient } from '@2060.io/message-pickup-repository-client'
import { ConnectionInfo } from '@2060.io/message-pickup-repository-client/build/interfaces'
gabrielmatau79 marked this conversation as resolved.
Show resolved Hide resolved
import { PostgresMessagePickupRepository } from '@2060.io/credo-ts-message-pickup-repository-pg'

export const initCloudAgent = async (config: CloudAgentOptions) => {
const logger = config.config.logger ?? new ConsoleLogger(LogLevel.off)
const publicDid = config.did

const messageRepository = config.messagePickupRepositoryWebSocketUrl
? new MessagePickupRepositoryClient({
const createMessagePickupRepository = (): MessagePickupRepository => {
if (config.messagePickupRepositoryWebSocketUrl) {
return new MessagePickupRepositoryClient({
url: config.messagePickupRepositoryWebSocketUrl,
})
: new InMemoryMessagePickupRepository(new LocalFcmNotificationSender(logger), logger)
} else if (config.postgresHost) {
const { postgresUser, postgresPassword, postgresHost } = config

if (!postgresUser || !postgresPassword) {
throw new Error(
'[createMessagePickupRepository] Both postgresUser and postgresPassword are required when using PostgresMessagePickupRepository.'
)
}
return new PostgresMessagePickupRepository({
logger: logger,
postgresUser,
postgresPassword,
postgresHost,
})
} else {
return new InMemoryMessagePickupRepository(new LocalFcmNotificationSender(logger), logger)
}
}

const messageRepository = createMessagePickupRepository()

if (!config.enableHttp && !config.enableWs) {
throw new Error('No transport has been enabled. Set at least one of HTTP and WS')
Expand Down Expand Up @@ -93,6 +115,29 @@ export const initCloudAgent = async (config: CloudAgentOptions) => {
})
} else if (messageRepository instanceof InMemoryMessagePickupRepository) {
messageRepository.setAgent(agent)
} else if (messageRepository instanceof PostgresMessagePickupRepository) {
// Define function that use to send push notification.

const localFcmNotificationSender = new LocalFcmNotificationSender(logger)

const connectionInfoCallback = async (connectionId: string) => {
const connectionRecord = await agent.connections.findById(connectionId)

const token = connectionRecord?.getTag('device_token') as string | null

return {
sendPushNotification:
token && localFcmNotificationSender.isInitialized()
? async (messageId: string) => {
await localFcmNotificationSender.sendMessage(token, messageId)
}
: undefined,
}
}
await messageRepository.initialize({
agent,
connectionInfoCallback,
})
}

const app = express()
Expand Down
Loading
Loading