mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
setup gradido node
This commit is contained in:
parent
f962baf1a1
commit
3bdc99b203
1
dlt-connector/.gitignore
vendored
1
dlt-connector/.gitignore
vendored
@ -7,3 +7,4 @@ package-json.lock
|
|||||||
coverage
|
coverage
|
||||||
# emacs
|
# emacs
|
||||||
*~
|
*~
|
||||||
|
gradido_node
|
||||||
@ -13,6 +13,7 @@
|
|||||||
"@sinclair/typemap": "^0.10.1",
|
"@sinclair/typemap": "^0.10.1",
|
||||||
"@types/bun": "^1.2.17",
|
"@types/bun": "^1.2.17",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
|
"async-mutex": "^0.5.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"elysia": "1.3.8",
|
"elysia": "1.3.8",
|
||||||
"graphql-request": "^7.2.0",
|
"graphql-request": "^7.2.0",
|
||||||
@ -306,6 +307,8 @@
|
|||||||
|
|
||||||
"async-limiter": ["async-limiter@1.0.1", "", {}, "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="],
|
"async-limiter": ["async-limiter@1.0.1", "", {}, "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="],
|
||||||
|
|
||||||
|
"async-mutex": ["async-mutex@0.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA=="],
|
||||||
|
|
||||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||||
|
|
||||||
"atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
|
"atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
"@sinclair/typemap": "^0.10.1",
|
"@sinclair/typemap": "^0.10.1",
|
||||||
"@types/bun": "^1.2.17",
|
"@types/bun": "^1.2.17",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
|
"async-mutex": "^0.5.0",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"elysia": "1.3.8",
|
"elysia": "1.3.8",
|
||||||
"graphql-request": "^7.2.0",
|
"graphql-request": "^7.2.0",
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { SendToHieroContext } from '../interactions/sendToHiero/SendToHiero.cont
|
|||||||
import { Community, communitySchema } from '../schemas/transaction.schema'
|
import { Community, communitySchema } from '../schemas/transaction.schema'
|
||||||
import { isPortOpenRetry } from '../utils/network'
|
import { isPortOpenRetry } from '../utils/network'
|
||||||
import { type AppContext, type AppContextClients } from './appContext'
|
import { type AppContext, type AppContextClients } from './appContext'
|
||||||
|
import { initGradidoNode } from './initGradidoNode'
|
||||||
|
|
||||||
export function loadConfig(): Logger {
|
export function loadConfig(): Logger {
|
||||||
// configure log4js
|
// configure log4js
|
||||||
@ -74,6 +75,9 @@ export async function checkGradidoNode(
|
|||||||
logger: Logger,
|
logger: Logger,
|
||||||
homeCommunity: Community,
|
homeCommunity: Community,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
// check if gradido node is running, if not setup and start it
|
||||||
|
await initGradidoNode(clients)
|
||||||
|
|
||||||
// ask gradido node if community blockchain was created
|
// ask gradido node if community blockchain was created
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
|
|||||||
89
dlt-connector/src/bootstrap/initGradidoNode.ts
Normal file
89
dlt-connector/src/bootstrap/initGradidoNode.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import { execSync } from 'node:child_process'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { gunzipSync } from 'node:zlib'
|
||||||
|
import { getLogger } from 'log4js'
|
||||||
|
import { exportCommunities } from '../client/GradidoNode/communities'
|
||||||
|
import { GradidoNodeProcess } from '../client/GradidoNode/GradidoNodeProcess'
|
||||||
|
import { HieroClient } from '../client/hiero/HieroClient'
|
||||||
|
import { CONFIG } from '../config'
|
||||||
|
import {
|
||||||
|
GRADIDO_NODE_HOME_FOLDER_NAME,
|
||||||
|
GRADIDO_NODE_RUNTIME_PATH,
|
||||||
|
LOG4JS_BASE_CATEGORY,
|
||||||
|
} from '../config/const'
|
||||||
|
import { checkFileExist, checkPathExist } from '../utils/filesystem'
|
||||||
|
import { isPortOpen } from '../utils/network'
|
||||||
|
import { AppContextClients } from './appContext'
|
||||||
|
|
||||||
|
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.bootstrap.initGradidoNode`)
|
||||||
|
|
||||||
|
export async function initGradidoNode(clients: AppContextClients): Promise<void> {
|
||||||
|
const url = `http://localhost:${CONFIG.DLT_NODE_SERVER_PORT}`
|
||||||
|
const isOpen = await isPortOpen(url)
|
||||||
|
if (isOpen) {
|
||||||
|
logger.info(`GradidoNode is already running on ${url}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const gradidoNodeHomeFolder = path.join(
|
||||||
|
CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
||||||
|
GRADIDO_NODE_HOME_FOLDER_NAME,
|
||||||
|
)
|
||||||
|
// check folder, create when missing
|
||||||
|
checkPathExist(gradidoNodeHomeFolder, true)
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
// write Hedera Address Book
|
||||||
|
exportHederaAddressbooks(gradidoNodeHomeFolder, clients.hiero),
|
||||||
|
// check GradidoNode Runtime, download when missing
|
||||||
|
ensureGradidoNodeRuntimeAvailable(GRADIDO_NODE_RUNTIME_PATH),
|
||||||
|
// export communities to GradidoNode Folder
|
||||||
|
exportCommunities(gradidoNodeHomeFolder, clients.backend),
|
||||||
|
])
|
||||||
|
GradidoNodeProcess.getInstance().start()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exportHederaAddressbooks(
|
||||||
|
homeFolder: string,
|
||||||
|
hieroClient: HieroClient,
|
||||||
|
): Promise<void> {
|
||||||
|
const networkName = CONFIG.HIERO_HEDERA_NETWORK
|
||||||
|
const addressBook = await hieroClient.downloadAddressBook()
|
||||||
|
const addressBookPath = path.join(homeFolder, 'addressbook', `${networkName}.pb`)
|
||||||
|
checkPathExist(path.dirname(addressBookPath), true)
|
||||||
|
fs.writeFileSync(addressBookPath, addressBook.toBytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureGradidoNodeRuntimeAvailable(runtimeFileName: string): Promise<void> {
|
||||||
|
const runtimeFolder = path.dirname(runtimeFileName)
|
||||||
|
checkPathExist(runtimeFolder, true)
|
||||||
|
logger.debug(`GradidoNode Runtime: ${runtimeFileName}`)
|
||||||
|
if (!checkFileExist(runtimeFileName)) {
|
||||||
|
const runtimeArchiveFilename = createGradidoNodeRuntimeArchiveFilename()
|
||||||
|
const downloadUrl = new URL(
|
||||||
|
`https://github.com/gradido/gradido_node/releases/download/v${CONFIG.DLT_GRADIDO_NODE_SERVER_VERSION}/${runtimeArchiveFilename}`,
|
||||||
|
)
|
||||||
|
logger.debug(`download GradidoNode Runtime from ${downloadUrl}`)
|
||||||
|
const archive = await fetch(downloadUrl)
|
||||||
|
if (!archive.ok) {
|
||||||
|
throw new Error(`Failed to download GradidoNode Runtime: ${archive.statusText}`)
|
||||||
|
}
|
||||||
|
const compressedBuffer = await archive.arrayBuffer()
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
fs.writeFileSync(runtimeFileName, gunzipSync(Buffer.from(compressedBuffer)))
|
||||||
|
} else {
|
||||||
|
const archivePath = path.join(runtimeFolder, runtimeArchiveFilename)
|
||||||
|
logger.debug(`GradidoNode Runtime Archive: ${archivePath}`)
|
||||||
|
fs.writeFileSync(archivePath, Buffer.from(compressedBuffer))
|
||||||
|
execSync(`tar -xzf ${archivePath}`, { cwd: runtimeFolder })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createGradidoNodeRuntimeArchiveFilename(): string {
|
||||||
|
const version = CONFIG.DLT_GRADIDO_NODE_SERVER_VERSION
|
||||||
|
const platform: string = process.platform
|
||||||
|
const fileEnding = platform === 'win32' ? 'zip' : 'tar.gz'
|
||||||
|
return `gradido_node-v${version}-${platform}-${process.arch}.${fileEnding}`
|
||||||
|
}
|
||||||
@ -40,7 +40,7 @@ export class GradidoNodeClient {
|
|||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.GradidoNodeClient`)
|
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.GradidoNodeClient`)
|
||||||
this.urlValue = `http://localhost:${CONFIG.DLT_NODE_SERVER_PORT}`
|
this.urlValue = `http://localhost:${CONFIG.DLT_NODE_SERVER_PORT}/api`
|
||||||
this.logger.addContext('url', this.urlValue)
|
this.logger.addContext('url', this.urlValue)
|
||||||
this.client = new JsonRpcClient({
|
this.client = new JsonRpcClient({
|
||||||
url: this.urlValue,
|
url: this.urlValue,
|
||||||
|
|||||||
@ -52,12 +52,13 @@ export class GradidoNodeProcess {
|
|||||||
env: {
|
env: {
|
||||||
CLIENTS_HIERO_NETWORKTYPE: CONFIG.HIERO_HEDERA_NETWORK,
|
CLIENTS_HIERO_NETWORKTYPE: CONFIG.HIERO_HEDERA_NETWORK,
|
||||||
SERVER_JSON_RPC_PORT: CONFIG.DLT_NODE_SERVER_PORT.toString(),
|
SERVER_JSON_RPC_PORT: CONFIG.DLT_NODE_SERVER_PORT.toString(),
|
||||||
|
HOME: CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
||||||
},
|
},
|
||||||
onExit(proc, exitCode, signalCode, error) {
|
onExit(proc, exitCode, signalCode, error) {
|
||||||
logger.warn(`GradidoNodeProcess exited with code ${exitCode} and signalCode ${signalCode}`)
|
logger.warn(`GradidoNodeProcess exited with code ${exitCode} and signalCode ${signalCode}`)
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.error(`GradidoNodeProcess exit error: ${error}`)
|
logger.error(`GradidoNodeProcess exit error: ${error}`)
|
||||||
if (logger.isDebugEnabled() && proc.stderr) {
|
/*if (logger.isDebugEnabled() && proc.stderr) {
|
||||||
// print error messages from GradidoNode in our own log if debug is enabled
|
// print error messages from GradidoNode in our own log if debug is enabled
|
||||||
proc.stderr
|
proc.stderr
|
||||||
.getReader()
|
.getReader()
|
||||||
@ -65,9 +66,9 @@ export class GradidoNodeProcess {
|
|||||||
.then((chunk) => {
|
.then((chunk) => {
|
||||||
logger.debug(chunk.value?.toString())
|
logger.debug(chunk.value?.toString())
|
||||||
})
|
})
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
logger.debug(`ressource usage: ${proc?.resourceUsage()}`)
|
logger.debug(`ressource usage: ${JSON.stringify(proc?.resourceUsage(), null, 2)}`)
|
||||||
const gradidoNodeProcess = GradidoNodeProcess.getInstance()
|
const gradidoNodeProcess = GradidoNodeProcess.getInstance()
|
||||||
gradidoNodeProcess.proc = null
|
gradidoNodeProcess.proc = null
|
||||||
if (
|
if (
|
||||||
@ -80,8 +81,10 @@ export class GradidoNodeProcess {
|
|||||||
gradidoNodeProcess.start()
|
gradidoNodeProcess.start()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
stdout: 'ignore',
|
/*stdout: 'ignore',
|
||||||
stderr: logger.isDebugEnabled() ? 'pipe' : 'ignore',
|
stderr: logger.isDebugEnabled() ? 'pipe' : 'ignore',*/
|
||||||
|
stdout: 'inherit',
|
||||||
|
stderr: 'inherit',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
84
dlt-connector/src/client/GradidoNode/communities.ts
Normal file
84
dlt-connector/src/client/GradidoNode/communities.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { Mutex } from 'async-mutex'
|
||||||
|
import { getLogger } from 'log4js'
|
||||||
|
import { CONFIG } from '../../config'
|
||||||
|
import { GRADIDO_NODE_HOME_FOLDER_NAME, LOG4JS_BASE_CATEGORY } from '../../config/const'
|
||||||
|
import { HieroId } from '../../schemas/typeGuard.schema'
|
||||||
|
import { checkFileExist, checkPathExist } from '../../utils/filesystem'
|
||||||
|
import { BackendClient } from '../backend/BackendClient'
|
||||||
|
import { GradidoNodeProcess } from './GradidoNodeProcess'
|
||||||
|
|
||||||
|
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.GradidoNode.communities`)
|
||||||
|
const ensureCommunitiesAvailableMutex: Mutex = new Mutex()
|
||||||
|
|
||||||
|
// prototype, later add api call to gradido dlt node server for adding/updating communities
|
||||||
|
type CommunityForDltNodeServer = {
|
||||||
|
communityId: string
|
||||||
|
hieroTopicId: string
|
||||||
|
alias: string
|
||||||
|
folder: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ensureCommunitiesAvailable(communityTopicIds: HieroId[]): Promise<void> {
|
||||||
|
const release = await ensureCommunitiesAvailableMutex.acquire()
|
||||||
|
try {
|
||||||
|
const homeFolder = path.join(
|
||||||
|
CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
||||||
|
GRADIDO_NODE_HOME_FOLDER_NAME,
|
||||||
|
)
|
||||||
|
if (!checkCommunityAvailable(communityTopicIds, homeFolder)) {
|
||||||
|
await exportCommunities(homeFolder, BackendClient.getInstance())
|
||||||
|
return GradidoNodeProcess.getInstance().restart()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function exportCommunities(homeFolder: string, client: BackendClient): Promise<void> {
|
||||||
|
const communities = await client.getReachableCommunities()
|
||||||
|
const communitiesPath = path.join(homeFolder, 'communities.json')
|
||||||
|
checkPathExist(path.dirname(communitiesPath), true)
|
||||||
|
// make sure communityName is unique
|
||||||
|
const communityName = new Set<string>()
|
||||||
|
const communitiesForDltNodeServer: CommunityForDltNodeServer[] = []
|
||||||
|
for (const com of communities) {
|
||||||
|
if (!com.uuid || !com.hieroTopicId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// use name as alias if not empty and unique, otherwise use uuid
|
||||||
|
let alias = com.name
|
||||||
|
if (!alias || communityName.has(alias)) {
|
||||||
|
alias = com.uuid
|
||||||
|
}
|
||||||
|
communityName.add(alias)
|
||||||
|
communitiesForDltNodeServer.push({
|
||||||
|
communityId: com.uuid,
|
||||||
|
hieroTopicId: com.hieroTopicId,
|
||||||
|
alias,
|
||||||
|
// use only alpha-numeric chars for folder name
|
||||||
|
folder: alias.replace(/[^a-zA-Z0-9]/g, '_'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fs.writeFileSync(communitiesPath, JSON.stringify(communitiesForDltNodeServer, null, 2))
|
||||||
|
logger.info(`exported ${communitiesForDltNodeServer.length} communities to ${communitiesPath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkCommunityAvailable(communityTopicIds: HieroId[], homeFolder: string): boolean {
|
||||||
|
const communitiesPath = path.join(homeFolder, 'communities.json')
|
||||||
|
if (!checkFileExist(communitiesPath)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const communities = JSON.parse(fs.readFileSync(communitiesPath, 'utf-8'))
|
||||||
|
let foundCount = 0
|
||||||
|
for (const community of communities) {
|
||||||
|
if (communityTopicIds.includes(community.hieroTopicId)) {
|
||||||
|
foundCount++
|
||||||
|
if (foundCount >= communityTopicIds.length) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@ -5,7 +5,11 @@ import * as v from 'valibot'
|
|||||||
import { CONFIG } from '../../config'
|
import { CONFIG } from '../../config'
|
||||||
import { LOG4JS_BASE_CATEGORY } from '../../config/const'
|
import { LOG4JS_BASE_CATEGORY } from '../../config/const'
|
||||||
import { HieroId, Uuidv4 } from '../../schemas/typeGuard.schema'
|
import { HieroId, Uuidv4 } from '../../schemas/typeGuard.schema'
|
||||||
import { homeCommunityGraphqlQuery, setHomeCommunityTopicId } from './graphql'
|
import {
|
||||||
|
getReachableCommunities,
|
||||||
|
homeCommunityGraphqlQuery,
|
||||||
|
setHomeCommunityTopicId,
|
||||||
|
} from './graphql'
|
||||||
import { type Community, communitySchema } from './output.schema'
|
import { type Community, communitySchema } from './output.schema'
|
||||||
|
|
||||||
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
|
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
|
||||||
@ -42,7 +46,7 @@ export class BackendClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get url(): string {
|
public get url(): string {
|
||||||
return this.url
|
return this.urlValue
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,6 +88,19 @@ export class BackendClient {
|
|||||||
return v.parse(communitySchema, data.updateHomeCommunity)
|
return v.parse(communitySchema, data.updateHomeCommunity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getReachableCommunities(): Promise<Community[]> {
|
||||||
|
this.logger.info('get reachable communities on backend')
|
||||||
|
const { data, errors } = await this.client.rawRequest<{ reachableCommunities: Community[] }>(
|
||||||
|
getReachableCommunities,
|
||||||
|
{},
|
||||||
|
await this.getRequestHeader(),
|
||||||
|
)
|
||||||
|
if (errors) {
|
||||||
|
throw errors[0]
|
||||||
|
}
|
||||||
|
return v.parse(v.array(communitySchema), data.reachableCommunities)
|
||||||
|
}
|
||||||
|
|
||||||
private async getRequestHeader(): Promise<{
|
private async getRequestHeader(): Promise<{
|
||||||
authorization: string
|
authorization: string
|
||||||
}> {
|
}> {
|
||||||
|
|||||||
@ -4,27 +4,40 @@ import { gql } from 'graphql-request'
|
|||||||
* Schema Definitions for graphql requests
|
* Schema Definitions for graphql requests
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const communityFragment = gql`
|
||||||
|
fragment Community_common on Community {
|
||||||
|
uuid
|
||||||
|
name
|
||||||
|
hieroTopicId
|
||||||
|
foreign
|
||||||
|
creationDate
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
// graphql query for getting home community in tune with community schema
|
// graphql query for getting home community in tune with community schema
|
||||||
export const homeCommunityGraphqlQuery = gql`
|
export const homeCommunityGraphqlQuery = gql`
|
||||||
query {
|
query {
|
||||||
homeCommunity {
|
homeCommunity {
|
||||||
uuid
|
...Community_common
|
||||||
name
|
|
||||||
hieroTopicId
|
|
||||||
foreign
|
|
||||||
creationDate
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${communityFragment}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const setHomeCommunityTopicId = gql`
|
export const setHomeCommunityTopicId = gql`
|
||||||
mutation ($uuid: String!, $hieroTopicId: String){
|
mutation ($uuid: String!, $hieroTopicId: String){
|
||||||
updateHomeCommunity(uuid: $uuid, hieroTopicId: $hieroTopicId) {
|
updateHomeCommunity(uuid: $uuid, hieroTopicId: $hieroTopicId) {
|
||||||
uuid
|
...Community_common
|
||||||
name
|
|
||||||
hieroTopicId
|
|
||||||
foreign
|
|
||||||
creationDate
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${communityFragment}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const getReachableCommunities = gql`
|
||||||
|
query {
|
||||||
|
reachableCommunities {
|
||||||
|
...Community_common
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${communityFragment}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
AccountBalance,
|
AccountBalance,
|
||||||
AccountBalanceQuery,
|
AccountBalanceQuery,
|
||||||
|
AddressBookQuery,
|
||||||
Client,
|
Client,
|
||||||
|
FileId,
|
||||||
LocalProvider,
|
LocalProvider,
|
||||||
|
NodeAddressBook,
|
||||||
PrivateKey,
|
PrivateKey,
|
||||||
TopicCreateTransaction,
|
TopicCreateTransaction,
|
||||||
TopicId,
|
TopicId,
|
||||||
@ -166,6 +169,16 @@ export class HieroClient {
|
|||||||
return v.parse(hieroIdSchema, createReceipt.topicId?.toString())
|
return v.parse(hieroIdSchema, createReceipt.topicId?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async downloadAddressBook(): Promise<NodeAddressBook> {
|
||||||
|
const query = new AddressBookQuery().setFileId(FileId.ADDRESS_BOOK)
|
||||||
|
try {
|
||||||
|
return await query.execute(this.client)
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async updateTopic(topicId: HieroId): Promise<void> {
|
public async updateTopic(topicId: HieroId): Promise<void> {
|
||||||
this.logger.addContext('topicId', topicId.toString())
|
this.logger.addContext('topicId', topicId.toString())
|
||||||
let transaction = new TopicUpdateTransaction()
|
let transaction = new TopicUpdateTransaction()
|
||||||
|
|||||||
@ -6,7 +6,16 @@ export const MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_UPDATE = 1000 * 60 * 60 * 24 * 7
|
|||||||
// 10 minutes
|
// 10 minutes
|
||||||
export const MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_SEND_MESSAGE = 1000 * 60 * 10
|
export const MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_SEND_MESSAGE = 1000 * 60 * 10
|
||||||
|
|
||||||
export const GRADIDO_NODE_RUNTIME_PATH = path.join(__dirname, 'gradido_node', 'bin', 'GradidoNode')
|
export const GRADIDO_NODE_RUNTIME_PATH = path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'gradido_node',
|
||||||
|
'bin',
|
||||||
|
'GradidoNode',
|
||||||
|
)
|
||||||
// if last start was less than this time, do not restart
|
// if last start was less than this time, do not restart
|
||||||
export const GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS = 1000 * 30
|
export const GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS = 1000 * 30
|
||||||
export const GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS = 1000
|
export const GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS = 1000
|
||||||
|
// currently hard coded in gradido node, update in future
|
||||||
|
export const GRADIDO_NODE_HOME_FOLDER_NAME = '.gradido'
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import path from 'node:path'
|
||||||
import { MemoryBlock } from 'gradido-blockchain-js'
|
import { MemoryBlock } from 'gradido-blockchain-js'
|
||||||
import * as v from 'valibot'
|
import * as v from 'valibot'
|
||||||
|
|
||||||
@ -78,7 +79,14 @@ export const configSchema = v.object({
|
|||||||
),
|
),
|
||||||
'8340',
|
'8340',
|
||||||
),
|
),
|
||||||
DLT_NODE_SERVER_VERSION: v.optional(v.string('The version of the DLT node server'), '0.9.0'),
|
DLT_GRADIDO_NODE_SERVER_VERSION: v.optional(
|
||||||
|
v.string('The version of the DLT node server'),
|
||||||
|
'0.9.0',
|
||||||
|
),
|
||||||
|
DLT_GRADIDO_NODE_SERVER_HOME_FOLDER: v.optional(
|
||||||
|
v.string('The home folder for the gradido dlt node server'),
|
||||||
|
path.join(__dirname, '..', '..', 'gradido_node'),
|
||||||
|
),
|
||||||
PORT: v.optional(
|
PORT: v.optional(
|
||||||
v.pipe(
|
v.pipe(
|
||||||
v.string('A valid port on which the backend server is running'),
|
v.string('A valid port on which the backend server is running'),
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
} from 'gradido-blockchain-js'
|
} from 'gradido-blockchain-js'
|
||||||
import { getLogger } from 'log4js'
|
import { getLogger } from 'log4js'
|
||||||
import * as v from 'valibot'
|
import * as v from 'valibot'
|
||||||
|
import { ensureCommunitiesAvailable } from '../../client/GradidoNode/communities'
|
||||||
import { HieroClient } from '../../client/hiero/HieroClient'
|
import { HieroClient } from '../../client/hiero/HieroClient'
|
||||||
import { LOG4JS_BASE_CATEGORY } from '../../config/const'
|
import { LOG4JS_BASE_CATEGORY } from '../../config/const'
|
||||||
import { InputTransactionType } from '../../data/InputTransactionType.enum'
|
import { InputTransactionType } from '../../data/InputTransactionType.enum'
|
||||||
@ -40,7 +41,7 @@ const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.interactions.sendToHiero.SendT
|
|||||||
export async function SendToHieroContext(
|
export async function SendToHieroContext(
|
||||||
input: TransactionInput | CommunityInput,
|
input: TransactionInput | CommunityInput,
|
||||||
): Promise<HieroTransactionIdString> {
|
): Promise<HieroTransactionIdString> {
|
||||||
const role = chooseCorrectRole(input)
|
const role = await chooseCorrectRole(input)
|
||||||
const builder = await role.getGradidoTransactionBuilder()
|
const builder = await role.getGradidoTransactionBuilder()
|
||||||
if (builder.isCrossCommunityTransaction()) {
|
if (builder.isCrossCommunityTransaction()) {
|
||||||
// build cross group transaction
|
// build cross group transaction
|
||||||
@ -107,9 +108,13 @@ async function sendViaHiero(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// choose correct role based on transaction type and input type
|
// choose correct role based on transaction type and input type
|
||||||
function chooseCorrectRole(input: TransactionInput | CommunityInput): AbstractTransactionRole {
|
async function chooseCorrectRole(
|
||||||
|
input: TransactionInput | CommunityInput,
|
||||||
|
): Promise<AbstractTransactionRole> {
|
||||||
const communityParsingResult = v.safeParse(communitySchema, input)
|
const communityParsingResult = v.safeParse(communitySchema, input)
|
||||||
if (communityParsingResult.success) {
|
if (communityParsingResult.success) {
|
||||||
|
// make sure gradido node knows community
|
||||||
|
await ensureCommunitiesAvailable([communityParsingResult.output.hieroTopicId])
|
||||||
return new CommunityRootTransactionRole(communityParsingResult.output)
|
return new CommunityRootTransactionRole(communityParsingResult.output)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +126,12 @@ function chooseCorrectRole(input: TransactionInput | CommunityInput): AbstractTr
|
|||||||
})
|
})
|
||||||
throw new Error('invalid input')
|
throw new Error('invalid input')
|
||||||
}
|
}
|
||||||
|
// make sure gradido node knows communities
|
||||||
|
const communityTopicIds = [
|
||||||
|
transactionParsingResult.output.user.communityTopicId,
|
||||||
|
transactionParsingResult.output.linkedUser?.communityTopicId,
|
||||||
|
].filter((id): id is HieroId => id !== undefined)
|
||||||
|
await ensureCommunitiesAvailable(communityTopicIds)
|
||||||
|
|
||||||
const transaction = transactionParsingResult.output
|
const transaction = transactionParsingResult.output
|
||||||
switch (transaction.type) {
|
switch (transaction.type) {
|
||||||
|
|||||||
@ -25,6 +25,12 @@ mock.module('../KeyPairCacheManager', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mock.module('../client/GradidoNode/communities', () => ({
|
||||||
|
ensureCommunitiesAvailable: () => {
|
||||||
|
return Promise.resolve()
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
mock.module('../client/hiero/HieroClient', () => ({
|
mock.module('../client/hiero/HieroClient', () => ({
|
||||||
HieroClient: {
|
HieroClient: {
|
||||||
getInstance: () => ({
|
getInstance: () => ({
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Elysia, status, t } from 'elysia'
|
|||||||
import { AddressType_NONE } from 'gradido-blockchain-js'
|
import { AddressType_NONE } from 'gradido-blockchain-js'
|
||||||
import { getLogger } from 'log4js'
|
import { getLogger } from 'log4js'
|
||||||
import * as v from 'valibot'
|
import * as v from 'valibot'
|
||||||
|
import { ensureCommunitiesAvailable } from '../client/GradidoNode/communities'
|
||||||
import { GradidoNodeClient } from '../client/GradidoNode/GradidoNodeClient'
|
import { GradidoNodeClient } from '../client/GradidoNode/GradidoNodeClient'
|
||||||
import { LOG4JS_BASE_CATEGORY } from '../config/const'
|
import { LOG4JS_BASE_CATEGORY } from '../config/const'
|
||||||
import { KeyPairIdentifierLogic } from '../data/KeyPairIdentifier.logic'
|
import { KeyPairIdentifierLogic } from '../data/KeyPairIdentifier.logic'
|
||||||
@ -125,6 +126,8 @@ async function isAccountExist(identifierAccount: IdentifierAccountInput): Promis
|
|||||||
// check and prepare input
|
// check and prepare input
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
const identifierAccountParsed = v.parse(identifierAccountSchema, identifierAccount)
|
const identifierAccountParsed = v.parse(identifierAccountSchema, identifierAccount)
|
||||||
|
// make sure gradido node knows community
|
||||||
|
await ensureCommunitiesAvailable([identifierAccountParsed.communityTopicId])
|
||||||
const accountKeyPair = await ResolveKeyPair(new KeyPairIdentifierLogic(identifierAccountParsed))
|
const accountKeyPair = await ResolveKeyPair(new KeyPairIdentifierLogic(identifierAccountParsed))
|
||||||
const publicKey = accountKeyPair.getPublicKey()
|
const publicKey = accountKeyPair.getPublicKey()
|
||||||
if (!publicKey) {
|
if (!publicKey) {
|
||||||
|
|||||||
30
dlt-connector/src/utils/filesystem.ts
Normal file
30
dlt-connector/src/utils/filesystem.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import fs from 'node:fs'
|
||||||
|
import { getLogger } from 'log4js'
|
||||||
|
import { LOG4JS_BASE_CATEGORY } from '../config/const'
|
||||||
|
|
||||||
|
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.utils.filesystem`)
|
||||||
|
|
||||||
|
export function checkFileExist(filePath: string): boolean {
|
||||||
|
try {
|
||||||
|
fs.accessSync(filePath, fs.constants.R_OK | fs.constants.W_OK)
|
||||||
|
return true
|
||||||
|
} catch (err) {
|
||||||
|
logger.debug(`file ${filePath} does not exist: ${err}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkPathExist(path: string, createIfMissing: boolean = false): boolean {
|
||||||
|
const exists = checkFileExist(path)
|
||||||
|
if (exists) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (createIfMissing) {
|
||||||
|
logger.info(`create folder ${path}`)
|
||||||
|
fs.mkdirSync(path, { recursive: true })
|
||||||
|
if (!checkPathExist(path)) {
|
||||||
|
throw new Error(`Failed to create path ${path}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user