Merge pull request #3272 from gradido/federation_optimize_logging

refactor(other): federation optimize logging, fix bug
This commit is contained in:
einhornimmond 2024-01-23 21:45:07 +01:00 committed by GitHub
commit 4b4946ade0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 905 additions and 214 deletions

View File

@ -62,4 +62,5 @@ WEBHOOK_ELOPAGE_SECRET=secret
# LOG_LEVEL=info
# Federation
FEDERATION_VALIDATE_COMMUNITY_TIMER=60000
FEDERATION_VALIDATE_COMMUNITY_TIMER=60000
FEDERATION_XCOM_SENDCOINS_ENABLED=false

View File

@ -62,3 +62,4 @@ WEBHOOK_ELOPAGE_SECRET=$WEBHOOK_ELOPAGE_SECRET
# Federation
FEDERATION_VALIDATE_COMMUNITY_TIMER=$FEDERATION_VALIDATE_COMMUNITY_TIMER
FEDERATION_XCOM_SENDCOINS_ENABLED=$FEDERATION_XCOM_SENDCOINS_ENABLED

View File

@ -28,6 +28,11 @@ module.exports = {
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/entity/$1'
: '<rootDir>/../database/build/entity/$1',
'@logging/(.*)':
// eslint-disable-next-line n/no-process-env
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/logging/$1'
: '<rootDir>/../database/build/logging/$1',
'@dbTools/(.*)':
// eslint-disable-next-line n/no-process-env
process.env.NODE_ENV === 'development'

View File

@ -28,9 +28,9 @@ export class AuthenticationClient {
async openConnection(args: OpenConnectionArgs): Promise<boolean | undefined> {
logger.debug(`Authentication: openConnection at ${this.endpoint} for args:`, args)
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { data } = await this.client.rawRequest(openConnection, { args })
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const { data } = await this.client.rawRequest<{ openConnection: boolean }>(openConnection, {
args,
})
if (!data?.openConnection) {
logger.warn(
'Authentication: openConnection without response data from endpoint',

View File

@ -5,9 +5,10 @@ import { getPublicCommunityInfo } from '@/federation/client/1_0/query/getPublicC
import { getPublicKey } from '@/federation/client/1_0/query/getPublicKey'
import { backendLogger as logger } from '@/server/logger'
import { PublicCommunityInfoLoggingView } from './logging/PublicCommunityInfoLogging.view'
import { GetPublicKeyResult } from './model/GetPublicKeyResult'
import { PublicCommunityInfo } from './model/PublicCommunityInfo'
// eslint-disable-next-line camelcase
export class FederationClient {
dbCom: DbFederatedCommunity
endpoint: string
@ -27,12 +28,17 @@ export class FederationClient {
})
}
getEndpoint = () => {
return this.endpoint
}
getPublicKey = async (): Promise<string | undefined> => {
logger.debug('Federation: getPublicKey from endpoint', this.endpoint)
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { data } = await this.client.rawRequest(getPublicKey, {})
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const { data } = await this.client.rawRequest<{ getPublicKey: GetPublicKeyResult }>(
getPublicKey,
{},
)
if (!data?.getPublicKey?.publicKey) {
logger.warn('Federation: getPublicKey without response data from endpoint', this.endpoint)
return
@ -40,22 +46,25 @@ export class FederationClient {
logger.debug(
'Federation: getPublicKey successful from endpoint',
this.endpoint,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
data.getPublicKey.publicKey,
)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
return data.getPublicKey.publicKey
} catch (err) {
logger.warn('Federation: getPublicKey failed for endpoint', this.endpoint)
const errorString = JSON.stringify(err)
logger.warn('Federation: getPublicKey failed for endpoint', {
endpoint: this.endpoint,
err: errorString.length <= 200 ? errorString : errorString.substring(0, 200) + '...',
})
}
}
getPublicCommunityInfo = async (): Promise<PublicCommunityInfo | undefined> => {
logger.debug(`Federation: getPublicCommunityInfo with endpoint='${this.endpoint}'...`)
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { data } = await this.client.rawRequest(getPublicCommunityInfo, {})
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const { data } = await this.client.rawRequest<{
getPublicCommunityInfo: PublicCommunityInfo
}>(getPublicCommunityInfo, {})
if (!data?.getPublicCommunityInfo?.name) {
logger.warn(
'Federation: getPublicCommunityInfo without response data from endpoint',
@ -64,12 +73,17 @@ export class FederationClient {
return
}
logger.debug(`Federation: getPublicCommunityInfo successful from endpoint=${this.endpoint}`)
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
logger.debug(`publicCommunityInfo:`, data.getPublicCommunityInfo)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
logger.debug(
`publicCommunityInfo:`,
new PublicCommunityInfoLoggingView(data.getPublicCommunityInfo),
)
return data.getPublicCommunityInfo
} catch (err) {
logger.warn('Federation: getPublicCommunityInfo failed for endpoint', this.endpoint)
const errorString = JSON.stringify(err)
logger.warn('Federation: getPublicCommunityInfo failed for endpoint', {
endpoint: this.endpoint,
err: errorString.length <= 200 ? errorString : errorString.substring(0, 200) + '...',
})
}
}
}

View File

@ -4,6 +4,8 @@ import { GraphQLClient } from 'graphql-request'
import { LogError } from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
import { SendCoinsArgsLoggingView } from './logging/SendCoinsArgsLogging.view'
import { SendCoinsResultLoggingView } from './logging/SendCoinsResultLogging.view'
import { SendCoinsArgs } from './model/SendCoinsArgs'
import { SendCoinsResult } from './model/SendCoinsResult'
import { revertSendCoins as revertSendCoinsQuery } from './query/revertSendCoins'
@ -11,7 +13,6 @@ import { revertSettledSendCoins as revertSettledSendCoinsQuery } from './query/r
import { settleSendCoins as settleSendCoinsQuery } from './query/settleSendCoins'
import { voteForSendCoins as voteForSendCoinsQuery } from './query/voteForSendCoins'
// eslint-disable-next-line camelcase
export class SendCoinsClient {
dbCom: DbFederatedCommunity
endpoint: string
@ -34,26 +35,26 @@ export class SendCoinsClient {
async voteForSendCoins(args: SendCoinsArgs): Promise<SendCoinsResult> {
logger.debug('X-Com: voteForSendCoins against endpoint=', this.endpoint)
try {
logger.debug(`X-Com: SendCoinsClient: voteForSendCoins with args=`, args)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { data } = await this.client.rawRequest(voteForSendCoinsQuery, { args })
logger.debug(`X-Com: SendCoinsClient: after rawRequest...data:`, data)
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
logger.debug(
`X-Com: SendCoinsClient: voteForSendCoins with args=`,
new SendCoinsArgsLoggingView(args),
)
const { data } = await this.client.rawRequest<{ voteForSendCoins: SendCoinsResult }>(
voteForSendCoinsQuery,
{ args },
)
const result = data.voteForSendCoins
if (!data?.voteForSendCoins?.vote) {
logger.debug('X-Com: voteForSendCoins failed with: ', data)
logger.debug(
'X-Com: voteForSendCoins failed with: ',
new SendCoinsResultLoggingView(result),
)
return new SendCoinsResult()
}
const result = new SendCoinsResult()
result.vote = true
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
result.recipGradidoID = data.voteForSendCoins.recipGradidoID
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
result.recipFirstName = data.voteForSendCoins.recipFirstName
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
result.recipLastName = data.voteForSendCoins.recipLastName
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
result.recipAlias = data.voteForSendCoins.recipAlias
logger.debug('X-Com: voteForSendCoins successful with result=', result)
logger.debug(
'X-Com: voteForSendCoins successful with result=',
new SendCoinsResultLoggingView(result),
)
return result
} catch (err) {
throw new LogError(`X-Com: voteForSendCoins failed for endpoint=${this.endpoint}:`, err)
@ -63,11 +64,15 @@ export class SendCoinsClient {
async revertSendCoins(args: SendCoinsArgs): Promise<boolean> {
logger.debug('X-Com: revertSendCoins against endpoint=', this.endpoint)
try {
logger.debug(`X-Com: SendCoinsClient: revertSendCoins with args=`, args)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { data } = await this.client.rawRequest(revertSendCoinsQuery, { args })
logger.debug(
`X-Com: SendCoinsClient: revertSendCoins with args=`,
new SendCoinsArgsLoggingView(args),
)
const { data } = await this.client.rawRequest<{ revertSendCoins: boolean }>(
revertSendCoinsQuery,
{ args },
)
logger.debug(`X-Com: SendCoinsClient: after revertSendCoins: data=`, data)
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (!data?.revertSendCoins) {
logger.warn('X-Com: revertSendCoins without response data from endpoint', this.endpoint)
return false
@ -88,11 +93,15 @@ export class SendCoinsClient {
async settleSendCoins(args: SendCoinsArgs): Promise<boolean> {
logger.debug(`X-Com: settleSendCoins against endpoint='${this.endpoint}'...`)
try {
logger.debug(`X-Com: SendCoinsClient: settleSendCoins with args=`, args)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { data } = await this.client.rawRequest(settleSendCoinsQuery, { args })
logger.debug(
`X-Com: SendCoinsClient: settleSendCoins with args=`,
new SendCoinsArgsLoggingView(args),
)
const { data } = await this.client.rawRequest<{ settleSendCoins: boolean }>(
settleSendCoinsQuery,
{ args },
)
logger.debug(`X-Com: SendCoinsClient: after settleSendCoins: data=`, data)
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (!data?.settleSendCoins) {
logger.warn(
'X-Com: SendCoinsClient: settleSendCoins without response data from endpoint',
@ -115,9 +124,14 @@ export class SendCoinsClient {
async revertSettledSendCoins(args: SendCoinsArgs): Promise<boolean> {
logger.debug(`X-Com: revertSettledSendCoins against endpoint='${this.endpoint}'...`)
try {
logger.debug(`X-Com: SendCoinsClient: revertSettledSendCoins with args=`, args)
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { data } = await this.client.rawRequest(revertSettledSendCoinsQuery, { args })
logger.debug(
`X-Com: SendCoinsClient: revertSettledSendCoins with args=`,
new SendCoinsArgsLoggingView(args),
)
const { data } = await this.client.rawRequest<{ revertSettledSendCoins: boolean }>(
revertSettledSendCoinsQuery,
{ args },
)
logger.debug(`X-Com: SendCoinsClient: after revertSettledSendCoins: data=`, data)
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (!data?.revertSettledSendCoins) {

View File

@ -0,0 +1,19 @@
import { AbstractLoggingView } from '@logging/AbstractLogging.view'
import { PublicCommunityInfo } from '@/federation/client/1_0/model/PublicCommunityInfo'
export class PublicCommunityInfoLoggingView extends AbstractLoggingView {
public constructor(private self: PublicCommunityInfo) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
name: this.self.name,
description: this.self.description,
creationDate: this.dateToString(this.self.creationDate),
publicKey: this.self.publicKey,
}
}
}

View File

@ -0,0 +1,24 @@
import { AbstractLoggingView } from '@logging/AbstractLogging.view'
import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs'
export class SendCoinsArgsLoggingView extends AbstractLoggingView {
public constructor(private self: SendCoinsArgs) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
recipientCommunityUuid: this.self.recipientCommunityUuid,
recipientUserIdentifier: this.self.recipientUserIdentifier,
creationDate: this.self.creationDate,
amount: this.decimalToString(this.self.amount),
memoLength: this.self.memo.length,
senderCommunityUuid: this.self.senderCommunityUuid,
senderUserUuid: this.self.senderUserUuid,
senderUserName: this.self.senderUserName.substring(0, 3),
senderAlias: this.self.senderAlias?.substring(0, 3),
}
}
}

View File

@ -0,0 +1,20 @@
import { AbstractLoggingView } from '@logging/AbstractLogging.view'
import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
export class SendCoinsResultLoggingView extends AbstractLoggingView {
public constructor(private self: SendCoinsResult) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
vote: this.self.vote,
recipGradidoID: this.self.recipGradidoID,
recipFirstName: this.self.recipFirstName?.substring(0, 3),
recipLastName: this.self.recipLastName?.substring(0, 3),
recipAlias: this.self.recipAlias?.substring(0, 3),
}
}
}

View File

@ -0,0 +1,13 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Field, ObjectType } from 'type-graphql'
@ObjectType()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class GetPublicKeyResult {
constructor(pubKey: string) {
this.publicKey = pubKey
}
@Field(() => String)
publicKey: string
}

View File

@ -47,15 +47,25 @@ export class FederationClientFactory {
const instance = FederationClientFactory.instanceArray.find(
(instance) => instance.id === dbCom.id,
)
if (instance) {
// TODO: found a way to prevent double code with FederationClient::constructor
const endpoint = `${dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/'}${
dbCom.apiVersion
}/`
// check if endpoint is still the same and not changed meanwhile
if (instance && instance.client.getEndpoint() === endpoint) {
return instance.client
}
const client = FederationClientFactory.createFederationClient(dbCom)
if (client) {
FederationClientFactory.instanceArray.push({
id: dbCom.id,
client,
} as FederationClientInstance)
// only update instance if we already have one
if (instance) {
instance.client = client
} else {
FederationClientFactory.instanceArray.push({
id: dbCom.id,
client,
} as FederationClientInstance)
}
}
return client
}

View File

@ -68,7 +68,7 @@ describe('validate Communities', () => {
return { data: {} } as Response<unknown>
})
const variables1 = {
publicKey: Buffer.from('11111111111111111111111111111111'),
publicKey: Buffer.from('11111111111111111111111111111111', 'hex'),
apiVersion: '1_0',
endPoint: 'http//localhost:5001/api/',
lastAnnouncedAt: new Date(),
@ -113,7 +113,7 @@ describe('validate Communities', () => {
} as Response<unknown>
})
const variables1 = {
publicKey: Buffer.from('11111111111111111111111111111111'),
publicKey: Buffer.from('11111111111111111111111111111111', 'hex'),
apiVersion: '1_0',
endPoint: 'http//localhost:5001/api/',
lastAnnouncedAt: new Date(),
@ -195,7 +195,7 @@ describe('validate Communities', () => {
} as Response<unknown>
})
const variables1 = {
publicKey: Buffer.from('11111111111111111111111111111111'),
publicKey: Buffer.from('11111111111111111111111111111111', 'hex'),
apiVersion: '1_0',
endPoint: 'http//localhost:5001/api/',
lastAnnouncedAt: new Date(),
@ -315,7 +315,7 @@ describe('validate Communities', () => {
} as Response<unknown>
})
const variables3 = {
publicKey: Buffer.from('11111111111111111111111111111111'),
publicKey: Buffer.from('11111111111111111111111111111111', 'hex'),
apiVersion: '2_0',
endPoint: 'http//localhost:5001/api/',
lastAnnouncedAt: new Date(),

View File

@ -3,14 +3,15 @@
import { IsNull } from '@dbTools/typeorm'
import { Community as DbCommunity } from '@entity/Community'
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
import { FederatedCommunityLoggingView } from '@logging/FederatedCommunityLogging.view'
// eslint-disable-next-line camelcase
import { FederationClient as V1_0_FederationClient } from '@/federation/client/1_0/FederationClient'
import { PublicCommunityInfo } from '@/federation/client/1_0/model/PublicCommunityInfo'
import { FederationClientFactory } from '@/federation/client/FederationClientFactory'
import { backendLogger as logger } from '@/server/logger'
import { startCommunityAuthentication } from './authenticateCommunities'
import { PublicCommunityInfoLoggingView } from './client/1_0/logging/PublicCommunityInfoLogging.view'
import { ApiVersionType } from './enum/apiVersionType'
export async function startValidateCommunities(timerInterval: number): Promise<void> {
@ -37,7 +38,7 @@ export async function validateCommunities(): Promise<void> {
logger.debug(`Federation: found ${dbFederatedCommunities.length} dbCommunities`)
for (const dbCom of dbFederatedCommunities) {
logger.debug('Federation: dbCom', dbCom)
logger.debug('Federation: dbCom', new FederatedCommunityLoggingView(dbCom))
const apiValueStrings: string[] = Object.values(ApiVersionType)
logger.debug(`suppported ApiVersions=`, apiValueStrings)
if (!apiValueStrings.includes(dbCom.apiVersion)) {
@ -53,7 +54,7 @@ export async function validateCommunities(): Promise<void> {
// eslint-disable-next-line camelcase
if (client instanceof V1_0_FederationClient) {
const pubKey = await client.getPublicKey()
if (pubKey && pubKey === dbCom.publicKey.toString()) {
if (pubKey && pubKey === dbCom.publicKey.toString('hex')) {
await DbFederatedCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() })
logger.debug(`Federation: verified community with:`, dbCom.endPoint)
const pubComInfo = await client.getPublicCommunityInfo()
@ -68,7 +69,7 @@ export async function validateCommunities(): Promise<void> {
logger.debug(
'Federation: received not matching publicKey:',
pubKey,
dbCom.publicKey.toString(),
dbCom.publicKey.toString('hex'),
)
}
}
@ -82,10 +83,11 @@ async function writeForeignCommunity(
dbCom: DbFederatedCommunity,
pubInfo: PublicCommunityInfo,
): Promise<void> {
if (!dbCom || !pubInfo || !(dbCom.publicKey.toString() === pubInfo.publicKey)) {
if (!dbCom || !pubInfo || !(dbCom.publicKey.toString('hex') === pubInfo.publicKey)) {
const pubInfoView = new PublicCommunityInfoLoggingView(pubInfo)
logger.error(
`Error in writeForeignCommunity: missmatching parameters or publicKey. pubInfo:${JSON.stringify(
pubInfo,
`Error in writeForeignCommunity: missmatching parameters or publicKey. pubInfo:${pubInfoView.toString(
true,
)}`,
)
} else {

View File

@ -6,7 +6,7 @@ export class FederatedCommunity {
constructor(dbCom: DbFederatedCommunity) {
this.id = dbCom.id
this.foreign = dbCom.foreign
this.publicKey = dbCom.publicKey.toString()
this.publicKey = dbCom.publicKey.toString('hex')
this.url =
(dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/') + dbCom.apiVersion
this.lastAnnouncedAt = dbCom.lastAnnouncedAt

View File

@ -34,6 +34,60 @@ afterAll(async () => {
await con.close()
})
// real valid ed25519 key pairs
const ed25519KeyPairStaticHex = [
{
public: '264c1e88914d18166cc31e8d6c2111c03ac83f5910398eb45cd425c6c3836367',
private:
'0ddcafd5e2da92e171ccc974af22fee3ad8407475e330586c8f259837d4fedc6264c1e88914d18166cc31e8d6c2111c03ac83f5910398eb45cd425c6c3836367',
},
{
public: 'ac18a8754f725079f93d27b9054f2eff536109a2fd439f9755941abdd639baf0',
private:
'45325a0d0f22655095321d9d05999c65245da02130318ff51da1ee423b836117ac18a8754f725079f93d27b9054f2eff536109a2fd439f9755941abdd639baf0',
},
{
public: '6f7d4ccde610db1e1a33fabbb444d5400013c168296b03fd50bc686d4c1ad0ed',
private:
'8ab6d5da8b666ef5b3d754559c028806a1e2f8142a3e7ada411a8b6a3fe70eeb6f7d4ccde610db1e1a33fabbb444d5400013c168296b03fd50bc686d4c1ad0ed',
},
{
public: '85fbbce0763db24677cf7cb579a743013557a4fea0a9a624245f3ae8cd785e1d',
private:
'0369ea7c80c3134c2872c3cf77a68f12d57de57359145b550e3a0c4c8170a31785fbbce0763db24677cf7cb579a743013557a4fea0a9a624245f3ae8cd785e1d',
},
{
public: 'b099d023476ece01f231c269cbe496139ca73b3b4eb705816a511a1ca09661d0',
private:
'015ac650157b9e9bdbe718940606242daa318a251e8417b49440495e5afe3750b099d023476ece01f231c269cbe496139ca73b3b4eb705816a511a1ca09661d0',
},
{
public: '9f8dc17f1af9f71e9b9a1cd49ca295b89049863515a487578ad4f90b307abf39',
private:
'0c13e71c55a3c03bd5df05c92bbccde88ad4a47f3bac6bdc5383ef1ec946cfdc9f8dc17f1af9f71e9b9a1cd49ca295b89049863515a487578ad4f90b307abf39',
},
{
public: '34218b2f570d341370dd2db111d0ef2415c03a110c3bf3127c6b2337af71753a',
private:
'60f3479bba44d035886ac21c362bceece9f9ec81859c9b37f734b6442a06c93b34218b2f570d341370dd2db111d0ef2415c03a110c3bf3127c6b2337af71753a',
},
{
public: 'a447404f5e04ed4896ed64d0f704574ed780b52e90868d4b83e1afb8ea687ff6',
private:
'ea85ebb4332a52d87fe6f322dcd23ad4afc5eafb93dfff2216f3ffa9f0730e8aa447404f5e04ed4896ed64d0f704574ed780b52e90868d4b83e1afb8ea687ff6',
},
{
public: 'b8b987c55da62b30d929672520551033eb37abdd88f9ea104db5d107c19680b4',
private:
'29475dbbc96d694b3c653a1e143caf084f6daf2d35267522c4096c55b47e2b76b8b987c55da62b30d929672520551033eb37abdd88f9ea104db5d107c19680b4',
},
{
public: '40203d18a6ff8fb3c4c62d78e4807036fc9207782ce97a9bcf3be0755c236c37',
private:
'0b5c4d536d222e88b561ea495e15918fb8cba61a3f8c261ec9e587cca560804040203d18a6ff8fb3c4c62d78e4807036fc9207782ce97a9bcf3be0755c236c37',
},
]
describe('CommunityResolver', () => {
describe('getCommunities', () => {
let homeCom1: DbFederatedCommunity
@ -62,7 +116,7 @@ describe('CommunityResolver', () => {
homeCom1 = DbFederatedCommunity.create()
homeCom1.foreign = false
homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity')
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
homeCom1.apiVersion = '1_0'
homeCom1.endPoint = 'http://localhost/api'
homeCom1.createdAt = new Date()
@ -70,7 +124,7 @@ describe('CommunityResolver', () => {
homeCom2 = DbFederatedCommunity.create()
homeCom2.foreign = false
homeCom2.publicKey = Buffer.from('publicKey-HomeCommunity')
homeCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[1].public, 'hex')
homeCom2.apiVersion = '1_1'
homeCom2.endPoint = 'http://localhost/api'
homeCom2.createdAt = new Date()
@ -78,7 +132,7 @@ describe('CommunityResolver', () => {
homeCom3 = DbFederatedCommunity.create()
homeCom3.foreign = false
homeCom3.publicKey = Buffer.from('publicKey-HomeCommunity')
homeCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[2].public, 'hex')
homeCom3.apiVersion = '2_0'
homeCom3.endPoint = 'http://localhost/api'
homeCom3.createdAt = new Date()
@ -92,7 +146,7 @@ describe('CommunityResolver', () => {
{
id: 3,
foreign: homeCom3.foreign,
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[2].public),
url: expect.stringMatching('http://localhost/api/2_0'),
lastAnnouncedAt: null,
verifiedAt: null,
@ -103,7 +157,7 @@ describe('CommunityResolver', () => {
{
id: 2,
foreign: homeCom2.foreign,
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[1].public),
url: expect.stringMatching('http://localhost/api/1_1'),
lastAnnouncedAt: null,
verifiedAt: null,
@ -114,7 +168,7 @@ describe('CommunityResolver', () => {
{
id: 1,
foreign: homeCom1.foreign,
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
url: expect.stringMatching('http://localhost/api/1_0'),
lastAnnouncedAt: null,
verifiedAt: null,
@ -134,7 +188,7 @@ describe('CommunityResolver', () => {
foreignCom1 = DbFederatedCommunity.create()
foreignCom1.foreign = true
foreignCom1.publicKey = Buffer.from('publicKey-ForeignCommunity')
foreignCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[3].public, 'hex')
foreignCom1.apiVersion = '1_0'
foreignCom1.endPoint = 'http://remotehost/api'
foreignCom1.createdAt = new Date()
@ -142,7 +196,7 @@ describe('CommunityResolver', () => {
foreignCom2 = DbFederatedCommunity.create()
foreignCom2.foreign = true
foreignCom2.publicKey = Buffer.from('publicKey-ForeignCommunity')
foreignCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[4].public, 'hex')
foreignCom2.apiVersion = '1_1'
foreignCom2.endPoint = 'http://remotehost/api'
foreignCom2.createdAt = new Date()
@ -150,7 +204,7 @@ describe('CommunityResolver', () => {
foreignCom3 = DbFederatedCommunity.create()
foreignCom3.foreign = true
foreignCom3.publicKey = Buffer.from('publicKey-ForeignCommunity')
foreignCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[5].public, 'hex')
foreignCom3.apiVersion = '1_2'
foreignCom3.endPoint = 'http://remotehost/api'
foreignCom3.createdAt = new Date()
@ -164,7 +218,7 @@ describe('CommunityResolver', () => {
{
id: 3,
foreign: homeCom3.foreign,
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[2].public),
url: expect.stringMatching('http://localhost/api/2_0'),
lastAnnouncedAt: null,
verifiedAt: null,
@ -175,7 +229,7 @@ describe('CommunityResolver', () => {
{
id: 2,
foreign: homeCom2.foreign,
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[1].public),
url: expect.stringMatching('http://localhost/api/1_1'),
lastAnnouncedAt: null,
verifiedAt: null,
@ -186,7 +240,7 @@ describe('CommunityResolver', () => {
{
id: 1,
foreign: homeCom1.foreign,
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
url: expect.stringMatching('http://localhost/api/1_0'),
lastAnnouncedAt: null,
verifiedAt: null,
@ -197,7 +251,7 @@ describe('CommunityResolver', () => {
{
id: 6,
foreign: foreignCom3.foreign,
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[5].public),
url: expect.stringMatching('http://remotehost/api/1_2'),
lastAnnouncedAt: null,
verifiedAt: null,
@ -208,7 +262,7 @@ describe('CommunityResolver', () => {
{
id: 5,
foreign: foreignCom2.foreign,
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[4].public),
url: expect.stringMatching('http://remotehost/api/1_1'),
lastAnnouncedAt: null,
verifiedAt: null,
@ -219,7 +273,7 @@ describe('CommunityResolver', () => {
{
id: 4,
foreign: foreignCom1.foreign,
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[3].public),
url: expect.stringMatching('http://remotehost/api/1_0'),
lastAnnouncedAt: null,
verifiedAt: null,
@ -264,8 +318,8 @@ describe('CommunityResolver', () => {
homeCom1 = DbCommunity.create()
homeCom1.foreign = false
homeCom1.url = 'http://localhost/api'
homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity')
homeCom1.privateKey = Buffer.from('privateKey-HomeCommunity')
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
homeCom1.privateKey = Buffer.from(ed25519KeyPairStaticHex[0].private, 'hex')
homeCom1.communityUuid = 'HomeCom-UUID'
homeCom1.authenticatedAt = new Date()
homeCom1.name = 'HomeCommunity-name'
@ -302,8 +356,8 @@ describe('CommunityResolver', () => {
homeCom1 = DbCommunity.create()
homeCom1.foreign = false
homeCom1.url = 'http://localhost/api'
homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity')
homeCom1.privateKey = Buffer.from('privateKey-HomeCommunity')
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
homeCom1.privateKey = Buffer.from(ed25519KeyPairStaticHex[0].private, 'hex')
homeCom1.communityUuid = 'HomeCom-UUID'
homeCom1.authenticatedAt = new Date()
homeCom1.name = 'HomeCommunity-name'
@ -314,8 +368,8 @@ describe('CommunityResolver', () => {
foreignCom1 = DbCommunity.create()
foreignCom1.foreign = true
foreignCom1.url = 'http://stage-2.gradido.net/api'
foreignCom1.publicKey = Buffer.from('publicKey-stage-2_Community')
foreignCom1.privateKey = Buffer.from('privateKey-stage-2_Community')
foreignCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[3].public, 'hex')
foreignCom1.privateKey = Buffer.from(ed25519KeyPairStaticHex[3].private, 'hex')
// foreignCom1.communityUuid = 'Stage2-Com-UUID'
// foreignCom1.authenticatedAt = new Date()
foreignCom1.name = 'Stage-2_Community-name'
@ -326,8 +380,8 @@ describe('CommunityResolver', () => {
foreignCom2 = DbCommunity.create()
foreignCom2.foreign = true
foreignCom2.url = 'http://stage-3.gradido.net/api'
foreignCom2.publicKey = Buffer.from('publicKey-stage-3_Community')
foreignCom2.privateKey = Buffer.from('privateKey-stage-3_Community')
foreignCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[4].public, 'hex')
foreignCom2.privateKey = Buffer.from(ed25519KeyPairStaticHex[4].private, 'hex')
foreignCom2.communityUuid = 'Stage3-Com-UUID'
foreignCom2.authenticatedAt = new Date()
foreignCom2.name = 'Stage-3_Community-name'

View File

@ -58,7 +58,8 @@
"@test/*": ["test/*"],
/* external */
"@dbTools/*": ["../database/src/*", "../../database/build/src/*"],
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"]
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"],
"@logging/*": ["../database/logging/*", "../../database/build/logging/*"]
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": ["@types", "node_modules/@types"], /* List of folders to include type definitions from. */

View File

@ -0,0 +1,38 @@
import util from 'util'
import { Decimal } from 'decimal.js-light'
export abstract class AbstractLoggingView {
// eslint-disable-next-line no-undef
protected bufferStringFormat: BufferEncoding = 'hex'
// This function gets called automatically when JSON.stringify() is called on this class instance
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public abstract toJSON(): any
public toString(compact = false): string {
if (compact) {
return JSON.stringify(this.toJSON())
} else {
return JSON.stringify(this.toJSON(), null, 2)
}
}
// called form console.log or log4js logging functions
[util.inspect.custom](): string {
return this.toString()
}
public dateToString(date: Date | undefined | null): string | undefined {
if (date) {
return date.toISOString()
}
return undefined
}
public decimalToString(number: Decimal | undefined | null): string | undefined {
if (number) {
return number.toString()
}
return undefined
}
}

View File

@ -0,0 +1,26 @@
import { Community } from '../entity/Community'
import { AbstractLoggingView } from './AbstractLogging.view'
export class CommunityLoggingView extends AbstractLoggingView {
public constructor(private self: Community) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
foreign: this.self.foreign,
url: this.self.url,
publicKey: this.self.publicKey.toString(this.bufferStringFormat),
communityUuid: this.self.communityUuid,
authenticatedAt: this.dateToString(this.self.authenticatedAt),
name: this.self.name,
description: this.self.description?.substring(0, 24),
creationDate: this.dateToString(this.self.creationDate),
createdAt: this.dateToString(this.self.createdAt),
updatedAt: this.dateToString(this.self.updatedAt),
}
}
}

View File

@ -0,0 +1,45 @@
import { Contribution } from '../entity/Contribution'
import { AbstractLoggingView } from './AbstractLogging.view'
import { ContributionMessageLoggingView } from './ContributionMessageLogging.view'
import { TransactionLoggingView } from './TransactionLogging.view'
import { UserLoggingView } from './UserLogging.view'
export class ContributionLoggingView extends AbstractLoggingView {
public constructor(private self: Contribution) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
user: this.self.user
? new UserLoggingView(this.self.user).toJSON()
: { id: this.self.userId },
createdAt: this.dateToString(this.self.createdAt),
resubmissionAt: this.dateToString(this.self.resubmissionAt),
contributionDate: this.dateToString(this.self.contributionDate),
memoLength: this.self.memo.length,
amount: this.decimalToString(this.self.amount),
moderatorId: this.self.moderatorId,
contributionLinkId: this.self.contributionLinkId,
confirmedBy: this.self.confirmedBy,
confirmedAt: this.dateToString(this.self.confirmedAt),
deniedBy: this.self.deniedBy,
deniedAt: this.dateToString(this.self.deniedAt),
contributionType: this.self.contributionType,
contributionStatus: this.self.contributionStatus,
transactionId: this.self.transactionId,
updatedAt: this.dateToString(this.self.updatedAt),
updatedBy: this.self.updatedBy,
deletedAt: this.dateToString(this.self.deletedAt),
deletedBy: this.self.deletedBy,
messages: this.self.messages
? this.self.messages.map((message) => new ContributionMessageLoggingView(message).toJSON())
: undefined,
transaction: this.self.transaction
? new TransactionLoggingView(this.self.transaction).toJSON()
: { id: this.self.transactionId },
}
}
}

View File

@ -0,0 +1,30 @@
import { ContributionMessage } from '../entity/ContributionMessage'
import { AbstractLoggingView } from './AbstractLogging.view'
import { ContributionLoggingView } from './ContributionLogging.view'
import { UserLoggingView } from './UserLogging.view'
export class ContributionMessageLoggingView extends AbstractLoggingView {
public constructor(private self: ContributionMessage) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
contribution: this.self.contribution
? new ContributionLoggingView(this.self.contribution).toJSON()
: { id: this.self.contributionId },
user: this.self.user
? new UserLoggingView(this.self.user).toJSON()
: { id: this.self.userId },
messageLength: this.self.message.length,
createdAt: this.dateToString(this.self.createdAt),
updatedAt: this.dateToString(this.self.updatedAt),
deletedAt: this.dateToString(this.self.deletedAt),
deletedBy: this.self.deletedBy,
type: this.self.type,
isModerator: this.self.isModerator,
}
}
}

View File

@ -0,0 +1,23 @@
import { DltTransaction } from '../entity/DltTransaction'
import { AbstractLoggingView } from './AbstractLogging.view'
import { TransactionLoggingView } from './TransactionLogging.view'
export class DltTransactionLoggingView extends AbstractLoggingView {
public constructor(private self: DltTransaction) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
transaction: this.self.transaction
? new TransactionLoggingView(this.self.transaction).toJSON()
: { id: this.self.transactionId },
messageId: this.self.messageId,
verified: this.self.verified,
createdAt: this.dateToString(this.self.createdAt),
verifiedAt: this.dateToString(this.self.verifiedAt),
}
}
}

View File

@ -0,0 +1,24 @@
import { FederatedCommunity } from '../entity/FederatedCommunity'
import { AbstractLoggingView } from './AbstractLogging.view'
export class FederatedCommunityLoggingView extends AbstractLoggingView {
public constructor(private self: FederatedCommunity) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
foreign: this.self.foreign,
publicKey: this.self.publicKey.toString(this.bufferStringFormat),
apiVersion: this.self.apiVersion,
endPoint: this.self.endPoint,
lastAnnouncedAt: this.dateToString(this.self.lastAnnouncedAt),
verifiedAt: this.self.verifiedAt,
lastErrorAt: this.self.lastErrorAt,
createdAt: this.dateToString(this.self.createdAt),
updatedAt: this.dateToString(this.self.updatedAt),
}
}
}

View File

@ -0,0 +1,27 @@
/* eslint-disable no-unused-vars */
import { PendingTransaction } from '../entity/PendingTransaction'
import { Transaction } from '../entity/Transaction'
import { AbstractLoggingView } from './AbstractLogging.view'
import { TransactionLoggingView } from './TransactionLogging.view'
// TODO: move enum into database, maybe rename database
enum PendingTransactionState {
NEW = 1,
PENDING = 2,
SETTLED = 3,
REVERTED = 4,
}
export class PendingTransactionLoggingView extends AbstractLoggingView {
public constructor(private self: PendingTransaction) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
...new TransactionLoggingView(this.self as Transaction).toJSON(),
state: PendingTransactionState[this.self.state],
}
}
}

View File

@ -0,0 +1,56 @@
/* eslint-disable no-unused-vars */
import { Transaction } from '../entity/Transaction'
import { AbstractLoggingView } from './AbstractLogging.view'
import { ContributionLoggingView } from './ContributionLogging.view'
import { DltTransactionLoggingView } from './DltTransactionLogging.view'
// TODO: move enum into database, maybe rename database
enum TransactionTypeId {
CREATION = 1,
SEND = 2,
RECEIVE = 3,
// This is a virtual property, never occurring on the database
DECAY = 4,
LINK_SUMMARY = 5,
}
export class TransactionLoggingView extends AbstractLoggingView {
public constructor(private self: Transaction) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
previous: this.self.previous,
typeId: TransactionTypeId[this.self.typeId],
transactionLinkId: this.self.transactionLinkId,
amount: this.decimalToString(this.self.amount),
balance: this.decimalToString(this.self.balance),
balanceDate: this.dateToString(this.self.balanceDate),
decay: this.decimalToString(this.self.decay),
decayStart: this.dateToString(this.self.decayStart),
memoLength: this.self.memo.length,
creationDate: this.dateToString(this.self.creationDate),
userId: this.self.userId,
userCommunityUuid: this.self.userCommunityUuid,
userGradidoId: this.self.userGradidoID,
userName: this.self.userName?.substring(0, 3) + '...',
linkedUserId: this.self.linkedUserId,
linkedUserCommunityUuid: this.self.linkedUserCommunityUuid,
linkedUserGradidoID: this.self.linkedUserGradidoID,
linkedUserName: this.self.linkedUserName?.substring(0, 3) + '...',
linkedTransactionId: this.self.linkedTransactionId,
contribution: this.self.contribution
? new ContributionLoggingView(this.self.contribution)
: undefined,
dltTransaction: this.self.dltTransaction
? new DltTransactionLoggingView(this.self.dltTransaction).toJSON()
: undefined,
previousTransaction: this.self.previousTransaction
? new TransactionLoggingView(this.self.previousTransaction).toJSON()
: undefined,
}
}
}

View File

@ -0,0 +1,35 @@
/* eslint-disable no-unused-vars */
import { UserContact } from '../entity/UserContact'
import { AbstractLoggingView } from './AbstractLogging.view'
import { UserLoggingView } from './UserLogging.view'
enum OptInType {
EMAIL_OPT_IN_REGISTER = 1,
EMAIL_OPT_IN_RESET_PASSWORD = 2,
}
export class UserContactLoggingView extends AbstractLoggingView {
public constructor(private self: UserContact) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
type: this.self.type,
user: this.self.user
? new UserLoggingView(this.self.user).toJSON()
: { id: this.self.userId },
email: this.self.email?.substring(0, 3) + '...',
emailVerificationCode: this.self.emailVerificationCode?.substring(0, 4) + '...',
emailOptInTypeId: OptInType[this.self.emailOptInTypeId],
emailResendCount: this.self.emailResendCount,
emailChecked: this.self.emailChecked,
phone: this.self.phone ? this.self.phone.substring(0, 3) + '...' : undefined,
createdAt: this.dateToString(this.self.createdAt),
updatedAt: this.dateToString(this.self.updatedAt),
deletedAt: this.dateToString(this.self.deletedAt),
}
}
}

View File

@ -0,0 +1,60 @@
/* eslint-disable no-unused-vars */
import { User } from '../entity/User'
import { AbstractLoggingView } from './AbstractLogging.view'
import { ContributionLoggingView } from './ContributionLogging.view'
import { ContributionMessageLoggingView } from './ContributionMessageLogging.view'
import { UserContactLoggingView } from './UserContactLogging.view'
import { UserRoleLoggingView } from './UserRoleLogging.view'
enum PasswordEncryptionType {
NO_PASSWORD = 0,
EMAIL = 1,
GRADIDO_ID = 2,
}
export class UserLoggingView extends AbstractLoggingView {
public constructor(private self: User) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
foreign: this.self.foreign,
gradidoID: this.self.gradidoID,
communityUuid: this.self.communityUuid,
alias: this.self.alias?.substring(0, 3) + '...',
emailContact: this.self.emailContact
? new UserContactLoggingView(this.self.emailContact).toJSON()
: { id: this.self.emailId },
firstName: this.self.firstName?.substring(0, 3) + '...',
lastName: this.self.lastName?.substring(0, 3) + '...',
createdAt: this.dateToString(this.self.createdAt),
deletedAt: this.dateToString(this.self.deletedAt),
passwordEncryptionType: this.self.passwordEncryptionType as PasswordEncryptionType,
language: this.self.language,
hideAmountGDD: this.self.hideAmountGDD,
hideAmountGDT: this.self.hideAmountGDT,
userRoles: this.self.userRoles
? this.self.userRoles.map((userRole) => new UserRoleLoggingView(userRole).toJSON())
: undefined,
referrerId: this.self.referrerId,
contributionLinkId: this.self.contributionLinkId,
publisherId: this.self.publisherId,
contributions: this.self.contributions
? this.self.contributions.map((contribution) =>
new ContributionLoggingView(contribution).toJSON(),
)
: undefined,
messages: this.self.messages
? this.self.messages.map((message) => new ContributionMessageLoggingView(message).toJSON())
: undefined,
userContacts: this.self.userContacts
? this.self.userContacts.map((userContact) =>
new UserContactLoggingView(userContact).toJSON(),
)
: undefined,
}
}
}

View File

@ -0,0 +1,22 @@
import { UserRole } from '../entity/UserRole'
import { AbstractLoggingView } from './AbstractLogging.view'
import { UserLoggingView } from './UserLogging.view'
export class UserRoleLoggingView extends AbstractLoggingView {
public constructor(private self: UserRole) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
user: this.self.user
? new UserLoggingView(this.self.user).toJSON()
: { id: this.self.userId },
role: this.self.role,
createdAt: this.dateToString(this.self.createdAt),
updatedAt: this.dateToString(this.self.updatedAt),
}
}
}

View File

@ -12,6 +12,9 @@ EMAIL_PASSWORD=1234
EMAIL_SMTP_URL=smtp.lustig.de
EMAIL_SMTP_PORT=587
# if set to true allow sending gradidos to another communities
FEDERATION_XCOM_SENDCOINS_ENABLED=false
# how many minutes email verification code is valid
# also used for password reset code
EMAIL_CODE_VALID_TIME=1440
@ -25,8 +28,8 @@ DATABASE_CONFIG_VERSION=v1.2022-03-18
BACKEND_CONFIG_VERSION=v21.2024-01-06
FRONTEND_CONFIG_VERSION=v5.2024-01-08
ADMIN_CONFIG_VERSION=v2.2024-01-04
FEDERATION_CONFIG_VERSION=v1.2023-01-09
FEDERATION_DHT_CONFIG_VERSION=v3.2023-04-26
FEDERATION_CONFIG_VERSION=v2.2023-08-24
FEDERATION_DHT_CONFIG_VERSION=v4.2024-01-17
FEDERATION_DHT_TOPIC=GRADIDO_HUB
@ -76,7 +79,7 @@ FEDERATION_COMMUNITY_API_PORT=5000
FEDERATION_VALIDATE_COMMUNITY_TIMER=60000
# comma separated list of api-versions, which cause starting several federation modules
FEDERATION_COMMUNITY_APIS=1_0,1_1
FEDERATION_COMMUNITY_APIS=1_0
# externe gradido services (more added in future)
GDT_API_URL=https://gdt.gradido.net

View File

@ -15,5 +15,5 @@ TYPEORM_LOGGING_RELATIVE_PATH=typeorm.dht-node.log
FEDERATION_DHT_TOPIC=GRADIDO_HUB
# FEDERATION_DHT_SEED=64ebcb0e3ad547848fef4197c6e2332f
FEDERATION_COMMUNITY_URL=http://localhost
# the api port is the dht baseport, which will be added with the supported api-versions, e.g. 1_0 = 5010
FEDERATION_COMMUNITY_API_PORT=5000
# comma separated values, which apis should be announced
FEDERATION_COMMUNITY_APIS=1_0

View File

@ -1,5 +1,5 @@
# must match the CONFIG_VERSION.EXPECTED definition in scr/config/index.ts
CONFIG_VERSION=v3.2023-04-26
CONFIG_VERSION=$FEDERATION_DHT_CONFIG_VERSION
# Database
DB_HOST=localhost
@ -19,6 +19,7 @@ FEDERATION_DHT_CONFIG_VERSION=$FEDERATION_DHT_CONFIG_VERSION
# on an hash created from this topic
FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC
FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED
# comma separated values, which apis should be announced
FEDERATION_COMMUNITY_APIS=$FEDERATION_COMMUNITY_APIS
COMMUNITY_HOST=$COMMUNITY_HOST
URL_PROTOCOL=$URL_PROTOCOL
FEDERATION_COMMUNITY_API_PORT=$FEDERATION_COMMUNITY_API_PORT

View File

@ -7,7 +7,7 @@ module.exports = {
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
coverageThreshold: {
global: {
lines: 83,
lines: 82,
},
},
setupFiles: ['<rootDir>/test/testSetup.ts'],
@ -21,6 +21,11 @@ module.exports = {
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/entity/$1'
: '<rootDir>/../database/build/entity/$1',
'@logging/(.*)':
// eslint-disable-next-line n/no-process-env
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/logging/$1'
: '<rootDir>/../database/build/logging/$1',
'@dbTools/(.*)':
// eslint-disable-next-line n/no-process-env
process.env.NODE_ENV === 'development'

View File

@ -10,7 +10,7 @@ const constants = {
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v3.2023-04-26',
EXPECTED: 'v4.2024-01-17',
CURRENT: '',
},
}
@ -43,7 +43,7 @@ const federation = {
FEDERATION_DHT_TOPIC: process.env.FEDERATION_DHT_TOPIC ?? 'GRADIDO_HUB',
FEDERATION_DHT_SEED: process.env.FEDERATION_DHT_SEED ?? null,
FEDERATION_COMMUNITY_URL: process.env.FEDERATION_COMMUNITY_URL ?? COMMUNITY_URL,
FEDERATION_COMMUNITY_API_PORT: process.env.FEDERATION_COMMUNITY_API_PORT ?? '5000',
FEDERATION_COMMUNITY_APIS: process.env.FEDERATION_COMMUNITY_APIS ?? '1_0',
}
// Check config version

View File

@ -0,0 +1,5 @@
export enum ApiVersionType {
V1_0 = '1_0',
V1_1 = '1_1', // currently no changes
V2_0 = '2_0', // not exist
}

View File

@ -14,6 +14,7 @@ import { CONFIG } from '@/config'
import { startDHT } from './index'
CONFIG.FEDERATION_DHT_SEED = '64ebcb0e3ad547848fef4197c6e2332f'
CONFIG.FEDERATION_COMMUNITY_APIS = '1_0,1_1,2_0'
jest.mock('@hyperswarm/dht')
@ -253,6 +254,29 @@ describe('federation', () => {
})
})
describe('with receiving non ascii character', () => {
beforeEach(() => {
jest.clearAllMocks()
// containing non-ascii character copyright symbol, U+00A9
socketEventMocks.data(Buffer.from('48656C6C6F2C20C2A92048656C6C6F21', 'hex'))
/*
const buffer = Buffer.from('48656C6C6F2C20C2A92048656C6C6F21', 'hex')
for (const byte of buffer) {
console.log('byte: %o', byte)
if (byte > 127) {
console.log('non ascii char spotted')
}
}
*/
})
it('logs the binary data as hex', () => {
expect(logger.warn).toBeCalledWith(
'received non ascii character, content as hex: 48656c6c6f2c20c2a92048656c6c6f21',
)
})
})
describe('with receiving array of strings', () => {
beforeEach(() => {
jest.clearAllMocks()

View File

@ -3,11 +3,14 @@
import { Community as DbCommunity } from '@entity/Community'
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
import DHT from '@hyperswarm/dht'
import { CommunityLoggingView } from '@logging/CommunityLogging.view'
import { v4 as uuidv4 } from 'uuid'
import { CONFIG } from '@/config'
import { logger } from '@/server/logger'
import { ApiVersionType } from './ApiVersionType'
const KEY_SECRET_SEEDBYTES = 32
const POLLTIME = 20000
@ -15,11 +18,6 @@ const SUCCESSTIME = 120000
const ERRORTIME = 240000
const ANNOUNCETIME = 30000
enum ApiVersionType {
V1_0 = '1_0',
V1_1 = '1_1',
V2_0 = '2_0',
}
type CommunityApi = {
api: string
url: string
@ -27,6 +25,15 @@ type CommunityApi = {
type KeyPair = { publicKey: Buffer; secretKey: Buffer }
function isAscii(buffer: Buffer): boolean {
for (const byte of buffer) {
if (byte > 127) {
return false
}
}
return true
}
export const startDHT = async (topic: string): Promise<void> => {
try {
const TOPIC = DHT.hash(Buffer.from(topic))
@ -59,6 +66,10 @@ export const startDHT = async (topic: string): Promise<void> => {
)
return
}
if (!isAscii(data)) {
logger.warn(`received non ascii character, content as hex: ${data.toString('hex')}`)
return
}
logger.info(`data: ${data.toString('ascii')}`)
const recApiVersions: CommunityApi[] = JSON.parse(data.toString('ascii'))
@ -190,9 +201,14 @@ export const startDHT = async (topic: string): Promise<void> => {
}
async function writeFederatedHomeCommunityEntries(pubKey: string): Promise<CommunityApi[]> {
const homeApiVersions: CommunityApi[] = Object.values(ApiVersionType).map(function (apiEnum) {
const homeApiVersions: CommunityApi[] = CONFIG.FEDERATION_COMMUNITY_APIS.split(',').map(function (
api,
) {
if (!Object.values(ApiVersionType).includes(api as ApiVersionType)) {
throw new Error(`Federation: unknown api version: ${api}`)
}
const comApi: CommunityApi = {
api: apiEnum,
api,
url: CONFIG.FEDERATION_COMMUNITY_URL + '/api/',
}
return comApi
@ -227,7 +243,7 @@ async function writeHomeCommunityEntry(keyPair: KeyPair): Promise<void> {
homeCom.name = CONFIG.COMMUNITY_NAME
homeCom.description = CONFIG.COMMUNITY_DESCRIPTION
await DbCommunity.save(homeCom)
logger.info(`home-community updated successfully:`, homeCom)
logger.info(`home-community updated successfully:`, new CommunityLoggingView(homeCom))
} else {
// insert a new homecommunity entry including a new ID and a new but ensured unique UUID
homeCom = new DbCommunity()
@ -240,7 +256,7 @@ async function writeHomeCommunityEntry(keyPair: KeyPair): Promise<void> {
homeCom.description = CONFIG.COMMUNITY_DESCRIPTION
homeCom.creationDate = new Date()
await DbCommunity.insert(homeCom)
logger.info(`home-community inserted successfully:`, homeCom)
logger.info(`home-community inserted successfully:`, new CommunityLoggingView(homeCom))
}
} catch (err) {
throw new Error(`Federation: Error writing HomeCommunity-Entry: ${err}`)

View File

@ -52,7 +52,8 @@
/* external */
"@typeorm/*": ["../backend/src/typeorm/*", "../../backend/src/typeorm/*"],
"@dbTools/*": ["../database/src/*", "../../database/build/src/*"],
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"]
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"],
"@logging/*": ["../database/logging/*", "../../database/build/logging/*"]
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": ["src/dht_node/@types", "node_modules/@types"], /* List of folders to include type definitions from. */

View File

@ -1,5 +1,5 @@
# must match the CONFIG_VERSION.EXPECTED definition in scr/config/index.ts
CONFIG_VERSION=v2.2023-08-24
CONFIG_VERSION=$FEDERATION_CONFIG_VERSION
LOG_LEVEL=$LOG_LEVEL
# this is set fix to false, because it is important for 'production' environments. only set to true if a graphql-playground should be in use

View File

@ -24,6 +24,11 @@ module.exports = {
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/entity/$1'
: '<rootDir>/../database/build/entity/$1',
'@logging/(.*)':
// eslint-disable-next-line n/no-process-env
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/logging/$1'
: '<rootDir>/../database/build/logging/$1',
'@dbTools/(.*)':
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/src/$1'

View File

@ -63,7 +63,10 @@ export class AuthenticationClient {
return authUuid
}
} catch (err) {
logger.error('Authentication: authenticate failed for endpoint', this.endpoint)
logger.error('Authentication: authenticate failed', {
endpoint: this.endpoint,
err,
})
}
return null
}

View File

@ -0,0 +1,18 @@
import { GetPublicCommunityInfoResult } from '@/graphql/api/1_0/model/GetPublicCommunityInfoResult'
import { AbstractLoggingView } from '@logging/AbstractLogging.view'
export class GetPublicCommunityInfoResultLoggingView extends AbstractLoggingView {
public constructor(private self: GetPublicCommunityInfoResult) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
name: this.self.name,
description: this.self.description,
creationDate: this.dateToString(this.self.creationDate),
publicKey: this.self.publicKey,
}
}
}

View File

@ -0,0 +1,23 @@
import { AbstractLoggingView } from '@logging/AbstractLogging.view'
import { SendCoinsArgs } from '@/graphql/api/1_0/model/SendCoinsArgs'
export class SendCoinsArgsLoggingView extends AbstractLoggingView {
public constructor(private self: SendCoinsArgs) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
recipientCommunityUuid: this.self.recipientCommunityUuid,
recipientUserIdentifier: this.self.recipientUserIdentifier,
creationDate: this.self.creationDate,
amount: this.decimalToString(this.self.amount),
memoLength: this.self.memo.length,
senderCommunityUuid: this.self.senderCommunityUuid,
senderUserUuid: this.self.senderUserUuid,
senderUserName: this.self.senderUserName.substring(0, 3),
senderAlias: this.self.senderAlias?.substring(0, 3),
}
}
}

View File

@ -7,7 +7,7 @@ import { Field, ObjectType } from 'type-graphql'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class GetPublicCommunityInfoResult {
constructor(dbCom: DbCommunity) {
this.publicKey = dbCom.publicKey.toString()
this.publicKey = dbCom.publicKey.toString('hex')
this.name = dbCom.name
this.description = dbCom.description
this.creationDate = dbCom.creationDate

View File

@ -3,6 +3,8 @@ import { Arg, Mutation, Resolver } from 'type-graphql'
import { federationLogger as logger } from '@/server/logger'
import { Community as DbCommunity } from '@entity/Community'
import { FederatedCommunity as DbFedCommunity } from '@entity/FederatedCommunity'
import { CommunityLoggingView } from '@logging/CommunityLogging.view'
import { FederatedCommunityLoggingView } from '@logging/FederatedCommunityLogging.view'
import { LogError } from '@/server/LogError'
import { OpenConnectionArgs } from '../model/OpenConnectionArgs'
import { startAuthentication, startOpenConnectionCallback } from '../util/authenticateCommunity'
@ -11,7 +13,6 @@ import { CONFIG } from '@/config'
import { AuthenticationArgs } from '../model/AuthenticationArgs'
@Resolver()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class AuthenticationResolver {
@Mutation(() => Boolean)
async openConnection(
@ -28,7 +29,7 @@ export class AuthenticationResolver {
if (!comA) {
throw new LogError(`unknown requesting community with publicKey`, pubKeyBuf.toString('hex'))
}
logger.debug(`Authentication: found requestedCom:`, comA)
logger.debug(`Authentication: found requestedCom:`, new CommunityLoggingView(comA))
// no await to respond immediatly and invoke callback-request asynchron
void startOpenConnectionCallback(args, comA, CONFIG.FEDERATION_API)
return true
@ -48,7 +49,10 @@ export class AuthenticationResolver {
if (!fedComB) {
throw new LogError(`unknown callback community with url`, args.url)
}
logger.debug(`Authentication: found fedComB and start authentication:`, fedComB)
logger.debug(
`Authentication: found fedComB and start authentication:`,
new FederatedCommunityLoggingView(fedComB),
)
// no await to respond immediatly and invoke authenticate-request asynchron
void startAuthentication(args.oneTimeCode, fedComB)
return true
@ -61,13 +65,16 @@ export class AuthenticationResolver {
): Promise<string | null> {
logger.debug(`Authentication: authenticate() via apiVersion=1_0 ...`, args)
const authCom = await DbCommunity.findOneByOrFail({ communityUuid: args.oneTimeCode })
logger.debug('Authentication: found authCom:', authCom)
logger.debug('Authentication: found authCom:', new CommunityLoggingView(authCom))
if (authCom) {
// TODO decrypt args.uuid with authCom.publicKey
authCom.communityUuid = args.uuid
authCom.authenticatedAt = new Date()
await DbCommunity.save(authCom)
logger.debug('Authentication: store authCom.uuid successfully:', authCom)
logger.debug(
'Authentication: store authCom.uuid successfully:',
new CommunityLoggingView(authCom),
)
const homeCom = await DbCommunity.findOneByOrFail({ foreign: false })
// TODO encrypt homeCom.uuid with homeCom.privateKey
if (homeCom.communityUuid) {

View File

@ -46,7 +46,10 @@ describe('PublicCommunityInfoResolver', () => {
homeCom.name = 'Community-Name'
homeCom.description = 'Community-Description'
homeCom.creationDate = new Date()
homeCom.publicKey = Buffer.from('homeCommunity-publicKey')
homeCom.publicKey = Buffer.from(
'316f2951501f27c664e188d5128505917e8673e8bebce141f86e70907e782a08',
'hex',
)
await DbCommunity.insert(homeCom)
})
@ -57,7 +60,7 @@ describe('PublicCommunityInfoResolver', () => {
name: 'Community-Name',
description: 'Community-Description',
creationDate: homeCom.creationDate?.toISOString(),
publicKey: expect.stringMatching('homeCommunity-publicKey'),
publicKey: '316f2951501f27c664e188d5128505917e8673e8bebce141f86e70907e782a08',
},
},
})

View File

@ -3,16 +3,19 @@ import { Query, Resolver } from 'type-graphql'
import { federationLogger as logger } from '@/server/logger'
import { Community as DbCommunity } from '@entity/Community'
import { GetPublicCommunityInfoResult } from '../model/GetPublicCommunityInfoResult'
import { GetPublicCommunityInfoResultLoggingView } from '../logger/GetPublicCommunityInfoResultLogging.view'
@Resolver()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class PublicCommunityInfoResolver {
@Query(() => GetPublicCommunityInfoResult)
async getPublicCommunityInfo(): Promise<GetPublicCommunityInfoResult> {
logger.debug(`getPublicCommunityInfo() via apiVersion=1_0 ...`)
const homeCom = await DbCommunity.findOneByOrFail({ foreign: false })
const result = new GetPublicCommunityInfoResult(homeCom)
logger.debug(`getPublicCommunityInfo()-1_0... return publicInfo=${JSON.stringify(result)}`)
const publicInfoView = new GetPublicCommunityInfoResultLoggingView(result)
logger.debug(
`getPublicCommunityInfo()-1_0... return publicInfo=${publicInfoView.toString(true)}`,
)
return result
}
}

View File

@ -39,7 +39,10 @@ describe('PublicKeyResolver', () => {
homeCom.foreign = false
homeCom.apiVersion = '1_0'
homeCom.endPoint = 'endpoint-url'
homeCom.publicKey = Buffer.from('homeCommunity-publicKey')
homeCom.publicKey = Buffer.from(
'9f6dcd0d985cc7105cd71c3417d9c291b126c8ca90513197de02191f928ef713',
'hex',
)
await DbFederatedCommunity.insert(homeCom)
})
@ -47,7 +50,7 @@ describe('PublicKeyResolver', () => {
await expect(query({ query: getPublicKeyQuery })).resolves.toMatchObject({
data: {
getPublicKey: {
publicKey: expect.stringMatching('homeCommunity-publicKey'),
publicKey: '9f6dcd0d985cc7105cd71c3417d9c291b126c8ca90513197de02191f928ef713',
},
},
})

View File

@ -5,7 +5,6 @@ import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCom
import { GetPublicKeyResult } from '../model/GetPublicKeyResult'
@Resolver()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class PublicKeyResolver {
@Query(() => GetPublicKeyResult)
async getPublicKey(): Promise<GetPublicKeyResult> {
@ -16,7 +15,8 @@ export class PublicKeyResolver {
apiVersion: '1_0',
},
})
logger.debug(`getPublicKey()-1_0... return publicKey=${homeCom.publicKey}`)
return new GetPublicKeyResult(homeCom.publicKey.toString())
const publicKeyHex = homeCom.publicKey.toString('hex')
logger.debug(`getPublicKey()-1_0... return publicKey=${publicKeyHex}`)
return new GetPublicKeyResult(publicKeyHex)
}
}

View File

@ -1,6 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { Arg, Args, Mutation, Resolver } from 'type-graphql'
import { Arg, Mutation, Resolver } from 'type-graphql'
import { federationLogger as logger } from '@/server/logger'
import { PendingTransactionLoggingView } from '@logging/PendingTransactionLogging.view'
import { Community as DbCommunity } from '@entity/Community'
import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction'
import { SendCoinsArgs } from '../model/SendCoinsArgs'
@ -16,27 +16,16 @@ import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier'
import { SendCoinsResult } from '../model/SendCoinsResult'
import Decimal from 'decimal.js-light'
import { storeForeignUser } from '../util/storeForeignUser'
import { SendCoinsArgsLoggingView } from '../logger/SendCoinsArgsLogging.view'
@Resolver()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class SendCoinsResolver {
@Mutation(() => SendCoinsResult)
async voteForSendCoins(
@Arg('data')
args: SendCoinsArgs,
): Promise<SendCoinsResult> {
logger.debug(
`voteForSendCoins() via apiVersion=1_0 ...`,
args.recipientCommunityUuid,
args.recipientUserIdentifier,
args.creationDate,
args.amount.toString(),
args.memo,
args.senderCommunityUuid,
args.senderUserUuid,
args.senderUserName,
args.senderAlias,
)
logger.debug(`voteForSendCoins() via apiVersion=1_0 ...`, new SendCoinsArgsLoggingView(args))
const result = new SendCoinsResult()
// first check if receiver community is correct
const homeCom = await DbCommunity.findOneBy({
@ -152,7 +141,10 @@ export class SendCoinsResolver {
linkedUserCommunityUuid: args.senderCommunityUuid,
linkedUserGradidoID: args.senderUserUuid,
})
logger.debug('XCom: revertSendCoins found pendingTX=', pendingTx)
logger.debug(
'XCom: revertSendCoins found pendingTX=',
pendingTx ? new PendingTransactionLoggingView(pendingTx) : 'null',
)
if (pendingTx && pendingTx.amount.toString() === args.amount.toString()) {
logger.debug('XCom: revertSendCoins matching pendingTX for remove...')
try {
@ -167,19 +159,11 @@ export class SendCoinsResolver {
pendingTx?.amount.toString(),
args.amount.toString(),
)
throw new LogError(
`Can't find in revertSendCoins the pending receiver TX for args=`,
args.recipientCommunityUuid,
args.recipientUserIdentifier,
PendingTransactionState.NEW,
TransactionTypeId.RECEIVE,
args.creationDate,
args.amount,
args.memo,
args.senderCommunityUuid,
args.senderUserUuid,
args.senderUserName,
)
throw new LogError(`Can't find in revertSendCoins the pending receiver TX for `, {
args: new SendCoinsArgsLoggingView(args),
pendingTransactionState: PendingTransactionState.NEW,
transactionType: TransactionTypeId.RECEIVE,
})
}
logger.debug(`revertSendCoins()-1_0... successfull`)
return true
@ -193,15 +177,7 @@ export class SendCoinsResolver {
@Arg('data')
args: SendCoinsArgs,
): Promise<boolean> {
logger.debug(
`settleSendCoins() via apiVersion=1_0 ...userCommunityUuid=${
args.recipientCommunityUuid
}, userGradidoID=${args.recipientUserIdentifier}, balanceDate=${
args.creationDate
},amount=${args.amount.valueOf()}, memo=${args.memo}, linkedUserCommunityUuid = ${
args.senderCommunityUuid
}, userSenderIdentifier=${args.senderUserUuid}, userSenderName=${args.senderUserName}`,
)
logger.debug(`settleSendCoins() via apiVersion=1_0 ...`, new SendCoinsArgsLoggingView(args))
// first check if receiver community is correct
const homeCom = await DbCommunity.findOneBy({
communityUuid: args.recipientCommunityUuid,
@ -232,7 +208,10 @@ export class SendCoinsResolver {
linkedUserCommunityUuid: args.senderCommunityUuid,
linkedUserGradidoID: args.senderUserUuid,
})
logger.debug('XCom: settleSendCoins found pendingTX=', pendingTx?.toString())
logger.debug(
'XCom: settleSendCoins found pendingTX=',
pendingTx ? new PendingTransactionLoggingView(pendingTx) : 'null',
)
if (
pendingTx &&
pendingTx.amount.toString() === args.amount.toString() &&
@ -256,17 +235,12 @@ export class SendCoinsResolver {
} else {
logger.debug('XCom: settlePendingReceiveTransaction NOT matching pendingTX for settlement...')
throw new LogError(
`Can't find in settlePendingReceiveTransaction the pending receiver TX for args=`,
args.recipientCommunityUuid,
args.recipientUserIdentifier,
PendingTransactionState.NEW,
TransactionTypeId.RECEIVE,
args.creationDate,
args.amount,
args.memo,
args.senderCommunityUuid,
args.senderUserUuid,
args.senderUserName,
`Can't find in settlePendingReceiveTransaction the pending receiver TX for `,
{
args: new SendCoinsArgsLoggingView(args),
pendingTransactionState: PendingTransactionState.NEW,
transactionTypeId: TransactionTypeId.RECEIVE,
},
)
}
}
@ -307,7 +281,10 @@ export class SendCoinsResolver {
linkedUserCommunityUuid: args.senderCommunityUuid,
linkedUserGradidoID: args.senderUserUuid,
})
logger.debug('XCom: revertSettledSendCoins found pendingTX=', pendingTx)
logger.debug(
'XCom: revertSettledSendCoins found pendingTX=',
pendingTx ? new PendingTransactionLoggingView(pendingTx) : 'null',
)
if (
pendingTx &&
pendingTx.amount.toString() === args.amount.toString() &&
@ -322,19 +299,11 @@ export class SendCoinsResolver {
}
} else {
logger.debug('XCom: revertSettledSendCoins NOT matching pendingTX...')
throw new LogError(
`Can't find in revertSettledSendCoins the pending receiver TX for args=`,
args.recipientCommunityUuid,
args.recipientUserIdentifier,
PendingTransactionState.SETTLED,
TransactionTypeId.RECEIVE,
args.creationDate,
args.amount,
args.memo,
args.senderCommunityUuid,
args.senderUserUuid,
args.senderUserName,
)
throw new LogError(`Can't find in revertSettledSendCoins the pending receiver TX for `, {
args: new SendCoinsArgsLoggingView(args),
pendingTransactionState: PendingTransactionState.SETTLED,
transactionTypeId: TransactionTypeId.RECEIVE,
})
}
logger.debug(`revertSendCoins()-1_0... successfull`)
return true

View File

@ -9,13 +9,18 @@ import { AuthenticationClientFactory } from '@/client/AuthenticationClientFactor
// eslint-disable-next-line camelcase
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/client/1_0/AuthenticationClient'
import { AuthenticationArgs } from '../model/AuthenticationArgs'
import { CommunityLoggingView } from '@logging/CommunityLogging.view'
import { FederatedCommunityLoggingView } from '@logging/FederatedCommunityLogging.view'
export async function startOpenConnectionCallback(
args: OpenConnectionArgs,
comA: DbCommunity,
api: string,
): Promise<void> {
logger.debug(`Authentication: startOpenConnectionCallback() with:`, args, comA)
logger.debug(`Authentication: startOpenConnectionCallback() with:`, {
args,
comA: new CommunityLoggingView(comA),
})
try {
const homeFedCom = await DbFedCommunity.findOneByOrFail({
foreign: false,
@ -30,7 +35,10 @@ export async function startOpenConnectionCallback(
// store oneTimeCode in requestedCom.community_uuid as authenticate-request-identifier
comA.communityUuid = oneTimeCode.toString()
await DbCommunity.save(comA)
logger.debug(`Authentication: stored oneTimeCode in requestedCom:`, comA)
logger.debug(
`Authentication: stored oneTimeCode in requestedCom:`,
new CommunityLoggingView(comA),
)
const client = AuthenticationClientFactory.getInstance(fedComA)
// eslint-disable-next-line camelcase
@ -57,7 +65,10 @@ export async function startAuthentication(
oneTimeCode: string,
fedComB: DbFedCommunity,
): Promise<void> {
logger.debug(`Authentication: startAuthentication()...`, oneTimeCode, fedComB)
logger.debug(`Authentication: startAuthentication()...`, {
oneTimeCode,
fedComB: new FederatedCommunityLoggingView(fedComB),
})
try {
const homeCom = await DbCommunity.findOneByOrFail({ foreign: false })
@ -78,7 +89,7 @@ export async function startAuthentication(
logger.debug(
`Authentication: received communityUUid for callbackFedCom:`,
fedComUuid,
fedComB,
new FederatedCommunityLoggingView(fedComB),
)
const callbackCom = await DbCommunity.findOneByOrFail({
foreign: true,
@ -88,7 +99,10 @@ export async function startAuthentication(
callbackCom.communityUuid = fedComUuid
callbackCom.authenticatedAt = new Date()
await DbCommunity.save(callbackCom)
logger.debug('Authentication: Community Authentication successful:', callbackCom)
logger.debug(
'Authentication: Community Authentication successful:',
new CommunityLoggingView(callbackCom),
)
} else {
logger.error('Authentication: Community Authentication failed:', authenticationArgs)
}

View File

@ -15,6 +15,10 @@ import { federationLogger as logger } from '@/server/logger'
import { getLastTransaction } from '@/graphql/util/getLastTransaction'
import { TRANSACTIONS_LOCK } from '@/graphql/util/TRANSACTIONS_LOCK'
import { CommunityLoggingView } from '@logging/CommunityLogging.view'
import { UserLoggingView } from '@logging/UserLogging.view'
import { PendingTransactionLoggingView } from '@logging/PendingTransactionLogging.view'
import { TransactionLoggingView } from '@logging/TransactionLogging.view'
export async function revertSettledReceiveTransaction(
homeCom: DbCommunity,
@ -30,7 +34,11 @@ export async function revertSettledReceiveTransaction(
logger.debug(`start Transaction for write-access...`)
try {
logger.info('X-Com: revertSettledReceiveTransaction:', homeCom, receiverUser, pendingTx)
logger.info('X-Com: revertSettledReceiveTransaction:', {
homeCom: new CommunityLoggingView(homeCom),
receiverUser: new UserLoggingView(receiverUser),
pendingTx: new PendingTransactionLoggingView(pendingTx),
})
// ensure that no other pendingTx with the same sender or recipient exists
const openSenderPendingTx = await DbPendingTransaction.count({
@ -68,6 +76,7 @@ export async function revertSettledReceiveTransaction(
pendingTx.balanceDate.toISOString(),
)
logger.debug(`GradidoID:`, lastTransaction?.userGradidoID, pendingTx.userGradidoID)
// todo: Data privacy: personal user data in log file?
logger.debug(`Name:`, lastTransaction?.userName, pendingTx.userName)
logger.debug(`amount:`, lastTransaction?.amount.toString(), pendingTx.amount.toString())
logger.debug(`memo:`, lastTransaction?.memo, pendingTx.memo)
@ -90,7 +99,10 @@ export async function revertSettledReceiveTransaction(
lastTransaction.linkedUserName === pendingTx.linkedUserName
) {
await queryRunner.manager.remove(dbTransaction, lastTransaction)
logger.debug(`X-Com: revert settlement receive Transaction removed:`, lastTransaction)
logger.debug(
`X-Com: revert settlement receive Transaction removed:`,
new TransactionLoggingView(lastTransaction),
)
// and mark the pendingTx in the pending_transactions table as reverted
pendingTx.state = PendingTransactionState.REVERTED
await queryRunner.manager.save(DbPendingTransaction, pendingTx)
@ -98,12 +110,11 @@ export async function revertSettledReceiveTransaction(
await queryRunner.commitTransaction()
logger.debug(`commit revert settlement recipient Transaction successful...`)
} else {
// TODO: if the last TX is not equivelant to pendingTX, the transactions must be corrected in EXPERT-MODE
throw new LogError(
`X-Com: missmatching transaction order for revert settlement!`,
lastTransaction,
pendingTx,
)
// TODO: if the last TX is not equivalent to pendingTX, the transactions must be corrected in EXPERT-MODE
throw new LogError(`X-Com: mismatching transaction order for revert settlement!`, {
lastTransaction: lastTransaction ? new TransactionLoggingView(lastTransaction) : 'null',
pendingTx: new PendingTransactionLoggingView(pendingTx),
})
}
/*

View File

@ -17,6 +17,10 @@ import { getLastTransaction } from '@/graphql/util/getLastTransaction'
import { TRANSACTIONS_LOCK } from '@/graphql/util/TRANSACTIONS_LOCK'
import { calculateRecipientBalance } from './calculateRecipientBalance'
import Decimal from 'decimal.js-light'
import { CommunityLoggingView } from '@logging/CommunityLogging.view'
import { UserLoggingView } from '@logging/UserLogging.view'
import { PendingTransactionLoggingView } from '@logging/PendingTransactionLogging.view'
import { TransactionLoggingView } from '@logging/TransactionLogging.view'
export async function settlePendingReceiveTransaction(
homeCom: DbCommunity,
@ -32,7 +36,11 @@ export async function settlePendingReceiveTransaction(
logger.debug(`start Transaction for write-access...`)
try {
logger.info('X-Com: settlePendingReceiveTransaction:', homeCom, receiverUser, pendingTx)
logger.info('X-Com: settlePendingReceiveTransaction:', {
homeCom: new CommunityLoggingView(homeCom),
receiverUser: new UserLoggingView(receiverUser),
pendingTx: new PendingTransactionLoggingView(pendingTx),
})
// ensure that no other pendingTx with the same sender or recipient exists
const openSenderPendingTx = await DbPendingTransaction.count({
@ -84,7 +92,7 @@ export async function settlePendingReceiveTransaction(
transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null
transactionReceive.linkedTransactionId = pendingTx.linkedTransactionId
await queryRunner.manager.insert(dbTransaction, transactionReceive)
logger.debug(`receive Transaction inserted: ${dbTransaction}`)
logger.debug(`receive Transaction inserted: ${new TransactionLoggingView(transactionReceive)}`)
// and mark the pendingTx in the pending_transactions table as settled
pendingTx.state = PendingTransactionState.SETTLED

View File

@ -2,6 +2,8 @@ import { User as DbUser } from '@entity/User'
import { federationLogger as logger } from '@/server/logger'
import { SendCoinsArgs } from '../model/SendCoinsArgs'
import { UserLoggingView } from '@logging/UserLogging.view'
import { SendCoinsArgsLoggingView } from '../logger/SendCoinsArgsLogging.view'
export async function storeForeignUser(args: SendCoinsArgs): Promise<boolean> {
if (args.senderCommunityUuid !== null && args.senderUserUuid !== null) {
@ -34,7 +36,7 @@ export async function storeForeignUser(args: SendCoinsArgs): Promise<boolean> {
}
foreignUser.gradidoID = args.senderUserUuid
foreignUser = await DbUser.save(foreignUser)
logger.debug('X-Com: new foreignUser inserted:', foreignUser)
logger.debug('X-Com: new foreignUser inserted:', new UserLoggingView(foreignUser))
return true
} else if (
@ -43,14 +45,13 @@ export async function storeForeignUser(args: SendCoinsArgs): Promise<boolean> {
args.senderUserName.slice(args.senderUserName.indexOf(' '), args.senderUserName.length) ||
user.alias !== args.senderAlias
) {
logger.warn(
'X-Com: foreignUser still exists, but with different name or alias:',
user,
args,
)
logger.warn('X-Com: foreignUser still exists, but with different name or alias:', {
user: new UserLoggingView(user),
args: new SendCoinsArgsLoggingView(args),
})
return false
} else {
logger.debug('X-Com: foreignUser still exists...:', user)
logger.debug('X-Com: foreignUser still exists...:', new UserLoggingView(user))
return true
}
} catch (err) {

View File

@ -39,7 +39,10 @@ describe('PublicKeyResolver', () => {
homeCom.foreign = false
homeCom.apiVersion = '1_0'
homeCom.endPoint = 'endpoint-url'
homeCom.publicKey = Buffer.from('homeCommunity-publicKey')
homeCom.publicKey = Buffer.from(
'9f6dcd0d985cc7105cd71c3417d9c291b126c8ca90513197de02191f928ef713',
'hex',
)
await DbFederatedCommunity.insert(homeCom)
})
@ -47,7 +50,7 @@ describe('PublicKeyResolver', () => {
await expect(query({ query: getPublicKeyQuery })).resolves.toMatchObject({
data: {
getPublicKey: {
publicKey: expect.stringMatching('homeCommunity-publicKey'),
publicKey: '9f6dcd0d985cc7105cd71c3417d9c291b126c8ca90513197de02191f928ef713',
},
},
})

View File

@ -60,7 +60,8 @@
/* external */
"@typeorm/*": ["../backend/src/typeorm/*", "../../backend/src/typeorm/*"],
"@dbTools/*": ["../database/src/*", "../../database/build/src/*"],
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"]
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"],
"@logging/*": ["../database/logging/*", "../../database/build/logging/*"]
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": ["node_modules/@types"], /* List of folders to include type definitions from. */