use new logging approach in federation

This commit is contained in:
einhornimmond 2025-06-15 14:07:42 +02:00
parent bb9759ace3
commit a7d011b814
40 changed files with 190 additions and 327 deletions

16
.vscode/launch.json vendored
View File

@ -26,6 +26,22 @@
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"cwd": "${workspaceFolder}/dht-node"
},
{
"type": "node",
"request": "launch",
"name": "Federation Debug Tests",
"runtimeExecutable": "yarn",
"runtimeArgs": [
"run",
"test:debug"
],
"skipFiles": [
"<node_internals>/**"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"cwd": "${workspaceFolder}/federation"
}
]
}

View File

@ -1,7 +1,6 @@
import { validate } from 'config-schema'
import type { LogLevel } from 'config-schema'
import dotenv from 'dotenv'
import { LogLevel } from 'config-schema/src/log4js-config'
import { schema } from './schema'
dotenv.config()

View File

@ -12,4 +12,5 @@ build({
external: ['sodium-native'],
plugins: [esbuildDecorators()],
minify: true,
sourcemap: true,
})

View File

@ -2,14 +2,14 @@
module.exports = {
verbose: true,
preset: 'ts-jest',
collectCoverage: true,
collectCoverage: false,
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
coverageThreshold: {
global: {
lines: 68,
},
},
setupFiles: ['<rootDir>/test/testSetup.ts'],
setupFiles: ['config-schema/test/testSetup.ts'],
setupFilesAfterEnv: [],
modulePathIgnorePatterns: ['<rootDir>/build/'],
moduleNameMapper: {

View File

@ -1,151 +0,0 @@
{
"appenders":
{
"access":
{
"type": "dateFile",
"filename": "../logs/federation/access-%p.log",
"pattern": "yyyy-MM-dd",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
},
"compress": true,
"keepFileExt" : true,
"fileNameSep" : "_",
"numBackups" : 30
},
"apollo":
{
"type": "dateFile",
"filename": "../logs/federation/apollo-%p.log",
"pattern": "yyyy-MM-dd",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
},
"compress": true,
"keepFileExt" : true,
"fileNameSep" : "_",
"numBackups" : 30
},
"backend":
{
"type": "dateFile",
"filename": "../logs/federation/backend-%p.log",
"pattern": "yyyy-MM-dd",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
},
"compress": true,
"keepFileExt" : true,
"fileNameSep" : "_",
"numBackups" : 30
},
"federation":
{
"type": "dateFile",
"filename": "../logs/federation/apiversion-%v-%p.log",
"pattern": "yyyy-MM-dd",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
},
"compress": true,
"keepFileExt" : true,
"fileNameSep" : "_",
"numBackups" : 30
},
"errorFile":
{
"type": "dateFile",
"filename": "../logs/federation/errors-%p.log",
"pattern": "yyyy-MM-dd",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
},
"compress": true,
"keepFileExt" : true,
"fileNameSep" : "_",
"numBackups" : 30
},
"errors":
{
"type": "logLevelFilter",
"level": "error",
"appender": "errorFile"
},
"out":
{
"type": "stdout",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
}
},
"apolloOut":
{
"type": "stdout",
"layout":
{
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
}
}
},
"categories":
{
"default":
{
"appenders":
[
"out",
"errors"
],
"level": "debug",
"enableCallStack": true
},
"apollo":
{
"appenders":
[
"apollo",
"apolloOut",
"errors"
],
"level": "debug",
"enableCallStack": true
},
"backend":
{
"appenders":
[
"backend",
"out",
"errors"
],
"level": "debug",
"enableCallStack": true
},
"federation":
{
"appenders":
[
"federation",
"out",
"errors"
],
"level": "debug",
"enableCallStack": true
},
"http":
{
"appenders":
[
"access"
],
"level": "info"
}
}
}

View File

@ -15,6 +15,8 @@
"dev:bun": "cross-env TZ=UTC bun --hot src/index.ts",
"typecheck": "tsc --noEmit",
"test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --runInBand --forceExit --detectOpenHandles",
"test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles",
"test:coverage": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --coverage --runInBand --forceExit --detectOpenHandles",
"lint": "biome check --error-on-warnings .",
"lint:fix": "biome check --error-on-warnings . --write"
},

View File

@ -1,12 +1,15 @@
import { federationLogger as logger } from '@/server/logger'
import { FederatedCommunity as DbFederatedCommunity } from 'database'
import { GraphQLClient } from 'graphql-request'
import { getLogger } from 'log4js'
import { AuthenticationArgs } from '@/graphql/api/1_0/model/AuthenticationArgs'
import { OpenConnectionCallbackArgs } from '@/graphql/api/1_0/model/OpenConnectionCallbackArgs'
import { LOG4JS_CLIENT_1_0_CATEGORY_NAME } from '.'
import { authenticate } from './query/authenticate'
import { openConnectionCallback } from './query/openConnectionCallback'
const logger = getLogger(`${LOG4JS_CLIENT_1_0_CATEGORY_NAME}.AuthenticationClient`)
export class AuthenticationClient {
dbCom: DbFederatedCommunity
endpoint: string
@ -27,41 +30,35 @@ export class AuthenticationClient {
}
async openConnectionCallback(args: OpenConnectionCallbackArgs): Promise<boolean> {
logger.debug('Authentication: openConnectionCallback with endpoint', this.endpoint, args)
logger.debug('openConnectionCallback with endpoint', this.endpoint, args)
try {
const { data } = await this.client.rawRequest<any>(openConnectionCallback, { args })
if (data && data.openConnectionCallback) {
logger.warn(
'Authentication: openConnectionCallback without response data from endpoint',
this.endpoint,
)
logger.warn('openConnectionCallback without response data from endpoint', this.endpoint)
return false
}
logger.debug(
'Authentication: openConnectionCallback successfully started with endpoint',
this.endpoint,
)
logger.debug('openConnectionCallback successfully started with endpoint', this.endpoint)
return true
} catch (err) {
logger.error('Authentication: error on openConnectionCallback', err)
logger.error('error on openConnectionCallback', err)
}
return false
}
async authenticate(args: AuthenticationArgs): Promise<string | null> {
logger.debug('Authentication: authenticate with endpoint=', this.endpoint)
logger.debug('authenticate with endpoint=', this.endpoint)
try {
const { data } = await this.client.rawRequest<any>(authenticate, { args })
logger.debug('Authentication: after authenticate: data:', data)
logger.debug('after authenticate: data:', data)
const authUuid: string = data?.authenticate
if (authUuid) {
logger.debug('Authentication: received authenticated uuid', authUuid)
logger.debug('received authenticated uuid', authUuid)
return authUuid
}
} catch (err) {
logger.error('Authentication: authenticate failed', {
logger.error('authenticate failed', {
endpoint: this.endpoint,
err,
})

View File

@ -0,0 +1,3 @@
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
export const LOG4JS_CLIENT_1_0_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.client.1_0`

View File

@ -0,0 +1 @@
export const LOG4JS_BASE_CATEGORY_NAME = 'federation'

View File

@ -3,6 +3,7 @@ import { Decimal } from 'decimal.js-light'
import dotenv from 'dotenv'
import { validate } from 'config-schema'
import type { LogLevel } from 'config-schema'
import { schema } from './schema'
@ -13,11 +14,12 @@ Decimal.set({
rounding: Decimal.ROUND_HALF_UP,
})
const constants = {
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
LOG4JS_CONFIG: 'log4js-config.json',
const logging = {
LOG4JS_CONFIG_PLACEHOLDER: process.env.LOG4JS_CONFIG_PLACEHOLDER ?? 'log4js-config-%v.json',
// default log level on production should be info
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
// log level for default log4js-config.json, don't change existing log4js-config.json
LOG_LEVEL: (process.env.LOG_LEVEL ?? 'info') as LogLevel,
LOG_FILES_BASE_PATH: process.env.LOG_FILES_BASE_PATH ?? '../logs/federation',
}
const server = {
@ -44,10 +46,8 @@ const federation = {
}
export const CONFIG = {
...constants,
...logging,
...server,
// ...community,
// ...eventProtocol,
...federation,
}

View File

@ -1,7 +1,8 @@
import {
DECAY_START_TIME,
GRAPHIQL,
LOG4JS_CONFIG,
LOG4JS_CONFIG_PLACEHOLDER,
LOG_FILES_BASE_PATH,
LOG_LEVEL,
NODE_ENV,
PRODUCTION,
@ -11,7 +12,8 @@ import Joi from 'joi'
export const schema = Joi.object({
DECAY_START_TIME,
GRAPHIQL,
LOG4JS_CONFIG,
LOG4JS_CONFIG_PLACEHOLDER,
LOG_FILES_BASE_PATH,
LOG_LEVEL,
NODE_ENV,
PRODUCTION,

View File

@ -0,0 +1,3 @@
import { LOG4JS_API_CATEGORY_NAME } from '@/graphql/api'
export const LOG4JS_API_1_0_CATEGORY_NAME = `${LOG4JS_API_CATEGORY_NAME}.1_0`

View File

@ -1,18 +1,21 @@
import { CONFIG } from '@/config'
import { LogError } from '@/server/LogError'
import { federationLogger as logger } from '@/server/logger'
import {
CommunityLoggingView,
Community as DbCommunity,
FederatedCommunity as DbFedCommunity,
FederatedCommunityLoggingView,
} from 'database'
import { getLogger } from 'log4js'
import { Arg, Mutation, Resolver } from 'type-graphql'
import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.'
import { AuthenticationArgs } from '../model/AuthenticationArgs'
import { OpenConnectionArgs } from '../model/OpenConnectionArgs'
import { OpenConnectionCallbackArgs } from '../model/OpenConnectionCallbackArgs'
import { startAuthentication, startOpenConnectionCallback } from '../util/authenticateCommunity'
const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.AuthenticationResolver`)
@Resolver()
export class AuthenticationResolver {
@Mutation(() => Boolean)
@ -21,7 +24,7 @@ export class AuthenticationResolver {
args: OpenConnectionArgs,
): Promise<boolean> {
const pubKeyBuf = Buffer.from(args.publicKey, 'hex')
logger.debug(`Authentication: openConnection() via apiVersion=1_0:`, args)
logger.debug(`openConnection() via apiVersion=1_0:`, args)
// first find with args.publicKey the community 'comA', which starts openConnection request
const comA = await DbCommunity.findOneBy({
@ -30,7 +33,7 @@ export class AuthenticationResolver {
if (!comA) {
throw new LogError(`unknown requesting community with publicKey`, pubKeyBuf.toString('hex'))
}
logger.debug(`Authentication: found requestedCom:`, new CommunityLoggingView(comA))
logger.debug(`found requestedCom:`, new CommunityLoggingView(comA))
// biome-ignore lint/complexity/noVoid: no await to respond immediately and invoke callback-request asynchronously
void startOpenConnectionCallback(args, comA, CONFIG.FEDERATION_API)
return true
@ -41,17 +44,17 @@ export class AuthenticationResolver {
@Arg('data')
args: OpenConnectionCallbackArgs,
): Promise<boolean> {
logger.debug(`Authentication: openConnectionCallback() via apiVersion=1_0 ...`, args)
logger.debug(`openConnectionCallback() via apiVersion=1_0 ...`, args)
// TODO decrypt args.url with homeCom.privateKey and verify signing with callbackFedCom.publicKey
const endPoint = args.url.slice(0, args.url.lastIndexOf('/') + 1)
const apiVersion = args.url.slice(args.url.lastIndexOf('/') + 1, args.url.length)
logger.debug(`Authentication: search fedComB per:`, endPoint, apiVersion)
logger.debug(`search fedComB per:`, endPoint, apiVersion)
const fedComB = await DbFedCommunity.findOneBy({ endPoint, apiVersion })
if (!fedComB) {
throw new LogError(`unknown callback community with url`, args.url)
}
logger.debug(
`Authentication: found fedComB and start authentication:`,
`found fedComB and start authentication:`,
new FederatedCommunityLoggingView(fedComB),
)
// biome-ignore lint/complexity/noVoid: no await to respond immediately and invoke authenticate-request asynchronously
@ -64,18 +67,15 @@ export class AuthenticationResolver {
@Arg('data')
args: AuthenticationArgs,
): Promise<string | null> {
logger.debug(`Authentication: authenticate() via apiVersion=1_0 ...`, args)
logger.debug(`authenticate() via apiVersion=1_0 ...`, args)
const authCom = await DbCommunity.findOneByOrFail({ communityUuid: args.oneTimeCode })
logger.debug('Authentication: found authCom:', new CommunityLoggingView(authCom))
logger.debug('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:',
new CommunityLoggingView(authCom),
)
logger.debug('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

@ -1,6 +1,8 @@
import { CONFIG } from '@/config'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
import { createServer } from '@/server/createServer'
import { createTestClient } from 'apollo-server-testing'
import { getLogger } from 'log4js'
import { Community as DbCommunity } from 'database'
import { DataSource } from 'typeorm'
@ -12,7 +14,7 @@ let con: DataSource
CONFIG.FEDERATION_API = '1_0'
beforeAll(async () => {
const server = await createServer()
const server = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`))
con = server.con
query = createTestClient(server.apollo).query
DbCommunity.clear()

View File

@ -1,9 +1,12 @@
import { federationLogger as logger } from '@/server/logger'
import { Community as DbCommunity } from 'database'
import { getLogger } from 'log4js'
import { Query, Resolver } from 'type-graphql'
import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.'
import { GetPublicCommunityInfoResultLoggingView } from '../logger/GetPublicCommunityInfoResultLogging.view'
import { GetPublicCommunityInfoResult } from '../model/GetPublicCommunityInfoResult'
const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.PublicCommunityInfoResolver`)
@Resolver()
export class PublicCommunityInfoResolver {
@Query(() => GetPublicCommunityInfoResult)

View File

@ -1,6 +1,8 @@
import { CONFIG } from '@/config'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
import { createServer } from '@/server/createServer'
import { createTestClient } from 'apollo-server-testing'
import { getLogger } from 'log4js'
import { FederatedCommunity as DbFederatedCommunity } from 'database'
let query: any
@ -11,7 +13,7 @@ let con: any
CONFIG.FEDERATION_API = '1_0'
beforeAll(async () => {
const server = await createServer()
const server = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`))
con = server.con
query = createTestClient(server.apollo).query
DbFederatedCommunity.clear()

View File

@ -1,8 +1,11 @@
import { federationLogger as logger } from '@/server/logger'
import { FederatedCommunity as DbFederatedCommunity } from 'database'
import { getLogger } from 'log4js'
import { Query, Resolver } from 'type-graphql'
import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.'
import { GetPublicKeyResult } from '../model/GetPublicKeyResult'
const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.PublicKeyResolver`)
@Resolver()
export class PublicKeyResolver {
@Query(() => GetPublicKeyResult)

View File

@ -1,8 +1,9 @@
import { CONFIG } from '@/config'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
import { fullName } from '@/graphql/util/fullName'
import { cleanDB, testEnvironment } from '@test/helpers'
import { logger } from '@test/testSetup'
import { ApolloServerTestClient } from 'apollo-server-testing'
import { getLogger } from 'log4js'
import { Community as DbCommunity, User as DbUser, UserContact as DbUserContact } from 'database'
import Decimal from 'decimal.js-light'
import { GraphQLError } from 'graphql'
@ -28,7 +29,7 @@ let recipUser: DbUser
let recipContact: DbUserContact
beforeAll(async () => {
testEnv = await testEnvironment(logger)
testEnv = await testEnvironment(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`))
mutate = testEnv.mutate
// query = testEnv.query
// con = testEnv.con

View File

@ -1,14 +1,15 @@
import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier'
import { fullName } from '@/graphql/util/fullName'
import { LogError } from '@/server/LogError'
import { federationLogger as logger } from '@/server/logger'
import {
Community as DbCommunity,
PendingTransaction as DbPendingTransaction,
PendingTransactionLoggingView,
} from 'database'
import Decimal from 'decimal.js-light'
import { getLogger } from 'log4js'
import { Arg, Mutation, Resolver } from 'type-graphql'
import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.'
import { PendingTransactionState } from '../enum/PendingTransactionState'
import { TransactionTypeId } from '../enum/TransactionTypeId'
import { SendCoinsArgsLoggingView } from '../logger/SendCoinsArgsLogging.view'
@ -20,6 +21,8 @@ import { revertSettledReceiveTransaction } from '../util/revertSettledReceiveTra
import { settlePendingReceiveTransaction } from '../util/settlePendingReceiveTransaction'
import { storeForeignUser } from '../util/storeForeignUser'
const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.SendCoinsResolver`)
@Resolver()
export class SendCoinsResolver {
@Mutation(() => SendCoinsResult)

View File

@ -0,0 +1,3 @@
import { LOG4JS_API_1_0_CATEGORY_NAME } from '@/graphql/api/1_0'
export const LOG4JS_RESOLVER_1_0_CATEGORY_NAME = `${LOG4JS_API_1_0_CATEGORY_NAME}.resolver`

View File

@ -1,10 +1,10 @@
import { federationLogger as logger } from '@/server/logger'
import {
CommunityLoggingView,
Community as DbCommunity,
FederatedCommunity as DbFedCommunity,
FederatedCommunityLoggingView,
} from 'database'
import { getLogger } from 'log4js'
import { OpenConnectionArgs } from '../model/OpenConnectionArgs'
import { OpenConnectionCallbackArgs } from '../model/OpenConnectionCallbackArgs'
@ -12,14 +12,17 @@ import { AuthenticationClientFactory } from '@/client/AuthenticationClientFactor
import { randombytes_random } from 'sodium-native'
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/client/1_0/AuthenticationClient'
import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.'
import { AuthenticationArgs } from '../model/AuthenticationArgs'
const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.authenticateCommunity`)
export async function startOpenConnectionCallback(
args: OpenConnectionArgs,
comA: DbCommunity,
api: string,
): Promise<void> {
logger.debug(`Authentication: startOpenConnectionCallback() with:`, {
logger.debug(`startOpenConnectionCallback() with:`, {
args,
comA: new CommunityLoggingView(comA),
})
@ -53,13 +56,13 @@ export async function startOpenConnectionCallback(
: homeFedCom.endPoint + '/' + homeFedCom.apiVersion
logger.debug(`Authentication: start openConnectionCallback with args:`, callbackArgs)
if (await client.openConnectionCallback(callbackArgs)) {
logger.debug('Authentication: startOpenConnectionCallback() successful:', callbackArgs)
logger.debug('startOpenConnectionCallback() successful:', callbackArgs)
} else {
logger.error('Authentication: startOpenConnectionCallback() failed:', callbackArgs)
logger.error('startOpenConnectionCallback() failed:', callbackArgs)
}
}
} catch (err) {
logger.error('Authentication: error in startOpenConnectionCallback:', err)
logger.error('error in startOpenConnectionCallback:', err)
}
}
@ -67,7 +70,7 @@ export async function startAuthentication(
oneTimeCode: string,
fedComB: DbFedCommunity,
): Promise<void> {
logger.debug(`Authentication: startAuthentication()...`, {
logger.debug(`startAuthentication()...`, {
oneTimeCode,
fedComB: new FederatedCommunityLoggingView(fedComB),
})
@ -84,12 +87,12 @@ export async function startAuthentication(
if (homeCom.communityUuid) {
authenticationArgs.uuid = homeCom.communityUuid
}
logger.debug(`Authentication: invoke authenticate() with:`, authenticationArgs)
logger.debug(`invoke authenticate() with:`, authenticationArgs)
const fedComUuid = await client.authenticate(authenticationArgs)
logger.debug(`Authentication: response of authenticate():`, fedComUuid)
logger.debug(`response of authenticate():`, fedComUuid)
if (fedComUuid !== null) {
logger.debug(
`Authentication: received communityUUid for callbackFedCom:`,
`received communityUUid for callbackFedCom:`,
fedComUuid,
new FederatedCommunityLoggingView(fedComB),
)
@ -101,15 +104,12 @@ export async function startAuthentication(
callbackCom.communityUuid = fedComUuid
callbackCom.authenticatedAt = new Date()
await DbCommunity.save(callbackCom)
logger.debug(
'Authentication: Community Authentication successful:',
new CommunityLoggingView(callbackCom),
)
logger.debug('Community Authentication successful:', new CommunityLoggingView(callbackCom))
} else {
logger.error('Authentication: Community Authentication failed:', authenticationArgs)
logger.error('Community Authentication failed:', authenticationArgs)
}
}
} catch (err) {
logger.error('Authentication: error in startOpenConnectionCallback:', err)
logger.error('error in startAuthentication:', err)
}
}

View File

@ -0,0 +1,3 @@
import { LOG4JS_API_1_0_CATEGORY_NAME } from '@/graphql/api/1_0'
export const LOG4JS_1_0_UTIL_CATEGORY_NAME = `${LOG4JS_API_1_0_CATEGORY_NAME}.util`

View File

@ -13,12 +13,14 @@ import {
import { PendingTransactionState } from '../enum/PendingTransactionState'
import { LogError } from '@/server/LogError'
import { federationLogger as logger } from '@/server/logger'
import { getLogger } from 'log4js'
import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.'
import { TRANSACTIONS_LOCK } from '@/graphql/util/TRANSACTIONS_LOCK'
import { getLastTransaction } from '@/graphql/util/getLastTransaction'
const db = AppDatabase.getInstance()
const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.revertSettledReceiveTransaction`)
export async function revertSettledReceiveTransaction(
homeCom: DbCommunity,

View File

@ -12,14 +12,16 @@ import {
import { PendingTransactionState } from '../enum/PendingTransactionState'
import { LogError } from '@/server/LogError'
import { federationLogger as logger } from '@/server/logger'
import { TRANSACTIONS_LOCK } from '@/graphql/util/TRANSACTIONS_LOCK'
import { getLastTransaction } from '@/graphql/util/getLastTransaction'
import Decimal from 'decimal.js-light'
import { getLogger } from 'log4js'
import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.'
import { calculateRecipientBalance } from './calculateRecipientBalance'
const db = AppDatabase.getInstance()
const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.settlePendingReceiveTransaction`)
export async function settlePendingReceiveTransaction(
homeCom: DbCommunity,

View File

@ -1,9 +1,12 @@
import { User as DbUser, UserLoggingView } from 'database'
import { federationLogger as logger } from '@/server/logger'
import { getLogger } from 'log4js'
import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.'
import { SendCoinsArgsLoggingView } from '../logger/SendCoinsArgsLogging.view'
import { SendCoinsArgs } from '../model/SendCoinsArgs'
const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.storeForeignUser`)
export async function storeForeignUser(args: SendCoinsArgs): Promise<boolean> {
if (args.senderCommunityUuid !== null && args.senderUserUuid !== null) {
try {

View File

@ -1,6 +1,8 @@
import { CONFIG } from '@/config'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
import { createServer } from '@/server/createServer'
import { createTestClient } from 'apollo-server-testing'
import { getLogger } from 'log4js'
import { FederatedCommunity as DbFederatedCommunity } from 'database'
let query: any
@ -11,7 +13,7 @@ let con: any
CONFIG.FEDERATION_API = '1_1'
beforeAll(async () => {
const server = await createServer()
const server = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`))
con = server.con
query = createTestClient(server.apollo).query
DbFederatedCommunity.clear()

View File

@ -1,13 +1,16 @@
import { federationLogger as logger } from '@/server/logger'
import { FederatedCommunity as DbFederatedCommunity } from 'database'
import { getLogger } from 'log4js'
import { Query, Resolver } from 'type-graphql'
import { LOG4JS_RESOLVER_1_1_CATEGORY_NAME } from '.'
import { GetPublicKeyResult } from '../../1_0/model/GetPublicKeyResult'
const logger = getLogger(`${LOG4JS_RESOLVER_1_1_CATEGORY_NAME}.PublicKeyResolver`)
@Resolver()
export class PublicKeyResolver {
@Query(() => GetPublicKeyResult)
async getPublicKey(): Promise<GetPublicKeyResult> {
logger.debug(`getPublicKey() via apiVersion=1_0 ...`)
logger.debug(`getPublicKey()...`)
const homeCom = await DbFederatedCommunity.findOneOrFail({
where: {
foreign: false,
@ -15,7 +18,7 @@ export class PublicKeyResolver {
},
})
const publicKeyHex = homeCom.publicKey.toString('hex')
logger.debug(`getPublicKey()-1_1... return publicKey=${publicKeyHex}`)
logger.debug(`getPublicKey()... return publicKey=${publicKeyHex}`)
return new GetPublicKeyResult(publicKeyHex)
}
}

View File

@ -0,0 +1,3 @@
import { LOG4JS_API_CATEGORY_NAME } from '@/graphql/api'
export const LOG4JS_RESOLVER_1_1_CATEGORY_NAME = `${LOG4JS_API_CATEGORY_NAME}.1_1.resolver`

View File

@ -0,0 +1,3 @@
import { LOG4JS_GRAPHQL_CATEGORY_NAME } from '@/graphql'
export const LOG4JS_API_CATEGORY_NAME = `${LOG4JS_GRAPHQL_CATEGORY_NAME}.api`

View File

@ -1,12 +1,13 @@
import { federationLogger as logger } from '@/server/logger'
import { getLogger } from 'log4js'
import { NonEmptyArray } from 'type-graphql'
import { LOG4JS_API_CATEGORY_NAME } from '.'
// config
import { CONFIG } from '../../config'
import { getApiResolvers as getApiResolvers_1_0 } from './1_0/schema'
import { getApiResolvers as getApiResolvers_1_1 } from './1_1/schema'
export const getApiResolvers = (): NonEmptyArray<Function> => {
logger.info(`getApiResolvers...${CONFIG.FEDERATION_API}`)
getLogger(LOG4JS_API_CATEGORY_NAME).info(`getApiResolvers...${CONFIG.FEDERATION_API}`)
if (CONFIG.FEDERATION_API === '1_0') {
return getApiResolvers_1_0()

View File

@ -0,0 +1,3 @@
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
export const LOG4JS_GRAPHQL_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.graphql`

View File

@ -1,9 +1,12 @@
import { CONFIG } from '@/config'
import { federationLogger as logger } from '@/server/logger'
import { LOG4JS_GRAPHQL_UTIL_CATEGORY_NAME } from '@/graphql/util'
import { Community as DbCommunity } from 'database'
import { Decimal } from 'decimal.js-light'
import { getLogger } from 'log4js'
export async function checkTradingLevel(homeCom: DbCommunity, amount: Decimal): Promise<boolean> {
const logger = getLogger(`${LOG4JS_GRAPHQL_UTIL_CATEGORY_NAME}.checkTradingLevel`)
const tradingLevel = CONFIG.FEDERATION_TRADING_LEVEL
if (homeCom.url !== tradingLevel.RECEIVER_COMMUNITY_URL) {
logger.warn(

View File

@ -1,13 +1,17 @@
import { Decimal } from 'decimal.js-light'
import { CONFIG } from '@/config'
import { LogError } from '@/server/LogError'
import { DECAY_START_TIME } from 'config-schema'
import { Decay } from '../api/1_0/model/Decay'
// TODO: externalize all those definitions and functions into an external decay library
Decimal.set({
precision: 25,
rounding: Decimal.ROUND_HALF_UP,
})
// TODO: externalize all those definitions and functions into an external decay library
function decayFormula(value: Decimal, seconds: number): Decimal {
// TODO why do we need to convert this here to a stting to work properly?
// TODO why do we need to convert this here to a string to work properly?
return value.mul(
new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds).toString(),
)
@ -17,7 +21,7 @@ function calculateDecay(
amount: Decimal,
from: Date,
to: Date,
startBlock: Date = CONFIG.DECAY_START_TIME,
startBlock: Date = DECAY_START_TIME,
): Decay {
const fromMs = from.getTime()
const toMs = to.getTime()

View File

@ -0,0 +1,3 @@
import { LOG4JS_GRAPHQL_CATEGORY_NAME } from '@/graphql'
export const LOG4JS_GRAPHQL_UTIL_CATEGORY_NAME = `${LOG4JS_GRAPHQL_CATEGORY_NAME}.util`

View File

@ -1,21 +1,28 @@
import { createServer } from './server/createServer'
import { defaultCategory, initLogger } from 'config-schema'
import { getLogger } from 'log4js'
// config
import { CONFIG } from './config'
import { LOG4JS_BASE_CATEGORY_NAME } from './config/const'
async function main() {
// biome-ignore lint/suspicious/noConsole: no logger needed fot startup infos
console.log(`FEDERATION_PORT=${CONFIG.FEDERATION_PORT}`)
// biome-ignore lint/suspicious/noConsole: no logger needed fot startup infos
console.log(`FEDERATION_API=${CONFIG.FEDERATION_API}`)
const { app } = await createServer()
// init logger
const log4jsConfigFileName = CONFIG.LOG4JS_CONFIG_PLACEHOLDER.replace('%v', CONFIG.FEDERATION_API)
initLogger(
[defaultCategory('federation', CONFIG.LOG_LEVEL), defaultCategory('apollo', CONFIG.LOG_LEVEL)],
`${CONFIG.LOG_FILES_BASE_PATH}_${CONFIG.FEDERATION_API}`,
log4jsConfigFileName,
)
// init server
const { app } = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`))
app.listen(CONFIG.FEDERATION_PORT, () => {
// biome-ignore lint/suspicious/noConsole: no logger needed fot startup infos
console.log(`Server is running at http://localhost:${CONFIG.FEDERATION_PORT}`)
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}`)
logger.info(`Server is running at http://localhost:${CONFIG.FEDERATION_PORT}`)
if (CONFIG.GRAPHIQL) {
// biome-ignore lint/suspicious/noConsole: no logger needed fot startup infos
console.log(
logger.info(
`GraphIQL available at ${CONFIG.FEDERATION_COMMUNITY_URL}/api/${CONFIG.FEDERATION_API}`,
)
}

View File

@ -1,8 +1,23 @@
import { federationLogger as logger } from './logger'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
import { getLogger } from 'log4js'
/**
* A custom Error that logs itself immediately upon instantiation.
*
* TODO: Anti-pattern warning:
* Logging inside the constructor introduces side effects during object creation,
* which breaks separation of concerns and can lead to duplicate or unwanted logs.
* It is generally better to log errors where they are caught, not where they are thrown.
*
* @class LogError
* @extends {Error}
* @param {string} msg - The error message.
* @param {...any} details - Additional details passed to the logger.
*/
export class LogError extends Error {
constructor(msg: string, ...details: any[]) {
super(msg)
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.logError`)
logger.error(msg, ...details)
}
}

View File

@ -11,15 +11,12 @@ import { plugins } from './plugins'
// graphql
import { schema } from '@/graphql/schema'
// webhooks
// import { elopageWebhook } from '@/webhook/elopage'
import { AppDatabase } from 'database'
import { slowDown } from 'express-slow-down'
import helmet from 'helmet'
import { Logger } from 'log4js'
import { DataSource } from 'typeorm'
import { apolloLogger } from './logger'
// i18n
// import { i18n } from './localization'
@ -30,12 +27,9 @@ type ServerDef = { apollo: ApolloServer; app: Express; con: DataSource }
export const createServer = async (
// context: any = serverContext,
logger: Logger = apolloLogger,
apolloLogger: Logger,
// localization: i18n.I18n = i18n,
): Promise<ServerDef> => {
logger.addContext('user', 'unknown')
logger.debug('createServer...')
// open mysql connection
const db = AppDatabase.getInstance()
await db.init()
@ -77,9 +71,6 @@ export const createServer = async (
// i18n
// app.use(localization.init)
// Elopage Webhook
// app.post('/hook/elopage/' + CONFIG.WEBHOOK_ELOPAGE_SECRET, elopageWebhook)
// Apollo Server
const apollo = new ApolloServer({
schema: await schema(),
@ -87,10 +78,8 @@ export const createServer = async (
// introspection: CONFIG.GRAPHIQL,
// context,
plugins,
logger,
logger: apolloLogger,
})
apollo.applyMiddleware({ app, path: '/' })
logger.debug('createServer...successful')
return { apollo, app, con: db.getDataSource() }
}

View File

@ -1,31 +0,0 @@
import { CONFIG } from '@/config'
import log4js from 'log4js'
import { readFileSync } from 'fs'
const options = JSON.parse(readFileSync(CONFIG.LOG4JS_CONFIG, 'utf-8'))
options.categories.backend.level = CONFIG.LOG_LEVEL
options.categories.apollo.level = CONFIG.LOG_LEVEL
let filename: string = options.appenders.federation.filename
options.appenders.federation.filename = filename
.replace('%v', CONFIG.FEDERATION_API)
.replace('%p', CONFIG.FEDERATION_PORT.toString())
filename = options.appenders.access.filename
options.appenders.access.filename = filename.replace('%p', CONFIG.FEDERATION_PORT.toString())
filename = options.appenders.apollo.filename
options.appenders.apollo.filename = filename.replace('%p', CONFIG.FEDERATION_PORT.toString())
filename = options.appenders.backend.filename
options.appenders.backend.filename = filename.replace('%p', CONFIG.FEDERATION_PORT.toString())
filename = options.appenders.errorFile.filename
options.appenders.errorFile.filename = filename.replace('%p', CONFIG.FEDERATION_PORT.toString())
log4js.configure(options)
const apolloLogger = log4js.getLogger('apollo')
// const backendLogger = log4js.getLogger('backend')
const federationLogger = log4js.getLogger('federation')
// backendLogger.addContext('user', 'unknown')
export { apolloLogger, federationLogger }

View File

@ -3,7 +3,8 @@ import { createTestClient } from 'apollo-server-testing'
import { createServer } from '@/server/createServer'
import { logger } from './testSetup'
import { getLogger } from 'config-schema/test/testSetup'
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
export const headerPushMock = jest.fn((t) => {
context.token = t.value
@ -25,7 +26,7 @@ export const cleanDB = async () => {
}
}
export const testEnvironment = async (testLogger = logger /*, testI18n = i18n */) => {
export const testEnvironment = async (testLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`) /*, testI18n = i18n */) => {
const server = await createServer(/* context, */ testLogger /* , testI18n */)
const con = server.con
const testClient = createTestClient(server.apollo)

View File

@ -1,43 +0,0 @@
// import { CONFIG } from '@/config'
// import { i18n } from '@/server/localization'
import { federationLogger as logger } from '@/server/logger'
// CONFIG.EMAIL = true
// CONFIG.EMAIL_TEST_MODUS = false
jest.setTimeout(1000000)
jest.mock('@/server/logger', () => {
const originalModule = jest.requireActual<typeof logger>('@/server/logger')
return {
__esModule: true,
...originalModule,
backendLogger: {
addContext: jest.fn(),
trace: jest.fn(),
debug: jest.fn(),
warn: jest.fn(),
info: jest.fn(),
error: jest.fn(),
fatal: jest.fn(),
},
}
})
/*
jest.mock('@/server/localization', () => {
const originalModule = jest.requireActual<typeof i18n>('@/server/localization')
return {
__esModule: true,
...originalModule,
i18n: {
init: jest.fn(),
// configure: jest.fn(),
// __: jest.fn(),
// setLocale: jest.fn(),
},
}
})
*/
export { logger }