mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge pull request #3506 from gradido/upgrade_logging
refactor(other): centralize logging code, use log4js config-generator
This commit is contained in:
commit
e47b111993
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
- name: Setup Biome
|
||||
uses: biomejs/setup-biome@v2
|
||||
with:
|
||||
version: latest
|
||||
version: 2.0.0
|
||||
- name: Lint - Config-Schema
|
||||
id: config-schema
|
||||
run: |
|
||||
|
||||
5
.github/workflows/test_config.yml
vendored
5
.github/workflows/test_config.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
list-files: shell
|
||||
|
||||
build:
|
||||
name: typecheck - Config-Schema
|
||||
name: Unit Tests, typecheck - Config-Schema
|
||||
if: needs.files-changed.outputs.config == 'true' || needs.files-changed.outputs.docker-compose == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
@ -38,3 +38,6 @@ jobs:
|
||||
- name: typecheck
|
||||
run: cd config-schema && yarn typecheck
|
||||
|
||||
- name: unit tests
|
||||
run: cd config-schema && yarn test
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@
|
||||
*.bak
|
||||
.turbo
|
||||
vite.config.mjs.timestamp-*
|
||||
log4js-config*.json
|
||||
/node_modules/*
|
||||
messages.pot
|
||||
nbproject
|
||||
|
||||
84
.vscode/launch.json
vendored
84
.vscode/launch.json
vendored
@ -5,11 +5,87 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}/frontend",
|
||||
"name": "DHT-Node Debug Tests",
|
||||
"runtimeExecutable": "yarn",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"test:debug"
|
||||
],
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"cwd": "${workspaceFolder}/dht-node"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "DHT-Node Debug",
|
||||
"stopOnEntry": true,
|
||||
"runtimeExecutable": "yarn",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"dev"
|
||||
],
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Federation Debug",
|
||||
"stopOnEntry": true,
|
||||
"runtimeExecutable": "yarn",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"dev"
|
||||
],
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"cwd": "${workspaceFolder}/federation"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Backend Debug",
|
||||
"stopOnEntry": true,
|
||||
"runtimeExecutable": "yarn",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"dev"
|
||||
],
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"cwd": "${workspaceFolder}/backend"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -113,8 +113,6 @@ COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/backend/build/worker.js ./wo
|
||||
# add node_modules from production_node_modules
|
||||
COPY --chown=app:app --from=production-node-modules ${DOCKER_WORKDIR}/node_modules ./node_modules
|
||||
|
||||
# Copy log4js-config.json to provide log configuration
|
||||
COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/backend/log4js-config.json ./log4js-config.json
|
||||
# Copy locales
|
||||
COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/backend/locales ./locales
|
||||
|
||||
|
||||
@ -13,4 +13,5 @@ build({
|
||||
external: ['sodium-native', 'email-templates'],
|
||||
plugins: [esbuildDecorators()],
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
})
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
preset: 'ts-jest',
|
||||
collectCoverage: true,
|
||||
collectCoverage: false,
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
|
||||
@ -1,173 +0,0 @@
|
||||
{
|
||||
"appenders":
|
||||
{
|
||||
"access":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/backend/access.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/backend/apollo.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/backend/backend.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
|
||||
},
|
||||
"klicktipp":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/backend/klicktipp.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
|
||||
},
|
||||
"gms":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/backend/gms.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/backend/errors.log",
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m %s"
|
||||
},
|
||||
"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",
|
||||
"errors"
|
||||
],
|
||||
"level": "debug",
|
||||
"enableCallStack": true
|
||||
},
|
||||
"klicktipp":
|
||||
{
|
||||
"appenders":
|
||||
[
|
||||
"klicktipp",
|
||||
"errors"
|
||||
],
|
||||
"level": "debug",
|
||||
"enableCallStack": true
|
||||
},
|
||||
"gms":
|
||||
{
|
||||
"appenders":
|
||||
[
|
||||
"gms",
|
||||
"errors"
|
||||
],
|
||||
"level": "debug",
|
||||
"enableCallStack": true
|
||||
},
|
||||
"http":
|
||||
{
|
||||
"appenders":
|
||||
[
|
||||
"access"
|
||||
],
|
||||
"level": "info"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,9 +9,9 @@
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"build": "ts-node ./esbuild.config.ts && mkdirp build/templates/ && ncp src/emails/templates build/templates && mkdirp locales/ && ncp src/locales locales",
|
||||
"clean": "tsc --build --clean",
|
||||
"dev": "cross-env TZ=UTC nodemon -w src --ext ts,pug,json,css -r tsconfig-paths/register src/index.ts",
|
||||
"test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_backend jest --runInBand --forceExit --detectOpenHandles",
|
||||
"test:coverage": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_backend jest --coverage --runInBand --forceExit --detectOpenHandles",
|
||||
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
|
||||
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/executeKlicktipp.ts",
|
||||
"gmsusers": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/gms/ExportUsers.ts",
|
||||
@ -21,11 +21,13 @@
|
||||
"lint:fix:unsafe": "biome check --fix --unsafe",
|
||||
"locales": "scripts/sort.sh",
|
||||
"locales:fix": "scripts/sort.sh --fix",
|
||||
"start": "cross-env TZ=UTC NODE_ENV=production node build/index.js",
|
||||
"start": "cross-env TZ=UTC node build/index.js",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"ignore": ["**/*.test.ts"]
|
||||
"ignore": [
|
||||
"**/*.test.ts"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
@ -34,7 +36,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anatine/esbuild-decorators": "^0.2.19",
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@swc/cli": "^0.7.3",
|
||||
"@swc/core": "^1.11.24",
|
||||
"@swc/helpers": "^0.5.17",
|
||||
@ -47,6 +49,7 @@
|
||||
"@types/node": "^17.0.21",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/sodium-native": "^2.3.5",
|
||||
"@types/source-map-support": "^0.5.10",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"apollo-server-express": "^2.25.2",
|
||||
"apollo-server-testing": "^2.25.2",
|
||||
@ -85,6 +88,7 @@
|
||||
"random-bigint": "^0.0.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-jest": "27.0.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.1.1",
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import axios from 'axios'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.HttpRequest`)
|
||||
|
||||
import { httpAgent, httpsAgent } from './ConnectionAgents'
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { CONFIG } from '@/config'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import KlicktippConnector from 'klicktipp-api'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const klicktippConnector = new KlicktippConnector()
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.KlicktippController`)
|
||||
|
||||
export const subscribe = async (
|
||||
email: string,
|
||||
|
||||
@ -6,7 +6,6 @@ import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { DltConnectorClient } from './DltConnectorClient'
|
||||
|
||||
@ -76,7 +75,7 @@ describe.skip('transmitTransaction, without db connection', () => {
|
||||
|
||||
describe('transmitTransaction', () => {
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment(logger)
|
||||
testEnv = await testEnvironment()
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
@ -2,13 +2,16 @@ import { Transaction as DbTransaction } from 'database'
|
||||
import { GraphQLClient, gql } from 'graphql-request'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { TransactionResult } from './model/TransactionResult'
|
||||
import { UserIdentifier } from './model/UserIdentifier'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.dltConnector`)
|
||||
|
||||
const sendTransaction = gql`
|
||||
mutation ($input: TransactionInput!) {
|
||||
sendTransaction(data: $input) {
|
||||
|
||||
@ -1,20 +1,25 @@
|
||||
import { User as DbUser } from 'database'
|
||||
// import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
// import { createGmsUser } from '@/apis/gms/GmsClient'
|
||||
// import { GmsUser } from '@/apis/gms/model/GmsUser'
|
||||
import { CONFIG } from '@/config'
|
||||
import { getHomeCommunity } from '@/graphql/resolver/util/communities'
|
||||
import { sendUserToGms } from '@/graphql/resolver/util/sendUserToGms'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { initLogging } from '@/server/logger'
|
||||
import { AppDatabase } from 'database'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.gms.ExportUsers`)
|
||||
|
||||
CONFIG.EMAIL = false
|
||||
// use force to copy over all user even if gmsRegistered is set to true
|
||||
const forceMode = process.argv.includes('--force')
|
||||
|
||||
async function main() {
|
||||
initLogging()
|
||||
// open mysql connection
|
||||
const con = AppDatabase.getInstance()
|
||||
await con.init()
|
||||
@ -70,7 +75,6 @@ async function main() {
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
// biome-ignore lint/suspicious/noConsole: logger isn't used here
|
||||
console.error(e)
|
||||
logger.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import axios from 'axios'
|
||||
|
||||
import { httpAgent, httpsAgent } from '@/apis/ConnectionAgents'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { GmsUser } from './model/GmsUser'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.gms.GmsClient`)
|
||||
|
||||
/*
|
||||
export async function communityList(): Promise<GmsCommunity[] | string | undefined> {
|
||||
const baseUrl = ensureUrlEndsWithSlash(CONFIG.GMS_URL)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { AppDatabase, User } from 'database'
|
||||
import { IsNull, Not } from 'typeorm'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { initLogging } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
import { HumHubClient } from './HumHubClient'
|
||||
import { GetUser } from './model/GetUser'
|
||||
import { UsersResponse } from './model/UsersResponse'
|
||||
@ -11,6 +11,7 @@ import { ExecutedHumhubAction, syncUser } from './syncUser'
|
||||
|
||||
const USER_BULK_SIZE = 20
|
||||
const HUMHUB_BULK_SIZE = 50
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.humhub.ExportUsers`)
|
||||
|
||||
function getUsersPage(page: number, limit: number): Promise<[User[], number]> {
|
||||
return User.findAndCount({
|
||||
@ -34,7 +35,7 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise<Map<string, Ge
|
||||
do {
|
||||
usersPage = await client.users(page, HUMHUB_BULK_SIZE)
|
||||
if (!usersPage) {
|
||||
throw new LogError('error requesting next users page from humhub')
|
||||
throw new Error('error requesting next users page from humhub')
|
||||
}
|
||||
for (const user of usersPage.results) {
|
||||
// deleted users have empty emails
|
||||
@ -61,7 +62,7 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise<Map<string, Ge
|
||||
|
||||
async function main() {
|
||||
const start = new Date().getTime()
|
||||
|
||||
initLogging()
|
||||
// open mysql connection
|
||||
const con = AppDatabase.getInstance()
|
||||
await con.init()
|
||||
@ -70,7 +71,7 @@ async function main() {
|
||||
let page = 0
|
||||
const humHubClient = HumHubClient.getInstance()
|
||||
if (!humHubClient) {
|
||||
throw new LogError('error creating humhub client')
|
||||
throw new Error('error creating humhub client')
|
||||
}
|
||||
const humhubUsers = await loadUsersFromHumHub(humHubClient)
|
||||
|
||||
@ -115,7 +116,6 @@ async function main() {
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
// biome-ignore lint/suspicious/noConsole: logger isn't used here
|
||||
console.error(e)
|
||||
logger.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
@ -4,8 +4,9 @@ import { IRequestOptions, IRestResponse, RestClient } from 'typed-rest-client'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
import { PostUserLoggingView } from './logging/PostUserLogging.view'
|
||||
import { GetUser } from './model/GetUser'
|
||||
import { PostUser } from './model/PostUser'
|
||||
@ -13,6 +14,8 @@ import { Space } from './model/Space'
|
||||
import { SpacesResponse } from './model/SpacesResponse'
|
||||
import { UsersResponse } from './model/UsersResponse'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.humhub.HumHubClient`)
|
||||
|
||||
/**
|
||||
* HumHubClient as singleton class
|
||||
*/
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import { User } from 'database'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { HumHubClient } from './HumHubClient'
|
||||
import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser'
|
||||
import { GetUser } from './model/GetUser'
|
||||
import { PostUser } from './model/PostUser'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.humhub.syncUser`)
|
||||
|
||||
export enum ExecutedHumhubAction {
|
||||
UPDATE,
|
||||
CREATE,
|
||||
|
||||
@ -4,10 +4,14 @@ import { Message } from 'openai/resources/beta/threads/messages'
|
||||
|
||||
import { httpsAgent } from '@/apis/ConnectionAgents'
|
||||
import { CONFIG } from '@/config'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { Message as MessageModel } from './model/Message'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.openai.OpenaiClient`)
|
||||
|
||||
/**
|
||||
* The `OpenaiClient` class is a singleton that provides an interface to interact with the OpenAI API.
|
||||
* It ensures that only one instance of the client is created and used throughout the application.
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { createPrivateKey, sign } from 'node:crypto'
|
||||
|
||||
import { JWTPayload, SignJWT, decodeJwt, jwtVerify } from 'jose'
|
||||
import { SignJWT, decodeJwt, jwtVerify } from 'jose'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { JwtPayloadType } from './payloadtypes/JwtPayloadType'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.auth.jwt.JWT`)
|
||||
|
||||
export const verify = async (token: string, signkey: string): Promise<JwtPayloadType | null> => {
|
||||
if (!token) {
|
||||
throw new LogError('401 Unauthorized')
|
||||
|
||||
1
backend/src/config/const.ts
Normal file
1
backend/src/config/const.ts
Normal file
@ -0,0 +1 @@
|
||||
export const LOG4JS_BASE_CATEGORY_NAME = 'backend'
|
||||
@ -1,9 +0,0 @@
|
||||
import { CONFIG } from './index'
|
||||
|
||||
describe('config/index', () => {
|
||||
describe('decay start block', () => {
|
||||
it('has the correct date set', () => {
|
||||
expect(CONFIG.DECAY_START_TIME).toEqual(new Date('2021-05-13 17:46:31-0000'))
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,21 +1,18 @@
|
||||
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env)
|
||||
|
||||
import { validate } from 'config-schema'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { LogLevel, validate } from 'config-schema'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
import { schema } from './schema'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
Decimal.set({
|
||||
precision: 25,
|
||||
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: process.env.LOG4JS_CONFIG ?? 'log4js-config.json',
|
||||
// default log level on production should be 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/backend',
|
||||
}
|
||||
|
||||
const server = {
|
||||
@ -27,8 +24,6 @@ const server = {
|
||||
GDT_ACTIVE: process.env.GDT_ACTIVE === 'true' || false,
|
||||
GDT_API_URL: process.env.GDT_API_URL ?? 'https://gdt.gradido.net',
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' || false,
|
||||
// default log level on production should be info
|
||||
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
|
||||
}
|
||||
|
||||
const klicktipp = {
|
||||
@ -143,7 +138,7 @@ const openai = {
|
||||
}
|
||||
|
||||
export const CONFIG = {
|
||||
...constants,
|
||||
...logging,
|
||||
...server,
|
||||
...klicktipp,
|
||||
...dltConnector,
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
LOG4JS_CONFIG,
|
||||
LOGIN_APP_SECRET,
|
||||
LOGIN_SERVER_KEY,
|
||||
LOG_FILES_BASE_PATH,
|
||||
LOG_LEVEL,
|
||||
NODE_ENV,
|
||||
OPENAI_ACTIVE,
|
||||
@ -32,6 +33,7 @@ export const schema = Joi.object({
|
||||
GRAPHIQL,
|
||||
HUMHUB_ACTIVE,
|
||||
HUMHUB_API_URL,
|
||||
LOG_FILES_BASE_PATH,
|
||||
LOG4JS_CONFIG,
|
||||
LOGIN_APP_SECRET,
|
||||
LOGIN_SERVER_KEY,
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import { createTransport } from 'nodemailer'
|
||||
|
||||
import { i18n, logger } from '@test/testSetup'
|
||||
import { i18n } from '@test/testSetup'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { sendEmailTranslated } from './sendEmailTranslated'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`)
|
||||
|
||||
const testMailServerHost = 'localhost'
|
||||
const testMailServerPort = 1025
|
||||
|
||||
|
||||
@ -5,7 +5,10 @@ import i18n from 'i18n'
|
||||
import { createTransport } from 'nodemailer'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`)
|
||||
|
||||
export const sendEmailTranslated = async ({
|
||||
receiver,
|
||||
@ -31,8 +34,8 @@ export const sendEmailTranslated = async ({
|
||||
|
||||
i18n.setLocale('en') // for logging
|
||||
logger.info(
|
||||
`send Email: language=${locals.locale as string} to=${receiver.to}` +
|
||||
(receiver.cc ? `, cc=${receiver.cc}` : '') +
|
||||
`send Email: language=${locals.locale as string} to=${receiver.to.substring(0, 3)}...` +
|
||||
(receiver.cc ? `, cc=${receiver.cc.substring(0, 3)}...` : '') +
|
||||
`, subject=${i18n.__('emails.' + template + '.subject')}`,
|
||||
)
|
||||
|
||||
|
||||
@ -3,7 +3,8 @@ import { Decimal } from 'decimal.js-light'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
@ -53,7 +54,7 @@ let testEnv: {
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment(logger, localization)
|
||||
testEnv = await testEnvironment(getLogger('apollo'), localization)
|
||||
con = testEnv.con
|
||||
})
|
||||
|
||||
|
||||
@ -4,11 +4,14 @@ import { validate as validateUUID, version as versionUUID } from 'uuid'
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
|
||||
import { getLogger } from 'log4js'
|
||||
import { OpenConnectionArgs } from './client/1_0/model/OpenConnectionArgs'
|
||||
import { AuthenticationClientFactory } from './client/AuthenticationClientFactory'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.authenticateCommunities`)
|
||||
|
||||
export async function startCommunityAuthentication(
|
||||
foreignFedCom: DbFederatedCommunity,
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
import { OpenConnectionArgs } from './model/OpenConnectionArgs'
|
||||
import { openConnection } from './query/openConnection'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.AuthenticationClient`)
|
||||
|
||||
export class AuthenticationClient {
|
||||
dbCom: DbFederatedCommunity
|
||||
endpoint: string
|
||||
@ -25,25 +28,19 @@ export class AuthenticationClient {
|
||||
}
|
||||
|
||||
async openConnection(args: OpenConnectionArgs): Promise<boolean | undefined> {
|
||||
logger.debug(`Authentication: openConnection at ${this.endpoint} for args:`, args)
|
||||
logger.debug(`openConnection at ${this.endpoint} for args:`, args)
|
||||
try {
|
||||
const { data } = await this.client.rawRequest<{ openConnection: boolean }>(openConnection, {
|
||||
args,
|
||||
})
|
||||
if (!data?.openConnection) {
|
||||
logger.warn(
|
||||
'Authentication: openConnection without response data from endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
logger.warn('openConnection without response data from endpoint', this.endpoint)
|
||||
return false
|
||||
}
|
||||
logger.debug(
|
||||
'Authentication: openConnection successfully started with endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
logger.debug('openConnection successfully started with endpoint', this.endpoint)
|
||||
return true
|
||||
} catch (err) {
|
||||
logger.error('Authentication: error on openConnection: ', err)
|
||||
logger.error('error on openConnection: ', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getPublicCommunityInfo } from '@/federation/client/1_0/query/getPublicCommunityInfo'
|
||||
import { getPublicKey } from '@/federation/client/1_0/query/getPublicKey'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { PublicCommunityInfoLoggingView } from './logging/PublicCommunityInfoLogging.view'
|
||||
import { GetPublicKeyResult } from './model/GetPublicKeyResult'
|
||||
import { PublicCommunityInfo } from './model/PublicCommunityInfo'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.FederationClient`)
|
||||
|
||||
export class FederationClient {
|
||||
dbCom: DbFederatedCommunity
|
||||
endpoint: string
|
||||
@ -32,25 +35,25 @@ export class FederationClient {
|
||||
}
|
||||
|
||||
getPublicKey = async (): Promise<string | undefined> => {
|
||||
logger.debug('Federation: getPublicKey from endpoint', this.endpoint)
|
||||
logger.debug('getPublicKey from endpoint', this.endpoint)
|
||||
try {
|
||||
const { data } = await this.client.rawRequest<{ getPublicKey: GetPublicKeyResult }>(
|
||||
getPublicKey,
|
||||
{},
|
||||
)
|
||||
if (!data?.getPublicKey?.publicKey) {
|
||||
logger.warn('Federation: getPublicKey without response data from endpoint', this.endpoint)
|
||||
logger.warn('getPublicKey without response data from endpoint', this.endpoint)
|
||||
return
|
||||
}
|
||||
logger.debug(
|
||||
'Federation: getPublicKey successful from endpoint',
|
||||
'getPublicKey successful from endpoint',
|
||||
this.endpoint,
|
||||
data.getPublicKey.publicKey,
|
||||
)
|
||||
return data.getPublicKey.publicKey
|
||||
} catch (err) {
|
||||
const errorString = JSON.stringify(err)
|
||||
logger.warn('Federation: getPublicKey failed for endpoint', {
|
||||
logger.warn('getPublicKey failed for endpoint', {
|
||||
endpoint: this.endpoint,
|
||||
err: errorString.length <= 200 ? errorString : errorString.substring(0, 200) + '...',
|
||||
})
|
||||
@ -58,20 +61,17 @@ export class FederationClient {
|
||||
}
|
||||
|
||||
getPublicCommunityInfo = async (): Promise<PublicCommunityInfo | undefined> => {
|
||||
logger.debug(`Federation: getPublicCommunityInfo with endpoint='${this.endpoint}'...`)
|
||||
logger.debug(`getPublicCommunityInfo with endpoint='${this.endpoint}'...`)
|
||||
try {
|
||||
const { data } = await this.client.rawRequest<{
|
||||
getPublicCommunityInfo: PublicCommunityInfo
|
||||
}>(getPublicCommunityInfo, {})
|
||||
|
||||
if (!data?.getPublicCommunityInfo?.name) {
|
||||
logger.warn(
|
||||
'Federation: getPublicCommunityInfo without response data from endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
logger.warn('getPublicCommunityInfo without response data from endpoint', this.endpoint)
|
||||
return
|
||||
}
|
||||
logger.debug(`Federation: getPublicCommunityInfo successful from endpoint=${this.endpoint}`)
|
||||
logger.debug(`getPublicCommunityInfo successful from endpoint=${this.endpoint}`)
|
||||
logger.debug(
|
||||
`publicCommunityInfo:`,
|
||||
new PublicCommunityInfoLoggingView(data.getPublicCommunityInfo),
|
||||
@ -80,7 +80,7 @@ export class FederationClient {
|
||||
} catch (err) {
|
||||
logger.warn(' err', err)
|
||||
const errorString = JSON.stringify(err)
|
||||
logger.warn('Federation: getPublicCommunityInfo failed for endpoint', {
|
||||
logger.warn('getPublicCommunityInfo failed for endpoint', {
|
||||
endpoint: this.endpoint,
|
||||
err: errorString.length <= 200 ? errorString : errorString.substring(0, 200) + '...',
|
||||
})
|
||||
|
||||
@ -2,9 +2,10 @@ import { FederatedCommunity as DbFederatedCommunity } from 'database'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { SendCoinsArgsLoggingView } from './logging/SendCoinsArgsLogging.view'
|
||||
import { SendCoinsResultLoggingView } from './logging/SendCoinsResultLogging.view'
|
||||
import { SendCoinsArgs } from './model/SendCoinsArgs'
|
||||
@ -14,6 +15,8 @@ import { revertSettledSendCoins as revertSettledSendCoinsQuery } from './query/r
|
||||
import { settleSendCoins as settleSendCoinsQuery } from './query/settleSendCoins'
|
||||
import { voteForSendCoins as voteForSendCoinsQuery } from './query/voteForSendCoins'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.SendCoinsClient`)
|
||||
|
||||
export class SendCoinsClient {
|
||||
dbCom: DbFederatedCommunity
|
||||
endpoint: string
|
||||
@ -32,123 +35,87 @@ export class SendCoinsClient {
|
||||
}
|
||||
|
||||
async voteForSendCoins(args: SendCoinsArgs): Promise<SendCoinsResult> {
|
||||
logger.debug('X-Com: voteForSendCoins against endpoint=', this.endpoint)
|
||||
logger.debug('voteForSendCoins against endpoint=', this.endpoint)
|
||||
try {
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: voteForSendCoins with args=`,
|
||||
new SendCoinsArgsLoggingView(args),
|
||||
)
|
||||
logger.debug(`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: ',
|
||||
new SendCoinsResultLoggingView(result),
|
||||
)
|
||||
logger.debug('voteForSendCoins failed with: ', new SendCoinsResultLoggingView(result))
|
||||
return new SendCoinsResult()
|
||||
}
|
||||
logger.debug(
|
||||
'X-Com: voteForSendCoins successful with result=',
|
||||
'voteForSendCoins successful with result=',
|
||||
new SendCoinsResultLoggingView(result),
|
||||
)
|
||||
return result
|
||||
} catch (err) {
|
||||
throw new LogError(`X-Com: voteForSendCoins failed for endpoint=${this.endpoint}:`, err)
|
||||
throw new LogError(`voteForSendCoins failed for endpoint=${this.endpoint}:`, err)
|
||||
}
|
||||
}
|
||||
|
||||
async revertSendCoins(args: SendCoinsArgs): Promise<boolean> {
|
||||
logger.debug('X-Com: revertSendCoins against endpoint=', this.endpoint)
|
||||
logger.debug('revertSendCoins against endpoint=', this.endpoint)
|
||||
try {
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: revertSendCoins with args=`,
|
||||
new SendCoinsArgsLoggingView(args),
|
||||
)
|
||||
logger.debug(`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)
|
||||
logger.debug(`after revertSendCoins: data=`, data)
|
||||
if (!data?.revertSendCoins) {
|
||||
logger.warn('X-Com: revertSendCoins without response data from endpoint', this.endpoint)
|
||||
logger.warn('revertSendCoins without response data from endpoint', this.endpoint)
|
||||
return false
|
||||
}
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: revertSendCoins successful from endpoint=${this.endpoint}`,
|
||||
)
|
||||
logger.debug(`revertSendCoins successful from endpoint=${this.endpoint}`)
|
||||
return true
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`X-Com: SendCoinsClient: revertSendCoins failed for endpoint=${this.endpoint}`,
|
||||
err,
|
||||
)
|
||||
logger.error(`revertSendCoins failed for endpoint=${this.endpoint}`, err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async settleSendCoins(args: SendCoinsArgs): Promise<boolean> {
|
||||
logger.debug(`X-Com: settleSendCoins against endpoint='${this.endpoint}'...`)
|
||||
logger.debug(`settleSendCoins against endpoint='${this.endpoint}'...`)
|
||||
try {
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: settleSendCoins with args=`,
|
||||
new SendCoinsArgsLoggingView(args),
|
||||
)
|
||||
logger.debug(`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)
|
||||
logger.debug(`after settleSendCoins: data=`, data)
|
||||
if (!data?.settleSendCoins) {
|
||||
logger.warn(
|
||||
'X-Com: SendCoinsClient: settleSendCoins without response data from endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
logger.warn('settleSendCoins without response data from endpoint', this.endpoint)
|
||||
return false
|
||||
}
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: settleSendCoins successful from endpoint=${this.endpoint}`,
|
||||
)
|
||||
logger.debug(`settleSendCoins successful from endpoint=${this.endpoint}`)
|
||||
return true
|
||||
} catch (err) {
|
||||
throw new LogError(
|
||||
`X-Com: SendCoinsClient: settleSendCoins failed for endpoint=${this.endpoint}`,
|
||||
err,
|
||||
)
|
||||
throw new LogError(`settleSendCoins failed for endpoint=${this.endpoint}`, err)
|
||||
}
|
||||
}
|
||||
|
||||
async revertSettledSendCoins(args: SendCoinsArgs): Promise<boolean> {
|
||||
logger.debug(`X-Com: revertSettledSendCoins against endpoint='${this.endpoint}'...`)
|
||||
logger.debug(`revertSettledSendCoins against endpoint='${this.endpoint}'...`)
|
||||
try {
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: revertSettledSendCoins with args=`,
|
||||
new SendCoinsArgsLoggingView(args),
|
||||
)
|
||||
logger.debug(`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)
|
||||
logger.debug(`after revertSettledSendCoins: data=`, data)
|
||||
|
||||
if (!data?.revertSettledSendCoins) {
|
||||
logger.warn(
|
||||
'X-Com: SendCoinsClient: revertSettledSendCoins without response data from endpoint',
|
||||
this.endpoint,
|
||||
)
|
||||
logger.warn('revertSettledSendCoins without response data from endpoint', this.endpoint)
|
||||
return false
|
||||
}
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: revertSettledSendCoins successful from endpoint=${this.endpoint}`,
|
||||
)
|
||||
logger.debug(`revertSettledSendCoins successful from endpoint=${this.endpoint}`)
|
||||
return true
|
||||
} catch (err) {
|
||||
throw new LogError(
|
||||
`X-Com: SendCoinsClient: revertSettledSendCoins failed for endpoint=${this.endpoint}`,
|
||||
err,
|
||||
)
|
||||
throw new LogError(`revertSettledSendCoins failed for endpoint=${this.endpoint}`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,16 @@ import { Response } from 'graphql-request/dist/types'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { logger } from '@test/testSetup'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
import { validateCommunities } from './validateCommunities'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.validateCommunities`)
|
||||
const federationClientLogger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.FederationClient`,
|
||||
)
|
||||
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
@ -49,7 +55,7 @@ describe('validate Communities', () => {
|
||||
})
|
||||
|
||||
it('logs zero communities found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 0 dbCommunities`)
|
||||
expect(logger.debug).toBeCalledWith(`found 0 dbCommunities`)
|
||||
})
|
||||
|
||||
describe('with one Community of api 1_0 but missing pubKey response', () => {
|
||||
@ -79,11 +85,11 @@ describe('validate Communities', () => {
|
||||
})
|
||||
|
||||
it('logs one community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 1 dbCommunities`)
|
||||
expect(logger.debug).toBeCalledWith(`found 1 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey missing response data ', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
'Federation: getPublicKey without response data from endpoint',
|
||||
expect(federationClientLogger.warn).toBeCalledWith(
|
||||
'getPublicKey without response data from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
@ -153,17 +159,17 @@ describe('validate Communities', () => {
|
||||
})
|
||||
|
||||
it('logs one community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 1 dbCommunities`)
|
||||
expect(logger.debug).toBeCalledWith(`found 1 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
expect(federationClientLogger.debug).toBeCalledWith(
|
||||
'getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs not matching publicKeys', () => {
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: received not matching publicKey:',
|
||||
'received not matching publicKey:',
|
||||
'somePubKey',
|
||||
expect.stringMatching('11111111111111111111111111111111'),
|
||||
)
|
||||
@ -203,18 +209,18 @@ describe('validate Communities', () => {
|
||||
})
|
||||
|
||||
it('logs one community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 1 dbCommunities`)
|
||||
expect(logger.debug).toBeCalledWith(`found 1 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
expect(federationClientLogger.debug).toBeCalledWith(
|
||||
'getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs community pubKey verified', () => {
|
||||
expect(logger.debug).toHaveBeenNthCalledWith(
|
||||
5,
|
||||
'Federation: getPublicKey successful from endpoint',
|
||||
expect(federationClientLogger.debug).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'getPublicKey successful from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
'11111111111111111111111111111111',
|
||||
)
|
||||
@ -269,17 +275,17 @@ describe('validate Communities', () => {
|
||||
await validateCommunities()
|
||||
})
|
||||
it('logs two communities found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 2 dbCommunities`)
|
||||
expect(logger.debug).toBeCalledWith(`found 2 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
expect(federationClientLogger.debug).toBeCalledWith(
|
||||
'getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_1 ', () => {
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
expect(federationClientLogger.debug).toBeCalledWith(
|
||||
'getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_1/',
|
||||
)
|
||||
})
|
||||
@ -321,23 +327,23 @@ describe('validate Communities', () => {
|
||||
await validateCommunities()
|
||||
})
|
||||
it('logs three community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 3 dbCommunities`)
|
||||
expect(logger.debug).toBeCalledWith(`found 3 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
expect(federationClientLogger.debug).toBeCalledWith(
|
||||
'getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_1 ', () => {
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
expect(federationClientLogger.debug).toBeCalledWith(
|
||||
'getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_1/',
|
||||
)
|
||||
})
|
||||
it('logs unsupported api for community with api 2_0 ', () => {
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Federation: dbCom with unsupported apiVersion',
|
||||
'dbCom with unsupported apiVersion',
|
||||
dbCom.endPoint,
|
||||
'2_0',
|
||||
)
|
||||
|
||||
@ -9,19 +9,19 @@ import { FederationClient as V1_0_FederationClient } from '@/federation/client/1
|
||||
import { PublicCommunityInfo } from '@/federation/client/1_0/model/PublicCommunityInfo'
|
||||
import { FederationClientFactory } from '@/federation/client/FederationClientFactory'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { getLogger } from 'log4js'
|
||||
import { startCommunityAuthentication } from './authenticateCommunities'
|
||||
import { PublicCommunityInfoLoggingView } from './client/1_0/logging/PublicCommunityInfoLogging.view'
|
||||
import { ApiVersionType } from './enum/apiVersionType'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.validateCommunities`)
|
||||
|
||||
export async function startValidateCommunities(timerInterval: number): Promise<void> {
|
||||
if (Number.isNaN(timerInterval) || timerInterval <= 0) {
|
||||
throw new LogError('FEDERATION_VALIDATE_COMMUNITY_TIMER is not a positive number')
|
||||
}
|
||||
logger.info(
|
||||
`Federation: startValidateCommunities loop with an interval of ${timerInterval} ms...`,
|
||||
)
|
||||
logger.info(`startValidateCommunities loop with an interval of ${timerInterval} ms...`)
|
||||
// delete all foreign federated community entries to avoid increasing validation efforts and log-files
|
||||
await DbFederatedCommunity.delete({ foreign: true })
|
||||
|
||||
@ -40,17 +40,13 @@ export async function validateCommunities(): Promise<void> {
|
||||
.orWhere('verified_at < last_announced_at')
|
||||
.getMany()
|
||||
|
||||
logger.debug(`Federation: found ${dbFederatedCommunities.length} dbCommunities`)
|
||||
logger.debug(`found ${dbFederatedCommunities.length} dbCommunities`)
|
||||
for (const dbCom of dbFederatedCommunities) {
|
||||
logger.debug('Federation: dbCom', new FederatedCommunityLoggingView(dbCom))
|
||||
logger.debug('dbCom', new FederatedCommunityLoggingView(dbCom))
|
||||
const apiValueStrings: string[] = Object.values(ApiVersionType)
|
||||
logger.debug(`suppported ApiVersions=`, apiValueStrings)
|
||||
if (!apiValueStrings.includes(dbCom.apiVersion)) {
|
||||
logger.debug(
|
||||
'Federation: dbCom with unsupported apiVersion',
|
||||
dbCom.endPoint,
|
||||
dbCom.apiVersion,
|
||||
)
|
||||
logger.debug('dbCom with unsupported apiVersion', dbCom.endPoint, dbCom.apiVersion)
|
||||
continue
|
||||
}
|
||||
try {
|
||||
@ -60,21 +56,17 @@ export async function validateCommunities(): Promise<void> {
|
||||
const pubKey = await client.getPublicKey()
|
||||
if (pubKey && pubKey === dbCom.publicKey.toString('hex')) {
|
||||
await DbFederatedCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() })
|
||||
logger.debug(`Federation: verified community with:`, dbCom.endPoint)
|
||||
logger.debug(`verified community with:`, dbCom.endPoint)
|
||||
const pubComInfo = await client.getPublicCommunityInfo()
|
||||
if (pubComInfo) {
|
||||
await writeForeignCommunity(dbCom, pubComInfo)
|
||||
await startCommunityAuthentication(dbCom)
|
||||
logger.debug(`Federation: write publicInfo of community: name=${pubComInfo.name}`)
|
||||
logger.debug(`write publicInfo of community: name=${pubComInfo.name}`)
|
||||
} else {
|
||||
logger.debug('Federation: missing result of getPublicCommunityInfo')
|
||||
logger.debug('missing result of getPublicCommunityInfo')
|
||||
}
|
||||
} else {
|
||||
logger.debug(
|
||||
'Federation: received not matching publicKey:',
|
||||
pubKey,
|
||||
dbCom.publicKey.toString('hex'),
|
||||
)
|
||||
logger.debug('received not matching publicKey:', pubKey, dbCom.publicKey.toString('hex'))
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@ -9,9 +9,10 @@ import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { BalanceLoggingView } from '@/logging/BalanceLogging.view'
|
||||
import { DecayLoggingView } from '@/logging/DecayLogging.view'
|
||||
import { Context, getUser } from '@/server/context'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { GdtResolver } from './GdtResolver'
|
||||
import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { transactionLinkSummary } from './util/transactionLinkSummary'
|
||||
@ -23,9 +24,10 @@ export class BalanceResolver {
|
||||
async balance(@Ctx() context: Context): Promise<Balance> {
|
||||
const user = getUser(context)
|
||||
const now = new Date()
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.BalanceResolver`)
|
||||
|
||||
logger.addContext('user', user.id)
|
||||
logger.info(`balance(userId=${user.id})...`)
|
||||
logger.info(`balance...`)
|
||||
|
||||
let balanceGDT
|
||||
if (!context.balanceGDT) {
|
||||
|
||||
@ -5,7 +5,7 @@ import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { login, updateHomeCommunityQuery } from '@/seeds/graphql/mutations'
|
||||
@ -18,10 +18,12 @@ import {
|
||||
} from '@/seeds/graphql/queries'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { getCommunityByUuid } from './util/communities'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
|
||||
// to do: We need a setup for the tests that closes the connection
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
@ -40,7 +42,7 @@ const peterLoginData = {
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment(logger, localization)
|
||||
testEnv = await testEnvironment(getLogger('apollo'), localization)
|
||||
mutate = testEnv.mutate
|
||||
query = testEnv.query
|
||||
con = testEnv.con
|
||||
|
||||
@ -5,8 +5,8 @@ import { GraphQLError } from 'graphql'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||
import { logger } from '@test/testSetup'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { EventType } from '@/event/Events'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import {
|
||||
@ -18,9 +18,12 @@ import {
|
||||
import { listContributionLinks } from '@/seeds/graphql/queries'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
let con: DataSource
|
||||
@ -286,7 +289,7 @@ describe('Contribution Links', () => {
|
||||
})
|
||||
|
||||
it('logs the error "A Start-Date must be set"', () => {
|
||||
expect(logger.error).toBeCalledWith('A Start-Date must be set')
|
||||
expect(logErrorLogger.error).toBeCalledWith('A Start-Date must be set')
|
||||
})
|
||||
|
||||
it('returns an error if missing endDate', async () => {
|
||||
@ -307,7 +310,7 @@ describe('Contribution Links', () => {
|
||||
})
|
||||
|
||||
it('logs the error "An End-Date must be set"', () => {
|
||||
expect(logger.error).toBeCalledWith('An End-Date must be set')
|
||||
expect(logErrorLogger.error).toBeCalledWith('An End-Date must be set')
|
||||
})
|
||||
|
||||
it('returns an error if endDate is before startDate', async () => {
|
||||
@ -331,7 +334,7 @@ describe('Contribution Links', () => {
|
||||
})
|
||||
|
||||
it('logs the error "The value of validFrom must before or equals the validTo"', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
`The value of validFrom must before or equals the validTo`,
|
||||
)
|
||||
})
|
||||
@ -531,7 +534,7 @@ describe('Contribution Links', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Contribution Link not found"', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution Link not found', -1)
|
||||
expect(logErrorLogger.error).toBeCalledWith('Contribution Link not found', -1)
|
||||
})
|
||||
|
||||
describe('valid id', () => {
|
||||
@ -613,7 +616,7 @@ describe('Contribution Links', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Contribution Link not found"', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution Link not found', -1)
|
||||
expect(logErrorLogger.error).toBeCalledWith('Contribution Link not found', -1)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -5,8 +5,9 @@ import { DataSource } from 'typeorm'
|
||||
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants'
|
||||
import { EventType } from '@/event/Events'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
@ -20,6 +21,13 @@ import { adminListContributionMessages, listContributionMessages } from '@/seeds
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { getLogger} from 'config-schema/test/testSetup'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.ContributionMessageResolver`)
|
||||
const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
const interactionLogger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.interactions.updateUnconfirmedContribution`,
|
||||
)
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
jest.mock('@/emails/sendEmailVariants', () => {
|
||||
@ -121,7 +129,7 @@ describe('ContributionMessageResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "ContributionMessage was not sent successfully: Error: Contribution not found"', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'ContributionMessage was not sent successfully: Error: Contribution not found',
|
||||
new Error('Contribution not found'),
|
||||
)
|
||||
@ -148,9 +156,7 @@ describe('ContributionMessageResolver', () => {
|
||||
message: 'Test',
|
||||
},
|
||||
})
|
||||
expect(logger.debug).toBeCalledTimes(5)
|
||||
expect(logger.debug).toHaveBeenNthCalledWith(
|
||||
5,
|
||||
expect(interactionLogger.debug).toBeCalledWith(
|
||||
'use UnconfirmedContributionUserAddMessageRole',
|
||||
)
|
||||
expect(mutationResult).toEqual(
|
||||
@ -327,7 +333,7 @@ describe('ContributionMessageResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "ContributionMessage was not sent successfully: Error: Contribution not found"', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'ContributionMessage was not sent successfully: Error: Contribution not found',
|
||||
new Error('Contribution not found'),
|
||||
)
|
||||
@ -348,9 +354,7 @@ describe('ContributionMessageResolver', () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(logger.debug).toBeCalledTimes(5)
|
||||
expect(logger.debug).toHaveBeenNthCalledWith(
|
||||
5,
|
||||
expect(interactionLogger.debug).toBeCalledWith(
|
||||
'use UnconfirmedContributionAdminAddMessageRole',
|
||||
)
|
||||
|
||||
@ -382,10 +386,7 @@ describe('ContributionMessageResolver', () => {
|
||||
message: 'Test',
|
||||
},
|
||||
})
|
||||
|
||||
expect(logger.debug).toBeCalledTimes(5)
|
||||
expect(logger.debug).toHaveBeenNthCalledWith(
|
||||
5,
|
||||
expect(interactionLogger.debug).toBeCalledWith(
|
||||
'use UnconfirmedContributionAdminAddMessageRole',
|
||||
)
|
||||
|
||||
@ -401,13 +402,12 @@ describe('ContributionMessageResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "ContributionMessage was not sent successfully: Error: missing right ADMIN_CREATE_CONTRIBUTION_MESSAGE for user"', () => {
|
||||
expect(logger.debug).toBeCalledTimes(5)
|
||||
expect(logger.error).toHaveBeenNthCalledWith(
|
||||
expect(logErrorLogger.error).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'missing right ADMIN_CREATE_CONTRIBUTION_MESSAGE for user',
|
||||
expect.any(Number),
|
||||
)
|
||||
expect(logger.error).toHaveBeenNthCalledWith(
|
||||
expect(logErrorLogger.error).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'ContributionMessage was not sent successfully: Error: missing right ADMIN_CREATE_CONTRIBUTION_MESSAGE for user',
|
||||
new Error('missing right ADMIN_CREATE_CONTRIBUTION_MESSAGE for user'),
|
||||
|
||||
@ -22,12 +22,14 @@ import {
|
||||
import { UpdateUnconfirmedContributionContext } from '@/interactions/updateUnconfirmedContribution/UpdateUnconfirmedContribution.context'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getUser } from '@/server/context'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
import { contributionFrontendLink } from './util/contributions'
|
||||
import { findContributionMessages } from './util/findContributionMessages'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.ContributionMessageResolver`)
|
||||
|
||||
@Resolver()
|
||||
export class ContributionMessageResolver {
|
||||
@ -126,7 +128,9 @@ export class ContributionMessageResolver {
|
||||
@Args() contributionMessageArgs: ContributionMessageArgs,
|
||||
@Ctx() context: Context,
|
||||
): Promise<ContributionMessage> {
|
||||
const logger = createLogger()
|
||||
const { contributionId, messageType } = contributionMessageArgs
|
||||
logger.addContext('contribution', contributionMessageArgs.contributionId)
|
||||
const updateUnconfirmedContributionContext = new UpdateUnconfirmedContributionContext(
|
||||
contributionId,
|
||||
contributionMessageArgs,
|
||||
|
||||
@ -14,8 +14,9 @@ import {
|
||||
resetToken,
|
||||
testEnvironment,
|
||||
} from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import {
|
||||
sendContributionConfirmedEmail,
|
||||
sendContributionDeletedEmail,
|
||||
@ -50,10 +51,14 @@ import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz'
|
||||
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { getFirstDayOfPreviousNMonth } from '@/util/utilities'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { getLogger as originalGetLogger } from 'log4js'
|
||||
|
||||
jest.mock('@/emails/sendEmailVariants')
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
let con: DataSource
|
||||
@ -72,7 +77,7 @@ let contributionToDelete: any
|
||||
let bibiCreatedContribution: Contribution
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment(logger, localization)
|
||||
testEnv = await testEnvironment(originalGetLogger('apollo'), localization)
|
||||
mutate = testEnv.mutate
|
||||
query = testEnv.query
|
||||
con = testEnv.con
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { GraphQLResolveInfo } from 'graphql'
|
||||
import { Arg, Args, Authorized, Ctx, Info, Int, Mutation, Query, Resolver } from 'type-graphql'
|
||||
import { EntityManager, IsNull, getConnection } from 'typeorm'
|
||||
import { EntityManager, IsNull } from 'typeorm'
|
||||
|
||||
import { AdminCreateContributionArgs } from '@arg/AdminCreateContributionArgs'
|
||||
import { AdminUpdateContributionArgs } from '@arg/AdminUpdateContributionArgs'
|
||||
@ -43,13 +43,14 @@ import {
|
||||
import { UpdateUnconfirmedContributionContext } from '@/interactions/updateUnconfirmedContribution/UpdateUnconfirmedContribution.context'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getClientTimezoneOffset, getUser } from '@/server/context'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
import { fullName } from '@/util/utilities'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { ContributionMessageType } from '@enum/ContributionMessageType'
|
||||
import { AppDatabase } from 'database'
|
||||
import { ContributionMessageType } from '../enum/ContributionMessageType'
|
||||
import { getLogger } from 'log4js'
|
||||
import {
|
||||
contributionFrontendLink,
|
||||
loadAllContributions,
|
||||
@ -62,6 +63,7 @@ import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.ContributionResolver`)
|
||||
|
||||
@Resolver(() => Contribution)
|
||||
export class ContributionResolver {
|
||||
@ -85,6 +87,8 @@ export class ContributionResolver {
|
||||
|
||||
const user = getUser(context)
|
||||
const creations = await getUserCreation(user.id, clientTimezoneOffset)
|
||||
const logger = createLogger()
|
||||
logger.addContext('user', user.id)
|
||||
logger.trace('creations', creations)
|
||||
const contributionDateObj = new Date(contributionDate)
|
||||
validateContribution(creations, amount, contributionDateObj, clientTimezoneOffset)
|
||||
@ -215,6 +219,8 @@ export class ContributionResolver {
|
||||
@Args() { email, amount, memo, creationDate }: AdminCreateContributionArgs,
|
||||
@Ctx() context: Context,
|
||||
): Promise<Decimal[]> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('admin', context.user?.id)
|
||||
logger.info(
|
||||
`adminCreateContribution(email=${email}, amount=${amount.toString()}, memo=${memo}, creationDate=${creationDate})`,
|
||||
)
|
||||
@ -266,6 +272,8 @@ export class ContributionResolver {
|
||||
@Args() adminUpdateContributionArgs: AdminUpdateContributionArgs,
|
||||
@Ctx() context: Context,
|
||||
): Promise<AdminUpdateContribution> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('contribution', adminUpdateContributionArgs.id)
|
||||
const updateUnconfirmedContributionContext = new UpdateUnconfirmedContributionContext(
|
||||
adminUpdateContributionArgs.id,
|
||||
adminUpdateContributionArgs,
|
||||
@ -277,7 +285,6 @@ export class ContributionResolver {
|
||||
await transactionalEntityManager.save(contribution)
|
||||
// TODO: move into specialized view or formatting for logging class
|
||||
logger.debug('saved changed contribution', {
|
||||
id: contribution.id,
|
||||
amount: contribution.amount.toString(),
|
||||
memo: contribution.memo,
|
||||
contributionDate: contribution.contributionDate.toString(),
|
||||
@ -288,7 +295,6 @@ export class ContributionResolver {
|
||||
await transactionalEntityManager.save(contributionMessage)
|
||||
// TODO: move into specialized view or formatting for logging class
|
||||
logger.debug('save new contributionMessage', {
|
||||
contributionId: contributionMessage.contributionId,
|
||||
type: contributionMessage.type,
|
||||
message: contributionMessage.message,
|
||||
isModerator: contributionMessage.isModerator,
|
||||
@ -428,6 +434,9 @@ export class ContributionResolver {
|
||||
@Arg('id', () => Int) id: number,
|
||||
@Ctx() context: Context,
|
||||
): Promise<boolean> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('contribution', id)
|
||||
|
||||
// acquire lock
|
||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||
try {
|
||||
|
||||
@ -101,6 +101,7 @@ describe('EmailOptinCodes', () => {
|
||||
|
||||
describe('forgotPassword', () => {
|
||||
it('throws an error', async () => {
|
||||
await mutate({ mutation: forgotPassword, variables: { email: 'peter@lustig.de' } })
|
||||
await expect(
|
||||
mutate({ mutation: forgotPassword, variables: { email: 'peter@lustig.de' } }),
|
||||
).resolves.toMatchObject({
|
||||
|
||||
@ -10,8 +10,10 @@ import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getUser } from '@/server/context'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.GdtResolver`)
|
||||
|
||||
@Resolver()
|
||||
export class GdtResolver {
|
||||
|
||||
@ -2,15 +2,19 @@ import { Event as DbEvent, UserContact } from 'database'
|
||||
import { GraphQLError } from 'graphql'
|
||||
|
||||
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
|
||||
import { EventType } from '@/event/Events'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { login, subscribeNewsletter, unsubscribeNewsletter } from '@/seeds/graphql/mutations'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.KlicktippResolver`)
|
||||
|
||||
let testEnv: any
|
||||
let mutate: any
|
||||
let con: any
|
||||
|
||||
@ -9,7 +9,10 @@ import { SpaceList } from '@model/SpaceList'
|
||||
import { HumHubClient } from '@/apis/humhub/HumHubClient'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.ProjectBrandingResolver`)
|
||||
|
||||
@Resolver(() => ProjectBranding)
|
||||
export class ProjectBrandingResolver {
|
||||
|
||||
@ -12,7 +12,6 @@ import { DataSource } from 'typeorm'
|
||||
|
||||
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
||||
import { cleanDB, resetEntity, resetToken, testEnvironment } from '@test/helpers'
|
||||
import { logger } from '@test/testSetup'
|
||||
|
||||
import { EventType } from '@/event/Events'
|
||||
import { creations } from '@/seeds/creation/index'
|
||||
@ -35,8 +34,12 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { transactionLinkCode } from './TransactionLinkResolver'
|
||||
|
||||
const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
// mock semaphore to allow use fake timers
|
||||
@ -221,7 +224,7 @@ describe('TransactionLinkResolver', () => {
|
||||
})
|
||||
})
|
||||
it('logs the error "User has not enough GDD"', () => {
|
||||
expect(logger.error).toBeCalledWith('User has not enough GDD', expect.any(Number))
|
||||
expect(logErrorLogger.error).toBeCalledWith('User has not enough GDD', expect.any(Number))
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -273,11 +276,11 @@ describe('TransactionLinkResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "No contribution link found to given code"', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'No contribution link found to given code',
|
||||
'CL-123456',
|
||||
)
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Creation from contribution link was not successful',
|
||||
new Error('No contribution link found to given code'),
|
||||
)
|
||||
@ -317,8 +320,11 @@ describe('TransactionLinkResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Contribution link is not valid yet"', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution link is not valid yet', validFrom)
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Contribution link is not valid yet',
|
||||
validFrom,
|
||||
)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Creation from contribution link was not successful',
|
||||
new Error('Contribution link is not valid yet'),
|
||||
)
|
||||
@ -356,8 +362,11 @@ describe('TransactionLinkResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Contribution link has unknown cycle"', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution link has unknown cycle', 'INVALID')
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Contribution link has unknown cycle',
|
||||
'INVALID',
|
||||
)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Creation from contribution link was not successful',
|
||||
new Error('Contribution link has unknown cycle'),
|
||||
)
|
||||
@ -395,8 +404,11 @@ describe('TransactionLinkResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Contribution link is no longer valid"', () => {
|
||||
expect(logger.error).toBeCalledWith('Contribution link is no longer valid', validTo)
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Contribution link is no longer valid',
|
||||
validTo,
|
||||
)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Creation from contribution link was not successful',
|
||||
new Error('Contribution link is no longer valid'),
|
||||
)
|
||||
@ -491,7 +503,7 @@ describe('TransactionLinkResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Creation from contribution link was not successful"', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Creation from contribution link was not successful',
|
||||
new Error(
|
||||
'The amount to be created exceeds the amount still available for this month',
|
||||
@ -566,7 +578,7 @@ describe('TransactionLinkResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Creation from contribution link was not successful"', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Creation from contribution link was not successful',
|
||||
new Error('Contribution link already redeemed today'),
|
||||
)
|
||||
@ -618,7 +630,7 @@ describe('TransactionLinkResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Creation from contribution link was not successful"', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Creation from contribution link was not successful',
|
||||
new Error('Contribution link already redeemed today'),
|
||||
)
|
||||
@ -652,7 +664,7 @@ describe('TransactionLinkResolver', () => {
|
||||
).resolves.toMatchObject({
|
||||
errors: [new GraphQLError('Transaction link not found')],
|
||||
})
|
||||
expect(logger.error).toBeCalledWith('Transaction link not found', 'not-valid')
|
||||
expect(logErrorLogger.error).toBeCalledWith('Transaction link not found', 'not-valid')
|
||||
})
|
||||
})
|
||||
|
||||
@ -723,7 +735,7 @@ describe('TransactionLinkResolver', () => {
|
||||
).resolves.toMatchObject({
|
||||
errors: [new GraphQLError('Cannot redeem own transaction link')],
|
||||
})
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Cannot redeem own transaction link',
|
||||
expect.any(Number),
|
||||
)
|
||||
@ -927,7 +939,7 @@ describe('TransactionLinkResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error "Could not find requested User"', () => {
|
||||
expect(logger.error).toBeCalledWith('Could not find requested User', -1)
|
||||
expect(logErrorLogger.error).toBeCalledWith('Could not find requested User', -1)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -24,7 +24,6 @@ import {
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql'
|
||||
import { getConnection } from 'typeorm'
|
||||
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { decode, encode, verify } from '@/auth/jwt/JWT'
|
||||
@ -37,7 +36,6 @@ import {
|
||||
} from '@/event/Events'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getClientTimezoneOffset, getUser } from '@/server/context'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { TRANSACTION_LINK_LOCK } from '@/util/TRANSACTION_LINK_LOCK'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
@ -45,6 +43,8 @@ import { fullName } from '@/util/utilities'
|
||||
import { calculateBalance } from '@/util/validate'
|
||||
|
||||
import { DisburseJwtPayloadType } from '@/auth/jwt/payloadtypes/DisburseJwtPayloadType'
|
||||
import { Logger, getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { executeTransaction } from './TransactionResolver'
|
||||
import {
|
||||
getAuthenticatedCommunities,
|
||||
@ -56,6 +56,8 @@ import { getLastTransaction } from './util/getLastTransaction'
|
||||
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
|
||||
import { transactionLinkList } from './util/transactionLinkList'
|
||||
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.TransactionLinkResolver`)
|
||||
|
||||
// TODO: do not export, test it inside the resolver
|
||||
export const transactionLinkCode = (date: Date): string => {
|
||||
const time = date.getTime().toString(16)
|
||||
@ -149,7 +151,9 @@ export class TransactionLinkResolver {
|
||||
@Authorized([RIGHTS.QUERY_TRANSACTION_LINK])
|
||||
@Query(() => QueryLinkResult)
|
||||
async queryTransactionLink(@Arg('code') code: string): Promise<typeof QueryLinkResult> {
|
||||
logger.debug('TransactionLinkResolver.queryTransactionLink... code=', code)
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
logger.debug('TransactionLinkResolver.queryTransactionLink...')
|
||||
if (code.match(/^CL-/)) {
|
||||
const contributionLink = await DbContributionLink.findOneOrFail({
|
||||
where: { code: code.replace('CL-', '') },
|
||||
@ -185,7 +189,7 @@ export class TransactionLinkResolver {
|
||||
return new TransactionLink(dbTransactionLink, new User(user), redeemedBy, communities)
|
||||
} else {
|
||||
// redeem jwt-token
|
||||
return await this.queryRedeemJwtLink(code)
|
||||
return await this.queryRedeemJwtLink(code, logger)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,6 +200,8 @@ export class TransactionLinkResolver {
|
||||
@Arg('code', () => String) code: string,
|
||||
@Ctx() context: Context,
|
||||
): Promise<boolean> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
// const homeCom = await DbCommunity.findOneOrFail({ where: { foreign: false } })
|
||||
const user = getUser(context)
|
||||
@ -380,6 +386,7 @@ export class TransactionLinkResolver {
|
||||
transactionLink.memo,
|
||||
linkedUser,
|
||||
user,
|
||||
logger,
|
||||
transactionLink,
|
||||
)
|
||||
await EVENT_TRANSACTION_LINK_REDEEM(
|
||||
@ -409,6 +416,8 @@ export class TransactionLinkResolver {
|
||||
@Arg('alias', { nullable: true }) alias?: string,
|
||||
@Arg('validUntil', { nullable: true }) validUntil?: string,
|
||||
): Promise<string> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
logger.debug('TransactionLinkResolver.queryRedeemJwt... args=', {
|
||||
gradidoId,
|
||||
senderCommunityUuid,
|
||||
@ -457,6 +466,8 @@ export class TransactionLinkResolver {
|
||||
@Arg('validUntil', { nullable: true }) validUntil?: string,
|
||||
@Arg('recipientAlias', { nullable: true }) recipientAlias?: string,
|
||||
): Promise<boolean> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
logger.debug('TransactionLinkResolver.disburseTransactionLink... args=', {
|
||||
senderGradidoId,
|
||||
senderCommunityUuid,
|
||||
@ -528,7 +539,7 @@ export class TransactionLinkResolver {
|
||||
return transactionLinkList(paginated, filters, user)
|
||||
}
|
||||
|
||||
async queryRedeemJwtLink(code: string): Promise<RedeemJwtLink> {
|
||||
async queryRedeemJwtLink(code: string, logger: Logger): Promise<RedeemJwtLink> {
|
||||
logger.debug('TransactionLinkResolver.queryRedeemJwtLink... redeem jwt-token found')
|
||||
// decode token first to get the senderCommunityUuid as input for verify token
|
||||
const decodedPayload = decode(code)
|
||||
@ -653,6 +664,8 @@ export class TransactionLinkResolver {
|
||||
validUntil: string,
|
||||
recipientAlias: string,
|
||||
): Promise<string> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('code', code.substring(0, 6))
|
||||
logger.debug('TransactionLinkResolver.createDisburseJwt... args=', {
|
||||
senderCommunityUuid,
|
||||
senderGradidoId,
|
||||
|
||||
@ -8,13 +8,13 @@ import {
|
||||
User,
|
||||
} from 'database'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { Connection, In } from 'typeorm'
|
||||
import { DataSource, In } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { logger } from '@test/testSetup'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { EventType } from '@/event/Events'
|
||||
import { SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient'
|
||||
import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs'
|
||||
@ -32,16 +32,19 @@ import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
||||
import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
let con: Connection
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@ -477,8 +480,6 @@ describe('send coins', () => {
|
||||
})
|
||||
|
||||
it('has wait till sendTransactionsToDltConnector created all dlt-transactions', () => {
|
||||
expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...')
|
||||
|
||||
expect(dltTransactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
|
||||
@ -29,13 +29,14 @@ import { EVENT_TRANSACTION_RECEIVE, EVENT_TRANSACTION_SEND } from '@/event/Event
|
||||
import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getUser } from '@/server/context'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { communityUser } from '@/util/communityUser'
|
||||
import { fullName } from '@/util/utilities'
|
||||
import { calculateBalance } from '@/util/validate'
|
||||
import { virtualDecayTransaction, virtualLinkTransaction } from '@/util/virtualTransactions'
|
||||
|
||||
import { Logger, getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { BalanceResolver } from './BalanceResolver'
|
||||
import { GdtResolver } from './GdtResolver'
|
||||
import { getCommunityByIdentifier, getCommunityName, isHomeCommunity } from './util/communities'
|
||||
@ -51,16 +52,19 @@ import { storeForeignUser } from './util/storeForeignUser'
|
||||
import { transactionLinkSummary } from './util/transactionLinkSummary'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.TransactionResolver`)
|
||||
|
||||
export const executeTransaction = async (
|
||||
amount: Decimal,
|
||||
memo: string,
|
||||
sender: dbUser,
|
||||
recipient: dbUser,
|
||||
logger: Logger,
|
||||
transactionLink?: dbTransactionLink | null,
|
||||
): Promise<boolean> => {
|
||||
// acquire lock
|
||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||
|
||||
try {
|
||||
logger.info('executeTransaction', amount, memo, sender, recipient)
|
||||
|
||||
@ -228,9 +232,9 @@ export class TransactionResolver {
|
||||
): Promise<TransactionList> {
|
||||
const now = new Date()
|
||||
const user = getUser(context)
|
||||
|
||||
const logger = createLogger()
|
||||
logger.addContext('user', user.id)
|
||||
logger.info(`transactionList(user=${user.firstName}.${user.lastName}, ${user.emailId})`)
|
||||
logger.info(`transactionList`)
|
||||
|
||||
let balanceGDTPromise: Promise<number | null> = Promise.resolve(null)
|
||||
if (CONFIG.GDT_ACTIVE) {
|
||||
@ -240,7 +244,7 @@ export class TransactionResolver {
|
||||
|
||||
// find current balance
|
||||
const lastTransaction = await getLastTransaction(user.id)
|
||||
logger.debug(`lastTransaction=${lastTransaction}`)
|
||||
logger.debug(`lastTransaction=${lastTransaction?.id}`)
|
||||
|
||||
const balanceResolver = new BalanceResolver()
|
||||
context.lastTransaction = lastTransaction
|
||||
@ -288,10 +292,10 @@ export class TransactionResolver {
|
||||
},
|
||||
],
|
||||
})
|
||||
logger.debug('found dbRemoteUser:', dbRemoteUser)
|
||||
logger.debug(`found dbRemoteUser: ${dbRemoteUser?.id}`)
|
||||
const remoteUser = new User(dbRemoteUser)
|
||||
if (dbRemoteUser === null) {
|
||||
logger.debug('no dbRemoteUser found, init from tx:', transaction)
|
||||
logger.debug(`no dbRemoteUser found, init from tx: ${transaction.id}`)
|
||||
if (transaction.linkedUserCommunityUuid !== null) {
|
||||
remoteUser.communityUuid = transaction.linkedUserCommunityUuid
|
||||
}
|
||||
@ -312,7 +316,10 @@ export class TransactionResolver {
|
||||
}
|
||||
}
|
||||
logger.debug(`involvedUserIds=`, involvedUserIds)
|
||||
logger.debug(`involvedRemoteUsers=`, involvedRemoteUsers)
|
||||
logger.debug(
|
||||
`involvedRemoteUsers=`,
|
||||
involvedRemoteUsers.map((u) => u.id),
|
||||
)
|
||||
|
||||
// We need to show the name for deleted users for old transactions
|
||||
const involvedDbUsers = await dbUser.find({
|
||||
@ -321,7 +328,10 @@ export class TransactionResolver {
|
||||
relations: ['emailContact'],
|
||||
})
|
||||
const involvedUsers = involvedDbUsers.map((u) => new User(u))
|
||||
logger.debug(`involvedUsers=`, involvedUsers)
|
||||
logger.debug(
|
||||
`involvedUsers=`,
|
||||
involvedUsers.map((u) => u.id),
|
||||
)
|
||||
|
||||
const self = new User(user)
|
||||
const transactions: Transaction[] = []
|
||||
@ -332,11 +342,11 @@ export class TransactionResolver {
|
||||
context.linkCount = transactionLinkcount
|
||||
logger.debug(`transactionLinkcount=${transactionLinkcount}`)
|
||||
context.sumHoldAvailableAmount = sumHoldAvailableAmount
|
||||
logger.debug(`sumHoldAvailableAmount=${sumHoldAvailableAmount}`)
|
||||
logger.debug(`sumHoldAvailableAmount=${sumHoldAvailableAmount.toString()}`)
|
||||
|
||||
// decay & link transactions
|
||||
if (currentPage === 1 && order === Order.DESC) {
|
||||
logger.debug(`currentPage == 1: transactions=${transactions}`)
|
||||
logger.debug(`currentPage == 1: transactions=${transactions.map((t) => t.id)}`)
|
||||
// The virtual decay is always on the booked amount, not including the generated, not yet booked links,
|
||||
// since the decay is substantially different when the amount is less
|
||||
transactions.push(
|
||||
@ -348,7 +358,7 @@ export class TransactionResolver {
|
||||
sumHoldAvailableAmount,
|
||||
),
|
||||
)
|
||||
logger.debug(`transactions=${transactions}`)
|
||||
logger.debug(`transactions=${transactions.map((t) => t.id)}`)
|
||||
|
||||
// virtual transaction for pending transaction-links sum
|
||||
if (sumHoldAvailableAmount.isZero()) {
|
||||
@ -373,7 +383,7 @@ export class TransactionResolver {
|
||||
)
|
||||
}
|
||||
} else if (sumHoldAvailableAmount.greaterThan(0)) {
|
||||
logger.debug(`sumHoldAvailableAmount > 0: transactions=${transactions}`)
|
||||
logger.debug(`sumHoldAvailableAmount > 0: transactions=${transactions.map((t) => t.id)}`)
|
||||
transactions.push(
|
||||
virtualLinkTransaction(
|
||||
lastTransaction.balance.minus(sumHoldAvailableAmount.toString()),
|
||||
@ -386,7 +396,7 @@ export class TransactionResolver {
|
||||
(userTransactions.length && userTransactions[0].balance) || new Decimal(0),
|
||||
),
|
||||
)
|
||||
logger.debug(`transactions=`, transactions)
|
||||
logger.debug(`transactions=${transactions.map((t) => t.id)}`)
|
||||
}
|
||||
}
|
||||
|
||||
@ -401,19 +411,22 @@ export class TransactionResolver {
|
||||
let linkedUser: User | undefined
|
||||
if ((userTransaction.typeId as TransactionTypeId) === TransactionTypeId.CREATION) {
|
||||
linkedUser = communityUser
|
||||
logger.debug('CREATION-linkedUser=', linkedUser)
|
||||
logger.debug(`CREATION-linkedUser=${linkedUser.id}`)
|
||||
} else if (userTransaction.linkedUserId) {
|
||||
linkedUser = involvedUsers.find((u) => u.id === userTransaction.linkedUserId)
|
||||
logger.debug('local linkedUser=', linkedUser)
|
||||
logger.debug(`local linkedUser=${linkedUser?.id}`)
|
||||
} else if (userTransaction.linkedUserCommunityUuid) {
|
||||
linkedUser = involvedRemoteUsers.find(
|
||||
(u) => u.gradidoID === userTransaction.linkedUserGradidoID,
|
||||
)
|
||||
logger.debug('remote linkedUser=', linkedUser)
|
||||
logger.debug(`remote linkedUser=${linkedUser?.id}`)
|
||||
}
|
||||
transactions.push(new Transaction(userTransaction, self, linkedUser))
|
||||
})
|
||||
logger.debug(`TransactionTypeId.CREATION: transactions=`, transactions)
|
||||
logger.debug(
|
||||
`TransactionTypeId.CREATION: transactions=`,
|
||||
transactions.map((t) => t.id),
|
||||
)
|
||||
|
||||
transactions.forEach((transaction: Transaction) => {
|
||||
if (transaction.typeId !== TransactionTypeId.DECAY) {
|
||||
@ -439,6 +452,9 @@ export class TransactionResolver {
|
||||
{ recipientCommunityIdentifier, recipientIdentifier, amount, memo }: TransactionSendArgs,
|
||||
@Ctx() context: Context,
|
||||
): Promise<boolean> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('from', context.user?.id)
|
||||
logger.addContext('amount', amount.toString())
|
||||
logger.debug(
|
||||
`sendCoins(recipientCommunityIdentifier=${recipientCommunityIdentifier}, recipientIdentifier=${recipientIdentifier}, amount=${amount}, memo=${memo})`,
|
||||
)
|
||||
@ -454,28 +470,28 @@ export class TransactionResolver {
|
||||
if (!recipientUser) {
|
||||
throw new LogError('The recipient user was not found', recipientUser)
|
||||
}
|
||||
logger.addContext('to', recipientUser?.id)
|
||||
if (recipientUser.foreign) {
|
||||
throw new LogError('Found foreign recipient user for a local transaction:', recipientUser)
|
||||
}
|
||||
|
||||
await executeTransaction(amount, memo, senderUser, recipientUser)
|
||||
logger.info('successful executeTransaction', amount, memo, senderUser, recipientUser)
|
||||
await executeTransaction(amount, memo, senderUser, recipientUser, logger)
|
||||
logger.info('successful executeTransaction')
|
||||
} else {
|
||||
// processing a x-community sendCoins
|
||||
logger.debug('X-Com: processing a x-community transaction...')
|
||||
logger.info('X-Com: processing a x-community transaction...')
|
||||
if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) {
|
||||
throw new LogError('X-Community sendCoins disabled per configuration!')
|
||||
}
|
||||
const recipCom = await getCommunityByIdentifier(recipientCommunityIdentifier)
|
||||
logger.debug('recipient commuity: ', recipCom)
|
||||
logger.debug('recipient community: ', recipCom?.id)
|
||||
if (recipCom === null) {
|
||||
throw new LogError(
|
||||
'no recipient commuity found for identifier:',
|
||||
recipientCommunityIdentifier,
|
||||
`no recipient community found for identifier: ${recipientCommunityIdentifier}`,
|
||||
)
|
||||
}
|
||||
if (recipCom !== null && recipCom.authenticatedAt === null) {
|
||||
throw new LogError('recipient commuity is connected, but still not authenticated yet!')
|
||||
throw new LogError('recipient community is connected, but still not authenticated yet!')
|
||||
}
|
||||
let pendingResult: SendCoinsResult
|
||||
let committingResult: SendCoinsResult
|
||||
|
||||
@ -20,7 +20,7 @@ import { UserContactType } from '@enum/UserContactType'
|
||||
import { ContributionLink } from '@model/ContributionLink'
|
||||
import { Location } from '@model/Location'
|
||||
import { cleanDB, headerPushMock, resetToken, testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
|
||||
import { subscribe } from '@/apis/KlicktippController'
|
||||
import { CONFIG } from '@/config'
|
||||
@ -67,6 +67,8 @@ import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { printTimeDuration } from '@/util/time'
|
||||
import { objectValuesToArray } from '@/util/utilities'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { Location2Point } from './util/Location2Point'
|
||||
|
||||
jest.mock('@/apis/humhub/HumHubClient')
|
||||
@ -93,6 +95,9 @@ jest.mock('@/apis/KlicktippController', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.UserResolver`)
|
||||
const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
|
||||
CONFIG.EMAIL_CODE_REQUEST_TIME = 10
|
||||
|
||||
let admin: User
|
||||
@ -107,7 +112,7 @@ let testEnv: {
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment(logger, localization)
|
||||
testEnv = await testEnvironment(getLogger('apollo'), localization)
|
||||
mutate = testEnv.mutate
|
||||
query = testEnv.query
|
||||
con = testEnv.con
|
||||
@ -275,7 +280,8 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs an info', () => {
|
||||
expect(logger.info).toBeCalledWith('User already exists with this email=peter@lustig.de')
|
||||
expect(logger.info).toBeCalledWith('User already exists')
|
||||
expect(logger.addContext).toBeCalledWith('user', user[0].id)
|
||||
})
|
||||
|
||||
it('sends an account multi registration email', () => {
|
||||
@ -642,7 +648,7 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||
)
|
||||
})
|
||||
@ -672,7 +678,7 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('Could not login with emailVerificationCode')
|
||||
expect(logger.warn).toBeCalledWith('invalid emailVerificationCode=not valid')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -693,7 +699,8 @@ describe('UserResolver', () => {
|
||||
describe('no users in database', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
expect(await mutate({ mutation: login, variables })).toEqual(
|
||||
const result = await mutate({ mutation: login, variables })
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('No user with this credentials')],
|
||||
}),
|
||||
@ -701,7 +708,9 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('No user with this credentials', variables.email)
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`findUserByEmail failed, user with email=${variables.email} not found`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -782,8 +791,8 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('No user with this credentials', variables.email)
|
||||
it('logs warning before error is thrown', () => {
|
||||
expect(logger.warn).toBeCalledWith('login failed, wrong password')
|
||||
})
|
||||
})
|
||||
|
||||
@ -813,14 +822,8 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'This user was permanently deleted. Contact support for questions',
|
||||
expect.objectContaining({
|
||||
firstName: stephenHawking.firstName,
|
||||
lastName: stephenHawking.lastName,
|
||||
}),
|
||||
)
|
||||
it('logs warning before error is thrown', () => {
|
||||
expect(logger.warn).toBeCalledWith('login failed, user was deleted')
|
||||
})
|
||||
})
|
||||
|
||||
@ -848,14 +851,8 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'The Users email is not validate yet',
|
||||
expect.objectContaining({
|
||||
firstName: garrickOllivander.firstName,
|
||||
lastName: garrickOllivander.lastName,
|
||||
}),
|
||||
)
|
||||
it('logs warning before error is thrown', () => {
|
||||
expect(logger.warn).toBeCalledWith('login failed, user email not checked')
|
||||
})
|
||||
})
|
||||
|
||||
@ -881,14 +878,8 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'The User has not set a password yet',
|
||||
expect.objectContaining({
|
||||
firstName: bibiBloxberg.firstName,
|
||||
lastName: bibiBloxberg.lastName,
|
||||
}),
|
||||
)
|
||||
it('logs warning before error is thrown', () => {
|
||||
expect(logger.warn).toBeCalledWith('login failed, user has not set a password yet')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1114,7 +1105,7 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
describe('request reset password again', () => {
|
||||
it('thows an error', async () => {
|
||||
it('throws an error', async () => {
|
||||
CONFIG.EMAIL_CODE_REQUEST_TIME = emailCodeRequestTime
|
||||
await expect(mutate({ mutation: forgotPassword, variables })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
@ -1123,8 +1114,10 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith(`Email already sent less than 10 minutes ago`)
|
||||
it('logs warning before throwing error', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
'email already sent 0 minutes ago, min wait time: 10 minutes',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1374,13 +1367,13 @@ describe('UserResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('Given language is not a valid language')],
|
||||
errors: [new GraphQLError('Given language is not a valid language or not supported')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith('Given language is not a valid language', 'not-valid')
|
||||
expect(logger.warn).toBeCalledWith('try to set unsupported language', 'not-valid')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1403,8 +1396,8 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith(`Old password is invalid`)
|
||||
it('logs if logger is in debug mode', () => {
|
||||
expect(logger.debug).toBeCalledWith(`old password is invalid`)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1430,10 +1423,8 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error found', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||
)
|
||||
it('logs warning', () => {
|
||||
expect(logger.warn).toBeCalledWith('try to set invalid password')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1490,10 +1481,8 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||
)
|
||||
it('log warning', () => {
|
||||
expect(logger.warn).toBeCalledWith('login failed, wrong password')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1776,7 +1765,10 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Could not find user with given ID', admin.id + 1)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Could not find user with given ID',
|
||||
admin.id + 1,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1892,7 +1884,9 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Administrator can not change his own role')
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Administrator can not change his own role',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1937,7 +1931,10 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('User already has role=', RoleNames.ADMIN)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'User already has role=',
|
||||
RoleNames.ADMIN,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1961,7 +1958,10 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('User already has role=', RoleNames.MODERATOR)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'User already has role=',
|
||||
RoleNames.MODERATOR,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1982,7 +1982,7 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('User is already an usual user')
|
||||
expect(logErrorLogger.error).toBeCalledWith('User is already an usual user')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -2055,7 +2055,10 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Could not find user with given ID', admin.id + 1)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Could not find user with given ID',
|
||||
admin.id + 1,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -2072,7 +2075,7 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Moderator can not delete his own account')
|
||||
expect(logErrorLogger.error).toBeCalledWith('Moderator can not delete his own account')
|
||||
})
|
||||
})
|
||||
|
||||
@ -2125,7 +2128,10 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Could not find user with given ID', user.id)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Could not find user with given ID',
|
||||
user.id,
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -2201,7 +2207,9 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('No user with this credentials', 'invalid')
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
'findUserByEmail failed, user with email=invalid not found',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -2218,11 +2226,8 @@ describe('UserResolver', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'User with given email contact is deleted',
|
||||
'stephen@hawking.uk',
|
||||
)
|
||||
it('log warning', () => {
|
||||
expect(logger.warn).toBeCalledWith('call for activation of deleted user')
|
||||
})
|
||||
})
|
||||
|
||||
@ -2348,7 +2353,10 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('Could not find user with given ID', admin.id + 1)
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Could not find user with given ID',
|
||||
admin.id + 1,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -2369,7 +2377,7 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('User is not deleted')
|
||||
expect(logErrorLogger.error).toBeCalledWith('User is not deleted')
|
||||
})
|
||||
|
||||
describe('undelete deleted user', () => {
|
||||
@ -2682,7 +2690,7 @@ describe('UserResolver', () => {
|
||||
errors: [new GraphQLError('401 Unauthorized')],
|
||||
}),
|
||||
)
|
||||
expect(logger.error).toBeCalledWith('401 Unauthorized')
|
||||
expect(logErrorLogger.error).toBeCalledWith('401 Unauthorized')
|
||||
})
|
||||
})
|
||||
|
||||
@ -2720,7 +2728,7 @@ describe('UserResolver', () => {
|
||||
errors: [new GraphQLError('Unknown identifier type')],
|
||||
}),
|
||||
)
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'Unknown identifier type',
|
||||
'identifier_is_no_valid_alias!',
|
||||
)
|
||||
@ -2742,7 +2750,7 @@ describe('UserResolver', () => {
|
||||
errors: [new GraphQLError('No user found to given identifier(s)')],
|
||||
}),
|
||||
)
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'No user found to given identifier(s)',
|
||||
uuid,
|
||||
homeCom1.communityUuid,
|
||||
@ -2765,7 +2773,7 @@ describe('UserResolver', () => {
|
||||
errors: [new GraphQLError('No user with this credentials')],
|
||||
}),
|
||||
)
|
||||
expect(logger.error).toBeCalledWith(
|
||||
expect(logErrorLogger.error).toBeCalledWith(
|
||||
'No user with this credentials',
|
||||
'bibi@bloxberg.de',
|
||||
foreignCom1.communityUuid,
|
||||
|
||||
@ -23,7 +23,7 @@ import {
|
||||
Root,
|
||||
} from 'type-graphql'
|
||||
import { IRestResponse } from 'typed-rest-client'
|
||||
import { In, Point } from 'typeorm'
|
||||
import { EntityNotFoundError, In, Point } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { UserArgs } from '@arg//UserArgs'
|
||||
@ -80,15 +80,16 @@ import { isValidPassword } from '@/password/EncryptorUtils'
|
||||
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getClientTimezoneOffset, getUser } from '@/server/context'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { communityDbUser } from '@/util/communityUser'
|
||||
import { hasElopageBuys } from '@/util/hasElopageBuys'
|
||||
import { getTimeDurationObject, printTimeDuration } from '@/util/time'
|
||||
import { durationInMinutesFromDates, getTimeDurationObject, printTimeDuration } from '@/util/time'
|
||||
import { delay } from '@/util/utilities'
|
||||
|
||||
import random from 'random-bigint'
|
||||
import { randombytes_random } from 'sodium-native'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { Logger, getLogger } from 'log4js'
|
||||
import { FULL_CREATION_AVAILABLE } from './const/const'
|
||||
import { Location2Point, Point2Location } from './util/Location2Point'
|
||||
import { authenticateGmsUserPlayground } from './util/authenticateGmsUserPlayground'
|
||||
@ -107,11 +108,12 @@ import { validateAlias } from './util/validateAlias'
|
||||
const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl']
|
||||
const DEFAULT_LANGUAGE = 'de'
|
||||
const db = AppDatabase.getInstance()
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.UserResolver`)
|
||||
const isLanguage = (language: string): boolean => {
|
||||
return LANGUAGES.includes(language)
|
||||
}
|
||||
|
||||
const newEmailContact = (email: string, userId: number): DbUserContact => {
|
||||
const newEmailContact = (email: string, userId: number, logger: Logger): DbUserContact => {
|
||||
logger.trace(`newEmailContact...`)
|
||||
const emailContact = new DbUserContact()
|
||||
emailContact.email = email
|
||||
@ -124,12 +126,12 @@ const newEmailContact = (email: string, userId: number): DbUserContact => {
|
||||
return emailContact
|
||||
}
|
||||
|
||||
export const activationLink = (verificationCode: string): string => {
|
||||
export const activationLink = (verificationCode: string, logger: Logger): string => {
|
||||
logger.debug(`activationLink(${verificationCode})...`)
|
||||
return CONFIG.EMAIL_LINK_SETPASSWORD + verificationCode.toString()
|
||||
}
|
||||
|
||||
const newGradidoID = async (): Promise<string> => {
|
||||
const newGradidoID = async (logger: Logger): Promise<string> => {
|
||||
let gradidoId: string
|
||||
let countIds: number
|
||||
do {
|
||||
@ -147,14 +149,16 @@ export class UserResolver {
|
||||
@Authorized([RIGHTS.VERIFY_LOGIN])
|
||||
@Query(() => User)
|
||||
async verifyLogin(@Ctx() context: Context): Promise<User> {
|
||||
const logger = createLogger()
|
||||
logger.info('verifyLogin...')
|
||||
// TODO refactor and do not have duplicate code with login(see below)
|
||||
const userEntity = getUser(context)
|
||||
logger.addContext('user', userEntity.id)
|
||||
const user = new User(userEntity)
|
||||
// Elopage Status & Stored PublisherId
|
||||
user.hasElopage = await this.hasElopage(context)
|
||||
|
||||
logger.debug(`verifyLogin... successful: ${user.firstName}.${user.lastName}`)
|
||||
logger.debug(`verifyLogin... successful`)
|
||||
user.klickTipp = await getKlicktippState(userEntity.emailContact.email)
|
||||
return user
|
||||
}
|
||||
@ -165,31 +169,38 @@ export class UserResolver {
|
||||
@Args() { email, password, publisherId, project }: UnsecureLoginArgs,
|
||||
@Ctx() context: Context,
|
||||
): Promise<User> {
|
||||
logger.info(`login with ${email}, ***, ${publisherId}, project=${project} ...`)
|
||||
const logger = createLogger()
|
||||
logger.info(`login with ${email.substring(0, 3)}..., project=${project} ...`)
|
||||
email = email.trim().toLowerCase()
|
||||
let dbUser: DbUser
|
||||
|
||||
try {
|
||||
dbUser = await findUserByEmail(email)
|
||||
// add technical user identifier in logger-context for layout-pattern X{user} to print it in each logging message
|
||||
logger.addContext('user', dbUser.id)
|
||||
logger.trace('user before login', new UserLoggingView(dbUser))
|
||||
} catch (e) {
|
||||
// simulate delay which occur on password encryption 650 ms +- 50 rnd
|
||||
await delay(650 + Math.floor(Math.random() * 101) - 50)
|
||||
throw e
|
||||
}
|
||||
|
||||
if (dbUser.deletedAt) {
|
||||
throw new LogError('This user was permanently deleted. Contact support for questions', dbUser)
|
||||
logger.warn('login failed, user was deleted')
|
||||
throw new Error('This user was permanently deleted. Contact support for questions')
|
||||
}
|
||||
if (!dbUser.emailContact.emailChecked) {
|
||||
throw new LogError('The Users email is not validate yet', dbUser)
|
||||
logger.warn('login failed, user email not checked')
|
||||
throw new Error('The Users email is not validate yet')
|
||||
}
|
||||
// TODO: at least in test this does not work since `dbUser.password = 0` and `BigInto(0) = 0n`
|
||||
if (dbUser.password === BigInt(0)) {
|
||||
// TODO we want to catch this on the frontend and ask the user to check his emails or resend code
|
||||
throw new LogError('The User has not set a password yet', dbUser)
|
||||
logger.warn('login failed, user has not set a password yet')
|
||||
throw new Error('The User has not set a password yet')
|
||||
}
|
||||
if (!(await verifyPassword(dbUser, password))) {
|
||||
throw new LogError('No user with this credentials', dbUser)
|
||||
logger.warn('login failed, wrong password')
|
||||
throw new Error('No user with this credentials')
|
||||
}
|
||||
|
||||
// request to humhub and klicktipp run in parallel
|
||||
@ -217,17 +228,14 @@ export class UserResolver {
|
||||
dbUser.password = await encryptPassword(dbUser, password)
|
||||
await dbUser.save()
|
||||
}
|
||||
// add pubKey in logger-context for layout-pattern X{user} to print it in each logging message
|
||||
logger.addContext('user', dbUser.id)
|
||||
logger.debug('validation of login credentials successful...')
|
||||
|
||||
const user = new User(dbUser)
|
||||
logger.debug(`user= ${JSON.stringify(user, null, 2)}`)
|
||||
i18n.setLocale(user.language)
|
||||
|
||||
// Elopage Status & Stored PublisherId
|
||||
user.hasElopage = await this.hasElopage({ ...context, user: dbUser })
|
||||
logger.info('user.hasElopage', user.hasElopage)
|
||||
logger.debug('user.hasElopage', user.hasElopage)
|
||||
if (!user.hasElopage && publisherId) {
|
||||
user.publisherId = publisherId
|
||||
dbUser.publisherId = publisherId
|
||||
@ -241,7 +249,7 @@ export class UserResolver {
|
||||
|
||||
await EVENT_USER_LOGIN(dbUser)
|
||||
const projectBranding = await projectBrandingPromise
|
||||
logger.debug('project branding: ', projectBranding)
|
||||
logger.debug('project branding: ', projectBranding?.id)
|
||||
// load humhub state
|
||||
if (humhubUserPromise) {
|
||||
try {
|
||||
@ -260,7 +268,8 @@ export class UserResolver {
|
||||
}
|
||||
}
|
||||
user.klickTipp = await klicktippStatePromise
|
||||
logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`)
|
||||
logger.info('successful Login')
|
||||
logger.trace('user after login', new UserLoggingView(dbUser))
|
||||
return user
|
||||
}
|
||||
|
||||
@ -268,8 +277,6 @@ export class UserResolver {
|
||||
@Mutation(() => Boolean)
|
||||
async logout(@Ctx() context: Context): Promise<boolean> {
|
||||
await EVENT_USER_LOGOUT(getUser(context))
|
||||
// remove user from logger context
|
||||
logger.addContext('user', 'unknown')
|
||||
return true
|
||||
}
|
||||
|
||||
@ -288,10 +295,24 @@ export class UserResolver {
|
||||
project = null,
|
||||
}: CreateUserArgs,
|
||||
): Promise<User> {
|
||||
logger.addContext('user', 'unknown')
|
||||
logger.info(
|
||||
`createUser(email=${email}, firstName=${firstName}, lastName=${lastName}, language=${language}, publisherId=${publisherId}, redeemCode=${redeemCode}, project=${project})`,
|
||||
)
|
||||
const logger = createLogger()
|
||||
const shortEmail = email.substring(0, 3)
|
||||
logger.addContext('email', shortEmail)
|
||||
|
||||
const shortRedeemCode = redeemCode?.substring(0, 6)
|
||||
const infos = []
|
||||
infos.push(`language=${language}`)
|
||||
if (publisherId) {
|
||||
infos.push(`publisherId=${publisherId}`)
|
||||
}
|
||||
if (redeemCode) {
|
||||
infos.push(`redeemCode=${shortRedeemCode}`)
|
||||
}
|
||||
if (project) {
|
||||
infos.push(`project=${project}`)
|
||||
}
|
||||
logger.info(`createUser(${infos.join(', ')})`)
|
||||
|
||||
// TODO: wrong default value (should be null), how does graphql work here? Is it an required field?
|
||||
// default int publisher_id = 0;
|
||||
|
||||
@ -305,15 +326,16 @@ export class UserResolver {
|
||||
email = email.trim().toLowerCase()
|
||||
if (await checkEmailExists(email)) {
|
||||
const foundUser = await findUserByEmail(email)
|
||||
logger.info('DbUser.findOne', email, foundUser)
|
||||
logger.info('DbUser.findOne', foundUser.id)
|
||||
|
||||
if (foundUser) {
|
||||
logger.addContext('user', foundUser.id)
|
||||
logger.removeContext('email')
|
||||
// ATTENTION: this logger-message will be exactly expected during tests, next line
|
||||
logger.info(`User already exists with this email=${email}`)
|
||||
logger.info(`User already exists`)
|
||||
logger.info(
|
||||
`Specified username when trying to register multiple times with this email: firstName=${firstName}, lastName=${lastName}`,
|
||||
`Specified username when trying to register multiple times with this email: firstName=${firstName.substring(0, 4)}, lastName=${lastName.substring(0, 4)}`,
|
||||
)
|
||||
// TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent.
|
||||
|
||||
const user = new User(communityDbUser)
|
||||
user.id = randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in?
|
||||
@ -325,7 +347,7 @@ export class UserResolver {
|
||||
if (alias && (await validateAlias(alias))) {
|
||||
user.alias = alias
|
||||
}
|
||||
logger.debug('partly faked user', user)
|
||||
logger.debug('partly faked user', { id: user.id, gradidoID: user.gradidoID })
|
||||
|
||||
await sendAccountMultiRegistrationEmail({
|
||||
firstName: foundUser.firstName, // this is the real name of the email owner, but just "firstName" would be the name of the new registrant which shall not be passed to the outside
|
||||
@ -335,9 +357,6 @@ export class UserResolver {
|
||||
})
|
||||
await EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION(foundUser)
|
||||
|
||||
logger.info(
|
||||
`sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`,
|
||||
)
|
||||
/* uncomment this, when you need the activation link on the console */
|
||||
// In case EMails are disabled log the activation link for the user
|
||||
logger.info('createUser() faked and send multi registration mail...')
|
||||
@ -352,7 +371,7 @@ export class UserResolver {
|
||||
select: { logoUrl: true, spaceId: true },
|
||||
})
|
||||
}
|
||||
const gradidoID = await newGradidoID()
|
||||
const gradidoID = await newGradidoID(logger)
|
||||
|
||||
const eventRegisterRedeem = Event(
|
||||
EventType.USER_REGISTER_REDEEM,
|
||||
@ -375,21 +394,21 @@ export class UserResolver {
|
||||
}
|
||||
dbUser.publisherId = publisherId ?? 0
|
||||
dbUser.passwordEncryptionType = PasswordEncryptionType.NO_PASSWORD
|
||||
logger.debug('new dbUser', dbUser)
|
||||
logger.debug('new dbUser', new UserLoggingView(dbUser))
|
||||
if (redeemCode) {
|
||||
if (redeemCode.match(/^CL-/)) {
|
||||
const contributionLink = await DbContributionLink.findOne({
|
||||
where: { code: redeemCode.replace('CL-', '') },
|
||||
})
|
||||
logger.info('redeemCode found contributionLink', contributionLink)
|
||||
if (contributionLink) {
|
||||
logger.info('redeemCode found contributionLink', contributionLink.id)
|
||||
dbUser.contributionLinkId = contributionLink.id
|
||||
eventRegisterRedeem.involvedContributionLink = contributionLink
|
||||
}
|
||||
} else {
|
||||
const transactionLink = await DbTransactionLink.findOne({ where: { code: redeemCode } })
|
||||
logger.info('redeemCode found transactionLink', transactionLink)
|
||||
if (transactionLink) {
|
||||
logger.info('redeemCode found transactionLink', transactionLink.id)
|
||||
dbUser.referrerId = transactionLink.userId
|
||||
eventRegisterRedeem.involvedTransactionLink = transactionLink
|
||||
}
|
||||
@ -404,7 +423,7 @@ export class UserResolver {
|
||||
dbUser = await queryRunner.manager.save(dbUser).catch((error) => {
|
||||
throw new LogError('Error while saving dbUser', error)
|
||||
})
|
||||
let emailContact = newEmailContact(email, dbUser.id)
|
||||
let emailContact = newEmailContact(email, dbUser.id, logger)
|
||||
emailContact = await queryRunner.manager.save(emailContact).catch((error) => {
|
||||
throw new LogError('Error while saving user email contact', error)
|
||||
})
|
||||
@ -431,7 +450,7 @@ export class UserResolver {
|
||||
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
|
||||
logoUrl: projectBranding?.logoUrl,
|
||||
})
|
||||
logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`)
|
||||
logger.info('sendAccountActivationEmail')
|
||||
|
||||
await EVENT_EMAIL_CONFIRMATION(dbUser)
|
||||
|
||||
@ -485,18 +504,33 @@ export class UserResolver {
|
||||
@Authorized([RIGHTS.SEND_RESET_PASSWORD_EMAIL])
|
||||
@Mutation(() => Boolean)
|
||||
async forgotPassword(@Arg('email') email: string): Promise<boolean> {
|
||||
logger.addContext('user', 'unknown')
|
||||
logger.info(`forgotPassword(${email})...`)
|
||||
const logger = createLogger()
|
||||
const shortEmail = email.substring(0, 3)
|
||||
logger.addContext('email', shortEmail)
|
||||
logger.info('forgotPassword...')
|
||||
email = email.trim().toLowerCase()
|
||||
const user = await findUserByEmail(email).catch((error) => {
|
||||
logger.warn(`fail on find UserContact per ${email} because: ${error}`)
|
||||
})
|
||||
let user: DbUser
|
||||
try {
|
||||
user = await findUserByEmail(email)
|
||||
logger.removeContext('email')
|
||||
logger.addContext('user', user.id)
|
||||
} catch (_e) {
|
||||
logger.warn(`fail on find UserContact`)
|
||||
return true
|
||||
}
|
||||
|
||||
if (!user || user.deletedAt) {
|
||||
logger.warn(`no user found with ${email}`)
|
||||
if (user.deletedAt) {
|
||||
logger.warn(`user was deleted`)
|
||||
return true
|
||||
}
|
||||
if (!canEmailResend(user.emailContact.updatedAt || user.emailContact.createdAt)) {
|
||||
const diff = durationInMinutesFromDates(
|
||||
user.emailContact.updatedAt || user.emailContact.createdAt,
|
||||
new Date(),
|
||||
)
|
||||
logger.warn(
|
||||
`email already sent ${printTimeDuration(diff)} ago, min wait time: ${printTimeDuration(CONFIG.EMAIL_CODE_REQUEST_TIME)}`,
|
||||
)
|
||||
throw new LogError(
|
||||
`Email already sent less than ${printTimeDuration(CONFIG.EMAIL_CODE_REQUEST_TIME)} ago`,
|
||||
)
|
||||
@ -507,21 +541,19 @@ export class UserResolver {
|
||||
user.emailContact.emailVerificationCode = random(64).toString()
|
||||
user.emailContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_RESET_PASSWORD
|
||||
await user.emailContact.save().catch(() => {
|
||||
throw new LogError('Unable to save email verification code', user.emailContact)
|
||||
throw new LogError('Unable to save email verification code', user.emailContact.id)
|
||||
})
|
||||
|
||||
logger.info('optInCode for', email, user.emailContact)
|
||||
|
||||
await sendResetPasswordEmail({
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
email,
|
||||
language: user.language,
|
||||
resetLink: activationLink(user.emailContact.emailVerificationCode),
|
||||
resetLink: activationLink(user.emailContact.emailVerificationCode, logger),
|
||||
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
|
||||
})
|
||||
|
||||
logger.info(`forgotPassword(${email}) successful...`)
|
||||
logger.info(`forgotPassword successful...`)
|
||||
await EVENT_EMAIL_FORGOT_PASSWORD(user)
|
||||
|
||||
return true
|
||||
@ -533,7 +565,8 @@ export class UserResolver {
|
||||
@Arg('code') code: string,
|
||||
@Arg('password') password: string,
|
||||
): Promise<boolean> {
|
||||
logger.info(`setPassword(${code}, ***)...`)
|
||||
const logger = createLogger()
|
||||
logger.info(`setPassword...`)
|
||||
// Validate Password
|
||||
if (!isValidPassword(password)) {
|
||||
throw new LogError(
|
||||
@ -545,8 +578,11 @@ export class UserResolver {
|
||||
where: { emailVerificationCode: code },
|
||||
relations: ['user'],
|
||||
}).catch(() => {
|
||||
throw new LogError('Could not login with emailVerificationCode')
|
||||
// code wasn't in db, so we can write it into log without hesitation
|
||||
logger.warn(`invalid emailVerificationCode=${code}`)
|
||||
throw new Error('Could not login with emailVerificationCode')
|
||||
})
|
||||
logger.addContext('user', userContact.user.id)
|
||||
logger.debug('userContact loaded...')
|
||||
// Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes
|
||||
if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) {
|
||||
@ -596,9 +632,7 @@ export class UserResolver {
|
||||
if ((userContact.emailOptInTypeId as OptInType) === OptInType.EMAIL_OPT_IN_REGISTER) {
|
||||
try {
|
||||
await subscribe(userContact.email, user.language, user.firstName, user.lastName)
|
||||
logger.debug(
|
||||
`subscribe(${userContact.email}, ${user.language}, ${user.firstName}, ${user.lastName})`,
|
||||
)
|
||||
logger.debug('Success subscribe to klicktipp')
|
||||
} catch (e) {
|
||||
logger.error('Error subscribing to klicktipp', e)
|
||||
}
|
||||
@ -611,18 +645,21 @@ export class UserResolver {
|
||||
@Authorized([RIGHTS.QUERY_OPT_IN])
|
||||
@Query(() => Boolean)
|
||||
async queryOptIn(@Arg('optIn') optIn: string): Promise<boolean> {
|
||||
logger.info(`queryOptIn(${optIn})...`)
|
||||
const logger = createLogger()
|
||||
logger.addContext('optIn', optIn.substring(0, 4))
|
||||
logger.info(`queryOptIn...`)
|
||||
const userContact = await DbUserContact.findOneOrFail({
|
||||
where: { emailVerificationCode: optIn },
|
||||
})
|
||||
logger.debug('found optInCode', userContact)
|
||||
logger.addContext('user', userContact.userId)
|
||||
logger.debug('found optInCode', userContact.id)
|
||||
// Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes
|
||||
if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) {
|
||||
throw new LogError(
|
||||
`Email was sent more than ${printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME)} ago`,
|
||||
)
|
||||
}
|
||||
logger.info(`queryOptIn(${optIn}) successful...`)
|
||||
logger.info(`queryOptIn successful...`)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -659,10 +696,27 @@ export class UserResolver {
|
||||
gmsLocation,
|
||||
gmsPublishLocation,
|
||||
} = updateUserInfosArgs
|
||||
logger.info(
|
||||
`updateUserInfos(${firstName}, ${lastName}, ${alias}, ${language}, ***, ***, ${hideAmountGDD}, ${hideAmountGDT}, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})...`,
|
||||
)
|
||||
const user = getUser(context)
|
||||
const logger = createLogger()
|
||||
logger.addContext('user', user.id)
|
||||
// log only if a value is set
|
||||
logger.info(`updateUserInfos...`, {
|
||||
firstName: firstName !== undefined,
|
||||
lastName: lastName !== undefined,
|
||||
alias: alias !== undefined,
|
||||
language: language !== undefined,
|
||||
password: password !== undefined,
|
||||
passwordNew: passwordNew !== undefined,
|
||||
hideAmountGDD: hideAmountGDD !== undefined,
|
||||
hideAmountGDT: hideAmountGDT !== undefined,
|
||||
humhubAllowed: humhubAllowed !== undefined,
|
||||
gmsAllowed: gmsAllowed !== undefined,
|
||||
gmsPublishName: gmsPublishName !== undefined,
|
||||
humhubPublishName: humhubPublishName !== undefined,
|
||||
gmsLocation: gmsLocation !== undefined,
|
||||
gmsPublishLocation: gmsPublishLocation !== undefined,
|
||||
})
|
||||
|
||||
const updateUserInGMS = compareGmsRelevantUserSettings(user, updateUserInfosArgs)
|
||||
const publishNameLogic = new PublishNameLogic(user)
|
||||
const oldHumhubUsername = publishNameLogic.getUserIdentifier(
|
||||
@ -685,7 +739,8 @@ export class UserResolver {
|
||||
|
||||
if (language) {
|
||||
if (!isLanguage(language)) {
|
||||
throw new LogError('Given language is not a valid language', language)
|
||||
logger.warn('try to set unsupported language', language)
|
||||
throw new LogError('Given language is not a valid language or not supported')
|
||||
}
|
||||
user.language = language
|
||||
i18n.setLocale(language)
|
||||
@ -694,12 +749,15 @@ export class UserResolver {
|
||||
if (password && passwordNew) {
|
||||
// Validate Password
|
||||
if (!isValidPassword(passwordNew)) {
|
||||
throw new LogError(
|
||||
// TODO: log which rule(s) wasn't met
|
||||
logger.warn('try to set invalid password')
|
||||
throw new Error(
|
||||
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
|
||||
)
|
||||
}
|
||||
|
||||
if (!(await verifyPassword(user, password))) {
|
||||
logger.debug('old password is invalid')
|
||||
throw new LogError(`Old password is invalid`)
|
||||
}
|
||||
|
||||
@ -763,7 +821,7 @@ export class UserResolver {
|
||||
logger.debug(`changed user-settings relevant for gms-user update...`)
|
||||
const homeCom = await getHomeCommunity()
|
||||
if (homeCom.gmsApiKey !== null) {
|
||||
logger.debug(`send User to Gms...`, user)
|
||||
logger.debug(`send User to Gms...`)
|
||||
await sendUserToGms(user, homeCom)
|
||||
logger.debug(`sendUserToGms successfully.`)
|
||||
}
|
||||
@ -785,18 +843,22 @@ export class UserResolver {
|
||||
@Authorized([RIGHTS.HAS_ELOPAGE])
|
||||
@Query(() => Boolean)
|
||||
async hasElopage(@Ctx() context: Context): Promise<boolean> {
|
||||
logger.info(`hasElopage()...`)
|
||||
const userEntity = getUser(context)
|
||||
const elopageBuys = hasElopageBuys(userEntity.emailContact.email)
|
||||
logger.debug('has ElopageBuys', elopageBuys)
|
||||
const dbUser = getUser(context)
|
||||
const logger = createLogger()
|
||||
logger.addContext('user', dbUser.id)
|
||||
const elopageBuys = await hasElopageBuys(dbUser.emailContact.email)
|
||||
logger.info(`has Elopage (ablify): ${elopageBuys}`)
|
||||
return elopageBuys
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.GMS_USER_PLAYGROUND])
|
||||
@Query(() => GmsUserAuthenticationResult)
|
||||
async authenticateGmsUserSearch(@Ctx() context: Context): Promise<GmsUserAuthenticationResult> {
|
||||
logger.info(`authenticateGmsUserSearch()...`)
|
||||
const dbUser = getUser(context)
|
||||
const logger = createLogger()
|
||||
logger.addContext('user', dbUser.id)
|
||||
logger.info(`authenticateGmsUserSearch()...`)
|
||||
|
||||
let result = new GmsUserAuthenticationResult()
|
||||
if (context.token) {
|
||||
const homeCom = await getHomeCommunity()
|
||||
@ -815,8 +877,11 @@ export class UserResolver {
|
||||
@Authorized([RIGHTS.GMS_USER_PLAYGROUND])
|
||||
@Query(() => UserLocationResult)
|
||||
async userLocation(@Ctx() context: Context): Promise<UserLocationResult> {
|
||||
logger.info(`userLocation()...`)
|
||||
const dbUser = getUser(context)
|
||||
const logger = createLogger()
|
||||
logger.addContext('user', dbUser.id)
|
||||
logger.info(`userLocation()...`)
|
||||
|
||||
const result = new UserLocationResult()
|
||||
if (context.token) {
|
||||
const homeCom = await getHomeCommunity()
|
||||
@ -835,8 +900,11 @@ export class UserResolver {
|
||||
@Ctx() context: Context,
|
||||
@Arg('project', () => String, { nullable: true }) project?: string | null,
|
||||
): Promise<string> {
|
||||
logger.info(`authenticateHumhubAutoLogin()...`)
|
||||
const dbUser = getUser(context)
|
||||
const logger = createLogger()
|
||||
logger.addContext('user', dbUser.id)
|
||||
logger.info(`authenticateHumhubAutoLogin()...`)
|
||||
|
||||
const humhubClient = HumHubClient.getInstance()
|
||||
if (!humhubClient) {
|
||||
throw new LogError('cannot create humhub client')
|
||||
@ -1029,13 +1097,15 @@ export class UserResolver {
|
||||
@Arg('email') email: string,
|
||||
@Ctx() context: Context,
|
||||
): Promise<boolean> {
|
||||
const logger = createLogger()
|
||||
email = email.trim().toLowerCase()
|
||||
// const user = await dbUser.findOne({ id: emailContact.userId })
|
||||
const user = await findUserByEmail(email)
|
||||
logger.addContext('user', user.id)
|
||||
logger.info('sendActivationEmail...')
|
||||
if (user.deletedAt || user.emailContact.deletedAt) {
|
||||
throw new LogError('User with given email contact is deleted', email)
|
||||
logger.warn('call for activation of deleted user')
|
||||
throw new Error('User with given email contact is deleted')
|
||||
}
|
||||
|
||||
user.emailContact.emailResendCount++
|
||||
await user.emailContact.save()
|
||||
|
||||
@ -1044,7 +1114,7 @@ export class UserResolver {
|
||||
lastName: user.lastName,
|
||||
email,
|
||||
language: user.language,
|
||||
activationLink: activationLink(user.emailContact.emailVerificationCode),
|
||||
activationLink: activationLink(user.emailContact.emailVerificationCode, logger),
|
||||
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
|
||||
})
|
||||
|
||||
@ -1090,16 +1160,24 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
export async function findUserByEmail(email: string): Promise<DbUser> {
|
||||
const dbUser = await DbUser.findOneOrFail({
|
||||
where: {
|
||||
emailContact: { email },
|
||||
},
|
||||
withDeleted: true,
|
||||
relations: { userRoles: true, emailContact: true },
|
||||
}).catch(() => {
|
||||
throw new LogError('No user with this credentials', email)
|
||||
})
|
||||
return dbUser
|
||||
try {
|
||||
const dbUser = await DbUser.findOneOrFail({
|
||||
where: {
|
||||
emailContact: { email },
|
||||
},
|
||||
withDeleted: true,
|
||||
relations: { userRoles: true, emailContact: true },
|
||||
})
|
||||
return dbUser
|
||||
} catch (e) {
|
||||
const logger = createLogger()
|
||||
if (e instanceof EntityNotFoundError || (e as Error).name === 'EntityNotFoundError') {
|
||||
logger.warn(`findUserByEmail failed, user with email=${email} not found`)
|
||||
} else {
|
||||
logger.error(`findUserByEmail failed, unknown error: ${e}`)
|
||||
}
|
||||
throw new Error('No user with this credentials')
|
||||
}
|
||||
}
|
||||
|
||||
async function checkEmailExists(email: string): Promise<boolean> {
|
||||
|
||||
@ -3,8 +3,13 @@ import { User as DbUser } from 'database'
|
||||
import { verifyAuthToken } from '@/apis/gms/GmsClient'
|
||||
import { CONFIG } from '@/config'
|
||||
import { GmsUserAuthenticationResult } from '@/graphql/model/GmsUserAuthenticationResult'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { ensureUrlEndsWithSlash } from '@/util/utilities'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.authenticateGmsUserPlayground`,
|
||||
)
|
||||
|
||||
export async function authenticateGmsUserPlayground(
|
||||
_apiKey: string,
|
||||
|
||||
@ -5,10 +5,15 @@ import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs'
|
||||
import { GmsPublishLocationType } from '@/graphql/enum/GmsPublishLocationType'
|
||||
import { PublishNameType } from '@/graphql/enum/PublishNameType'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { Point2Location } from './Location2Point'
|
||||
|
||||
const logger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.compareGmsRelevantUserSettings`,
|
||||
)
|
||||
|
||||
export function compareGmsRelevantUserSettings(
|
||||
orgUser: DbUser,
|
||||
updateUserInfosArgs: UpdateUserInfosArgs,
|
||||
|
||||
@ -4,12 +4,14 @@ import { Decimal } from 'decimal.js-light'
|
||||
import { OpenCreation } from '@model/OpenCreation'
|
||||
|
||||
import { FULL_CREATION_AVAILABLE, MAX_CREATION_AMOUNT } from '@/graphql/resolver/const/const'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getFirstDayOfPreviousNMonth } from '@/util/utilities'
|
||||
import { AppDatabase } from 'database'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.creations`)
|
||||
|
||||
interface CreationMap {
|
||||
id: number
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { KlickTipp } from '@model/KlickTipp'
|
||||
|
||||
import { getKlickTippUser } from '@/apis/KlicktippController'
|
||||
import { klickTippLogger as logger } from '@/server/logger'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.getKlicktippState`)
|
||||
|
||||
export const getKlicktippState = async (email: string): Promise<KlickTipp> => {
|
||||
try {
|
||||
@ -10,7 +13,7 @@ export const getKlicktippState = async (email: string): Promise<KlickTipp> => {
|
||||
return new KlickTipp(klickTippUser.status === 'Subscribed')
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('There is no klicktipp user for email', email, err)
|
||||
logger.error('There is no klicktipp user for email', email.substring(0, 3), '...', err)
|
||||
}
|
||||
return new KlickTipp(false)
|
||||
}
|
||||
|
||||
@ -3,6 +3,9 @@ import {
|
||||
FederatedCommunity as DbFederatedCommunity,
|
||||
PendingTransaction as DbPendingTransaction,
|
||||
User as dbUser,
|
||||
PendingTransactionLoggingView,
|
||||
CommunityLoggingView,
|
||||
UserLoggingView,
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
@ -14,12 +17,17 @@ import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
|
||||
import { SendCoinsClientFactory } from '@/federation/client/SendCoinsClientFactory'
|
||||
import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState'
|
||||
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { calculateSenderBalance } from '@/util/calculateSenderBalance'
|
||||
import { fullName } from '@/util/utilities'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { settlePendingSenderTransaction } from './settlePendingSenderTransaction'
|
||||
import { SendCoinsArgsLoggingView } from '@/federation/client/1_0/logging/SendCoinsArgsLogging.view'
|
||||
import { SendCoinsResultLoggingView } from '@/federation/client/1_0/logging/SendCoinsResultLogging.view'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.processXComSendCoins`)
|
||||
|
||||
export async function processXComPendingSendCoins(
|
||||
receiverCom: DbCommunity,
|
||||
@ -32,15 +40,19 @@ export async function processXComPendingSendCoins(
|
||||
): Promise<SendCoinsResult> {
|
||||
let voteResult: SendCoinsResult
|
||||
try {
|
||||
logger.debug(
|
||||
`XCom: processXComPendingSendCoins...`,
|
||||
receiverCom,
|
||||
senderCom,
|
||||
amount,
|
||||
memo,
|
||||
sender,
|
||||
recipientIdentifier,
|
||||
)
|
||||
// even if debug is not enabled, attributes are processed so we skip the entire call for performance reasons
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
'XCom: processXComPendingSendCoins...', {
|
||||
receiverCom: new CommunityLoggingView(receiverCom),
|
||||
senderCom: new CommunityLoggingView(senderCom),
|
||||
amount: amount.toString(),
|
||||
memo: memo.substring(0, 5),
|
||||
sender: new UserLoggingView(sender),
|
||||
recipientIdentifier
|
||||
}
|
||||
)
|
||||
}
|
||||
const openSenderPendingTx = await DbPendingTransaction.count({
|
||||
where: [
|
||||
{ userGradidoID: sender.gradidoID, state: PendingTransactionState.NEW },
|
||||
@ -63,11 +75,13 @@ export async function processXComPendingSendCoins(
|
||||
if (!senderBalance) {
|
||||
throw new LogError('User has not enough GDD or amount is < 0', senderBalance)
|
||||
}
|
||||
logger.debug(`X-Com: calculated senderBalance = `, senderBalance)
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(`calculated senderBalance = ${JSON.stringify(senderBalance, null, 2)}`)
|
||||
}
|
||||
|
||||
const receiverFCom = await DbFederatedCommunity.findOneOrFail({
|
||||
where: {
|
||||
publicKey: receiverCom.publicKey,
|
||||
publicKey: Buffer.from(receiverCom.publicKey),
|
||||
apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API,
|
||||
},
|
||||
})
|
||||
@ -88,11 +102,15 @@ export async function processXComPendingSendCoins(
|
||||
args.senderUserUuid = sender.gradidoID
|
||||
args.senderUserName = fullName(sender.firstName, sender.lastName)
|
||||
args.senderAlias = sender.alias
|
||||
logger.debug(`X-Com: ready for voteForSendCoins with args=`, args)
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(`ready for voteForSendCoins with args=${new SendCoinsArgsLoggingView(args)}`)
|
||||
}
|
||||
voteResult = await client.voteForSendCoins(args)
|
||||
logger.debug(`X-Com: returned from voteForSendCoins:`, voteResult)
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(`returned from voteForSendCoins: ${new SendCoinsResultLoggingView(voteResult)}`)
|
||||
}
|
||||
if (voteResult.vote) {
|
||||
logger.debug(`X-Com: prepare pendingTransaction for sender...`)
|
||||
logger.debug('prepare pendingTransaction for sender...')
|
||||
// writing the pending transaction on receiver-side was successfull, so now write the sender side
|
||||
try {
|
||||
const pendingTx = DbPendingTransaction.create()
|
||||
@ -120,38 +138,37 @@ export async function processXComPendingSendCoins(
|
||||
pendingTx.userId = sender.id
|
||||
pendingTx.userGradidoID = sender.gradidoID
|
||||
pendingTx.userName = fullName(sender.firstName, sender.lastName)
|
||||
logger.debug(`X-Com: initialized sender pendingTX=`, pendingTx)
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(`initialized sender pendingTX=${new PendingTransactionLoggingView(pendingTx)}`)
|
||||
}
|
||||
|
||||
await DbPendingTransaction.insert(pendingTx)
|
||||
logger.debug(`X-Com: sender pendingTx successfully inserted...`)
|
||||
logger.debug('sender pendingTx successfully inserted...')
|
||||
} catch (err) {
|
||||
logger.error(`Error in writing sender pending transaction: `, err)
|
||||
logger.error(`Error in writing sender pending transaction: ${JSON.stringify(err, null, 2)}`)
|
||||
// revert the existing pending transaction on receiver side
|
||||
let revertCount = 0
|
||||
logger.debug(`X-Com: first try to revertSendCoins of receiver`)
|
||||
logger.debug('first try to revertSendCoins of receiver')
|
||||
do {
|
||||
if (await client.revertSendCoins(args)) {
|
||||
logger.debug(`revertSendCoins()-1_0... successfull after revertCount=`, revertCount)
|
||||
logger.debug(`revertSendCoins()-1_0... successfull after revertCount=${revertCount}`)
|
||||
// treat revertingSendCoins as an error of the whole sendCoins-process
|
||||
throw new LogError('Error in writing sender pending transaction: `, err')
|
||||
throw new LogError('Error in writing sender pending transaction: ', err)
|
||||
}
|
||||
} while (CONFIG.FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS > revertCount++)
|
||||
throw new LogError(
|
||||
`Error in reverting receiver pending transaction even after revertCount=`,
|
||||
revertCount,
|
||||
`Error in reverting receiver pending transaction even after revertCount=${revertCount}`,
|
||||
err,
|
||||
)
|
||||
}
|
||||
logger.debug(`voteForSendCoins()-1_0... successfull`)
|
||||
logger.debug('voteForSendCoins()-1_0... successfull')
|
||||
} else {
|
||||
logger.error(
|
||||
`X-Com: break with error on writing pendingTransaction for recipient...`,
|
||||
voteResult,
|
||||
)
|
||||
logger.error(`break with error on writing pendingTransaction for recipient... ${new SendCoinsResultLoggingView(voteResult)}`)
|
||||
}
|
||||
return voteResult
|
||||
}
|
||||
} catch (err) {
|
||||
throw new LogError(`Error:`, err)
|
||||
} catch (err: any) {
|
||||
throw new LogError(`Error: ${err.message}`, err)
|
||||
}
|
||||
return new SendCoinsResult()
|
||||
}
|
||||
@ -167,16 +184,19 @@ export async function processXComCommittingSendCoins(
|
||||
): Promise<SendCoinsResult> {
|
||||
const sendCoinsResult = new SendCoinsResult()
|
||||
try {
|
||||
logger.debug(
|
||||
`XCom: processXComCommittingSendCoins...`,
|
||||
receiverCom,
|
||||
senderCom,
|
||||
creationDate,
|
||||
amount,
|
||||
memo,
|
||||
sender,
|
||||
recipient,
|
||||
)
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
'XCom: processXComCommittingSendCoins...', {
|
||||
receiverCom: new CommunityLoggingView(receiverCom),
|
||||
senderCom: new CommunityLoggingView(senderCom),
|
||||
creationDate: creationDate.toISOString(),
|
||||
amount: amount.toString(),
|
||||
memo: memo.substring(0, 5),
|
||||
sender: new UserLoggingView(sender),
|
||||
recipient: new SendCoinsResultLoggingView(recipient),
|
||||
}
|
||||
)
|
||||
}
|
||||
// first find pending Tx with given parameters
|
||||
const pendingTx = await DbPendingTransaction.findOneBy({
|
||||
userCommunityUuid: senderCom.communityUuid ?? 'homeCom-UUID',
|
||||
@ -191,10 +211,12 @@ export async function processXComCommittingSendCoins(
|
||||
memo,
|
||||
})
|
||||
if (pendingTx) {
|
||||
logger.debug(`X-Com: find pending Tx for settlement:`, pendingTx)
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(`find pending Tx for settlement: ${new PendingTransactionLoggingView(pendingTx)}`)
|
||||
}
|
||||
const receiverFCom = await DbFederatedCommunity.findOneOrFail({
|
||||
where: {
|
||||
publicKey: receiverCom.publicKey,
|
||||
publicKey: Buffer.from(receiverCom.publicKey),
|
||||
|
||||
apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API,
|
||||
},
|
||||
@ -218,9 +240,11 @@ export async function processXComCommittingSendCoins(
|
||||
args.senderUserName = pendingTx.userName
|
||||
}
|
||||
args.senderAlias = sender.alias
|
||||
logger.debug(`X-Com: ready for settleSendCoins with args=`, args)
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug(`ready for settleSendCoins with args=${new SendCoinsArgsLoggingView(args)}`)
|
||||
}
|
||||
const acknowledge = await client.settleSendCoins(args)
|
||||
logger.debug(`X-Com: returnd from settleSendCoins:`, acknowledge)
|
||||
logger.debug(`returnd from settleSendCoins: ${acknowledge}`)
|
||||
if (acknowledge) {
|
||||
// settle the pending transaction on receiver-side was successfull, so now settle the sender side
|
||||
try {
|
||||
@ -244,30 +268,29 @@ export async function processXComCommittingSendCoins(
|
||||
sendCoinsResult.recipAlias = recipient.recipAlias
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`Error in writing sender pending transaction: `, err)
|
||||
logger.error(`Error in writing sender pending transaction: ${JSON.stringify(err, null, 2)}`)
|
||||
// revert the existing pending transaction on receiver side
|
||||
let revertCount = 0
|
||||
logger.debug(`X-Com: first try to revertSetteledSendCoins of receiver`)
|
||||
logger.debug('first try to revertSetteledSendCoins of receiver')
|
||||
do {
|
||||
if (await client.revertSettledSendCoins(args)) {
|
||||
logger.debug(
|
||||
`revertSettledSendCoins()-1_0... successfull after revertCount=`,
|
||||
revertCount,
|
||||
`revertSettledSendCoins()-1_0... successfull after revertCount=${revertCount}`,
|
||||
)
|
||||
// treat revertingSettledSendCoins as an error of the whole sendCoins-process
|
||||
throw new LogError('Error in settle sender pending transaction: `, err')
|
||||
throw new LogError('Error in settle sender pending transaction: ', err)
|
||||
}
|
||||
} while (CONFIG.FEDERATION_XCOM_MAXREPEAT_REVERTSENDCOINS > revertCount++)
|
||||
throw new LogError(
|
||||
`Error in reverting receiver pending transaction even after revertCount=`,
|
||||
revertCount,
|
||||
`Error in reverting receiver pending transaction even after revertCount=${revertCount}`,
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`Error:`, err)
|
||||
logger.error(`Error: ${JSON.stringify(err, null, 2)}`)
|
||||
sendCoinsResult.vote = false
|
||||
}
|
||||
return sendCoinsResult
|
||||
|
||||
@ -9,7 +9,7 @@ import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
|
||||
@ -20,11 +20,17 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
import { sendTransactionsToDltConnector } from './sendTransactionsToDltConnector'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
const logger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.sendTransactionsToDltConnector`,
|
||||
)
|
||||
|
||||
/*
|
||||
// Mock the GraphQLClient
|
||||
jest.mock('graphql-request', () => {
|
||||
@ -417,7 +423,7 @@ describe('create and send Transactions to DltConnector', () => {
|
||||
]),
|
||||
)
|
||||
|
||||
expect(logger.info).nthCalledWith(3, 'sending to DltConnector currently not configured...')
|
||||
expect(logger.info).nthCalledWith(2, 'sending to DltConnector currently not configured...')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -3,8 +3,13 @@ import { IsNull } from 'typeorm'
|
||||
|
||||
import { DltConnectorClient } from '@dltConnector/DltConnectorClient'
|
||||
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { Monitor, MonitorNames } from '@/util/Monitor'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.sendTransactionsToDltConnector`,
|
||||
)
|
||||
|
||||
export async function sendTransactionsToDltConnector(): Promise<void> {
|
||||
logger.info('sendTransactionsToDltConnector...')
|
||||
@ -35,7 +40,7 @@ export async function sendTransactionsToDltConnector(): Promise<void> {
|
||||
if (result) {
|
||||
dltTx.messageId = 'sended'
|
||||
await DltTransaction.save(dltTx)
|
||||
logger.info('store messageId=%s in dltTx=%d', dltTx.messageId, dltTx.id)
|
||||
logger.info(`store messageId=${dltTx.messageId} in dltTx=${dltTx.id}`)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
|
||||
@ -3,8 +3,11 @@ import { Community as DbCommunity, User as DbUser } from 'database'
|
||||
import { createGmsUser, updateGmsUser } from '@/apis/gms/GmsClient'
|
||||
import { GmsUser } from '@/apis/gms/model/GmsUser'
|
||||
import { CONFIG } from '@/config'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.sendUserToGms`)
|
||||
|
||||
export async function sendUserToGms(
|
||||
user: DbUser,
|
||||
|
||||
@ -8,14 +8,17 @@ import {
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||
import { calculateSenderBalance } from '@/util/calculateSenderBalance'
|
||||
|
||||
import { getLogger } from 'log4js'
|
||||
import { getLastTransaction } from './getLastTransaction'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const logger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.settlePendingSenderTransaction`,
|
||||
)
|
||||
|
||||
export async function settlePendingSenderTransaction(
|
||||
homeCom: DbCommunity,
|
||||
@ -31,7 +34,7 @@ export async function settlePendingSenderTransaction(
|
||||
logger.debug(`start Transaction for write-access...`)
|
||||
|
||||
try {
|
||||
logger.info('X-Com: settlePendingSenderTransaction:', homeCom, senderUser, pendingTx)
|
||||
logger.info('settlePendingSenderTransaction:', homeCom, senderUser, pendingTx)
|
||||
|
||||
// ensure that no other pendingTx with the same sender or recipient exists
|
||||
const openSenderPendingTx = await DbPendingTransaction.count({
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { Community as DbCommunity, User as DbUser } from 'database'
|
||||
|
||||
import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.storeForeignUser`)
|
||||
|
||||
export async function storeForeignUser(
|
||||
recipCom: DbCommunity,
|
||||
@ -18,7 +21,7 @@ export async function storeForeignUser(
|
||||
})
|
||||
if (!user) {
|
||||
logger.debug(
|
||||
'X-Com: no foreignUser found for:',
|
||||
'no foreignUser found for:',
|
||||
recipCom.communityUuid,
|
||||
committingResult.recipGradidoID,
|
||||
)
|
||||
@ -36,7 +39,7 @@ export async function storeForeignUser(
|
||||
}
|
||||
foreignUser.gradidoID = committingResult.recipGradidoID
|
||||
foreignUser = await DbUser.save(foreignUser)
|
||||
logger.debug('X-Com: new foreignUser inserted:', foreignUser)
|
||||
logger.debug('new foreignUser inserted:', foreignUser)
|
||||
|
||||
return true
|
||||
} else if (
|
||||
@ -45,7 +48,7 @@ export async function storeForeignUser(
|
||||
user.alias !== committingResult.recipAlias
|
||||
) {
|
||||
logger.warn(
|
||||
'X-Com: foreignUser still exists, but with different name or alias:',
|
||||
'foreignUser still exists, but with different name or alias:',
|
||||
user,
|
||||
committingResult,
|
||||
)
|
||||
@ -62,11 +65,11 @@ export async function storeForeignUser(
|
||||
logger.debug('update recipient successful.', user)
|
||||
return true
|
||||
} else {
|
||||
logger.debug('X-Com: foreignUser still exists...:', user)
|
||||
logger.debug('foreignUser still exists...:', user)
|
||||
return true
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('X-Com: error in storeForeignUser;', err)
|
||||
logger.error('error in storeForeignUser;', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,9 @@ import { HumHubClient } from '@/apis/humhub/HumHubClient'
|
||||
import { GetUser } from '@/apis/humhub/model/GetUser'
|
||||
import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs'
|
||||
import { PublishNameType } from '@/graphql/enum/PublishNameType'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { syncHumhub } from './syncHumhub'
|
||||
|
||||
jest.mock('@/apis/humhub/HumHubClient')
|
||||
@ -19,6 +20,8 @@ mockUser.humhubPublishName = PublishNameType.PUBLISH_NAME_FULL
|
||||
const mockUpdateUserInfosArg = new UpdateUserInfosArgs()
|
||||
const mockHumHubUser = new GetUser(mockUser, 1)
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.syncHumhub`)
|
||||
|
||||
describe('syncHumhub', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(logger, 'debug').mockImplementation()
|
||||
@ -33,8 +36,7 @@ describe('syncHumhub', () => {
|
||||
it('Should not sync if no relevant changes', async () => {
|
||||
await syncHumhub(mockUpdateUserInfosArg, new User(), 'username')
|
||||
expect(HumHubClient.getInstance).not.toBeCalled()
|
||||
// language logging from some other place
|
||||
expect(logger.debug).toBeCalledTimes(5)
|
||||
expect(logger.debug).toBeCalledTimes(1)
|
||||
expect(logger.info).toBeCalledTimes(0)
|
||||
})
|
||||
|
||||
@ -42,7 +44,7 @@ describe('syncHumhub', () => {
|
||||
mockUpdateUserInfosArg.firstName = 'New' // Relevant changes
|
||||
mockUser.firstName = 'New'
|
||||
await syncHumhub(mockUpdateUserInfosArg, mockUser, 'username')
|
||||
expect(logger.debug).toHaveBeenCalledTimes(8) // Four language logging calls, two debug calls in function, one for not syncing
|
||||
expect(logger.debug).toHaveBeenCalledTimes(4) // Four language logging calls, two debug calls in function, one for not syncing
|
||||
expect(logger.info).toHaveBeenLastCalledWith('finished sync user with humhub', {
|
||||
localId: mockUser.id,
|
||||
externId: mockHumHubUser.id,
|
||||
|
||||
@ -7,7 +7,10 @@ import { ExecutedHumhubAction, syncUser } from '@/apis/humhub/syncUser'
|
||||
import { PublishNameLogic } from '@/data/PublishName.logic'
|
||||
import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs'
|
||||
import { PublishNameType } from '@/graphql/enum/PublishNameType'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.syncHumhub`)
|
||||
|
||||
/**
|
||||
* Syncs the user with humhub
|
||||
@ -21,6 +24,8 @@ export async function syncHumhub(
|
||||
oldHumhubUsername: string,
|
||||
spaceId?: number | null,
|
||||
): Promise<GetUser | null | undefined> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('user', user.id)
|
||||
// check for humhub relevant changes
|
||||
if (
|
||||
updateUserInfosArg &&
|
||||
|
||||
@ -2,14 +2,18 @@ import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { User } from 'database'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization, logger } from '@test/testSetup'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
|
||||
import { validateAlias } from './validateAlias'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
@ -18,7 +22,7 @@ let testEnv: {
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment(logger, localization)
|
||||
testEnv = await testEnvironment(getLogger('apollo'), localization)
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import 'reflect-metadata'
|
||||
import 'source-map-support/register'
|
||||
import { getLogger } from 'log4js'
|
||||
import { CONFIG } from './config'
|
||||
import { startValidateCommunities } from './federation/validateCommunities'
|
||||
import { createServer } from './server/createServer'
|
||||
import { initLogging } from './server/logger'
|
||||
|
||||
async function main() {
|
||||
const { app } = await createServer()
|
||||
initLogging()
|
||||
const { app } = await createServer(getLogger('apollo'))
|
||||
|
||||
app.listen(CONFIG.PORT, () => {
|
||||
// biome-ignore lint/suspicious/noConsole: no need for logging the start message
|
||||
|
||||
@ -7,11 +7,14 @@ import { ContributionMessageBuilder } from '@/data/ContributionMessage.builder'
|
||||
import { ContributionStatus } from '@/graphql/enum/ContributionStatus'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { Context, getClientTimezoneOffset } from '@/server/context'
|
||||
import { Logger, getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
export abstract class AbstractUnconfirmedContributionRole {
|
||||
private availableCreationSums?: Decimal[]
|
||||
protected changed = true
|
||||
private currentStep = 0
|
||||
protected logger: Logger
|
||||
|
||||
public constructor(
|
||||
protected self: Contribution,
|
||||
@ -21,6 +24,8 @@ export abstract class AbstractUnconfirmedContributionRole {
|
||||
if (self.confirmedAt || self.deniedAt) {
|
||||
throw new LogError("this contribution isn't unconfirmed!")
|
||||
}
|
||||
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.interactions.updateUnconfirmedContribution`)
|
||||
this.logger.addContext('contribution', this.self.id)
|
||||
}
|
||||
|
||||
public isChanged(): boolean {
|
||||
|
||||
@ -6,8 +6,6 @@ import { ContributionMessageBuilder } from '@/data/ContributionMessage.builder'
|
||||
import { AdminUpdateContributionArgs } from '@/graphql/arg/AdminUpdateContributionArgs'
|
||||
import { ContributionStatus } from '@/graphql/enum/ContributionStatus'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { AbstractUnconfirmedContributionRole } from './AbstractUnconfirmedContribution.role'
|
||||
|
||||
/**
|
||||
@ -25,7 +23,7 @@ export class UnconfirmedContributionAdminRole extends AbstractUnconfirmedContrib
|
||||
updateData.amount ?? contribution.amount,
|
||||
updateData.creationDate ? new Date(updateData.creationDate) : contribution.contributionDate,
|
||||
)
|
||||
logger.debug('use UnconfirmedContributionAdminRole')
|
||||
this.logger.debug('use UnconfirmedContributionAdminRole')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -7,7 +7,6 @@ import { ContributionMessageArgs } from '@/graphql/arg/ContributionMessageArgs'
|
||||
import { ContributionMessageType } from '@/graphql/enum/ContributionMessageType'
|
||||
import { ContributionStatus } from '@/graphql/enum/ContributionStatus'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { AbstractUnconfirmedContributionRole } from './AbstractUnconfirmedContribution.role'
|
||||
|
||||
@ -21,7 +20,7 @@ export class UnconfirmedContributionAdminAddMessageRole extends AbstractUnconfir
|
||||
private updateData: ContributionMessageArgs,
|
||||
) {
|
||||
super(contribution, contribution.amount, contribution.contributionDate)
|
||||
logger.debug('use UnconfirmedContributionAdminAddMessageRole')
|
||||
this.logger.debug('use UnconfirmedContributionAdminAddMessageRole')
|
||||
}
|
||||
|
||||
protected update(): void {
|
||||
|
||||
@ -4,7 +4,6 @@ import { ContributionMessageBuilder } from '@/data/ContributionMessage.builder'
|
||||
import { ContributionArgs } from '@/graphql/arg/ContributionArgs'
|
||||
import { ContributionStatus } from '@/graphql/enum/ContributionStatus'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { AbstractUnconfirmedContributionRole } from './AbstractUnconfirmedContribution.role'
|
||||
|
||||
@ -18,7 +17,7 @@ export class UnconfirmedContributionUserRole extends AbstractUnconfirmedContribu
|
||||
private updateData: ContributionArgs,
|
||||
) {
|
||||
super(contribution, updateData.amount, new Date(updateData.contributionDate))
|
||||
logger.debug('use UnconfirmedContributionUserRole')
|
||||
this.logger.debug('use UnconfirmedContributionUserRole')
|
||||
}
|
||||
|
||||
protected update(): void {
|
||||
|
||||
@ -5,7 +5,6 @@ import { ContributionMessageArgs } from '@/graphql/arg/ContributionMessageArgs'
|
||||
import { ContributionMessageType } from '@/graphql/enum/ContributionMessageType'
|
||||
import { ContributionStatus } from '@/graphql/enum/ContributionStatus'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { AbstractUnconfirmedContributionRole } from './AbstractUnconfirmedContribution.role'
|
||||
|
||||
@ -19,7 +18,7 @@ export class UnconfirmedContributionUserAddMessageRole extends AbstractUnconfirm
|
||||
private updateData: ContributionMessageArgs,
|
||||
) {
|
||||
super(contribution, contribution.amount, contribution.contributionDate)
|
||||
logger.debug('use UnconfirmedContributionUserAddMessageRole')
|
||||
this.logger.debug('use UnconfirmedContributionUserAddMessageRole')
|
||||
}
|
||||
|
||||
protected update(): void {
|
||||
|
||||
@ -8,7 +8,6 @@ import { PasswordEncryptionType } from '@enum/PasswordEncryptionType'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { crypto_shorthash_KEYBYTES } from 'sodium-native'
|
||||
|
||||
@ -40,7 +39,6 @@ export const SecretKeyCryptographyCreateKey = async (
|
||||
password: string,
|
||||
): Promise<bigint> => {
|
||||
try {
|
||||
logger.trace('call worker for: SecretKeyCryptographyCreateKey')
|
||||
if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) {
|
||||
throw new LogError(
|
||||
'ServerKey has an invalid size',
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { User } from 'database'
|
||||
|
||||
// import { logger } from '@test/testSetup' getting error "jest is not defined"
|
||||
import { SecretKeyCryptographyCreateKey, getUserCryptographicSalt } from './EncryptorUtils'
|
||||
|
||||
export const encryptPassword = async (dbUser: User, password: string): Promise<bigint> => {
|
||||
|
||||
@ -4,8 +4,9 @@ import { PasswordEncryptionType } from '@enum/PasswordEncryptionType'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import {
|
||||
crypto_box_SEEDBYTES,
|
||||
crypto_hash_sha512_BYTES,
|
||||
@ -22,6 +23,8 @@ import {
|
||||
crypto_shorthash_KEYBYTES,
|
||||
} from 'sodium-native'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.password.EncryptorUtils`)
|
||||
|
||||
const SecretKeyCryptographyCreateKeyMock = (
|
||||
salt: string,
|
||||
password: string,
|
||||
|
||||
@ -15,11 +15,11 @@ export const userFactory = async (
|
||||
const { mutate } = client
|
||||
|
||||
const homeCom = await writeHomeCommunityEntry()
|
||||
|
||||
// console.log('call createUser with', JSON.stringify(user, null, 2))
|
||||
const response = await mutate({ mutation: createUser, variables: user })
|
||||
if (!response?.data?.createUser) {
|
||||
// biome-ignore lint/suspicious/noConsole: will be used in tests where logging is mocked
|
||||
console.log(response)
|
||||
// console.log(JSON.stringify(response, null, 2))
|
||||
throw new Error('createUser mutation returned unexpected response')
|
||||
}
|
||||
const {
|
||||
|
||||
@ -4,8 +4,9 @@ import { datatype, internet, name } from 'faker'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { createServer } from '@/server/createServer'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { initLogging } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
import { writeHomeCommunityEntry } from './community'
|
||||
import { contributionLinks } from './contributionLink/index'
|
||||
import { creations } from './creation/index'
|
||||
@ -17,6 +18,7 @@ import { transactionLinks } from './transactionLink/index'
|
||||
import { users } from './users/index'
|
||||
|
||||
CONFIG.EMAIL = false
|
||||
const logger = getLogger('seed')
|
||||
|
||||
const context = {
|
||||
token: '',
|
||||
@ -48,7 +50,8 @@ const resetEntity = async (entity: any) => {
|
||||
}
|
||||
|
||||
const run = async () => {
|
||||
const server = await createServer(context)
|
||||
initLogging()
|
||||
const server = await createServer(getLogger('apollo'), context)
|
||||
const seedClient = createTestClient(server.apollo)
|
||||
const { con } = server
|
||||
await cleanDB()
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { logger } from '@test/testSetup'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
|
||||
import { LogError } from './LogError'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
|
||||
describe('LogError', () => {
|
||||
it('logs an Error when created', () => {
|
||||
new LogError('new LogError')
|
||||
|
||||
@ -1,5 +1,21 @@
|
||||
import { backendLogger as logger } from './logger'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
@ -6,14 +6,14 @@ import { ApolloServer } from 'apollo-server-express'
|
||||
import express, { Express, json, urlencoded } from 'express'
|
||||
import { slowDown } from 'express-slow-down'
|
||||
import helmet from 'helmet'
|
||||
import { Logger } from 'log4js'
|
||||
import { Logger, getLogger } from 'log4js'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { AppDatabase } from 'database'
|
||||
import { context as serverContext } from './context'
|
||||
import { cors } from './cors'
|
||||
import { i18n } from './localization'
|
||||
import { apolloLogger } from './logger'
|
||||
import { plugins } from './plugins'
|
||||
// TODO implement
|
||||
// import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
|
||||
@ -25,11 +25,11 @@ interface ServerDef {
|
||||
}
|
||||
|
||||
export const createServer = async (
|
||||
apolloLogger: Logger,
|
||||
context: any = serverContext,
|
||||
logger: Logger = apolloLogger,
|
||||
localization: i18n.I18n = i18n,
|
||||
): Promise<ServerDef> => {
|
||||
logger.addContext('user', 'unknown')
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.createServer`)
|
||||
logger.debug('createServer...')
|
||||
|
||||
// open mariadb connection, retry connecting with mariadb
|
||||
@ -91,7 +91,7 @@ export const createServer = async (
|
||||
introspection: CONFIG.GRAPHIQL,
|
||||
context,
|
||||
plugins,
|
||||
logger,
|
||||
logger: apolloLogger,
|
||||
})
|
||||
apollo.applyMiddleware({ app, path: '/' })
|
||||
logger.info(
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import path from 'node:path'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import i18n from 'i18n'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { backendLogger } from './logger'
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.localization`)
|
||||
|
||||
i18n.configure({
|
||||
locales: ['en', 'de'],
|
||||
@ -11,9 +13,9 @@ i18n.configure({
|
||||
// autoReload: true, // if this is activated the seeding hangs at the very end
|
||||
updateFiles: false,
|
||||
objectNotation: true,
|
||||
logDebugFn: (msg) => backendLogger.debug(msg),
|
||||
logWarnFn: (msg) => backendLogger.info(msg),
|
||||
logErrorFn: (msg) => backendLogger.error(msg),
|
||||
logDebugFn: (msg) => logger.debug(msg),
|
||||
logWarnFn: (msg) => logger.info(msg),
|
||||
logErrorFn: (msg) => logger.error(msg),
|
||||
// this api is needed for email-template pug files
|
||||
api: {
|
||||
__: 't', // now req.__ becomes req.t
|
||||
|
||||
@ -1,21 +1,17 @@
|
||||
import { readFileSync } from 'fs'
|
||||
|
||||
import { configure, getLogger } from 'log4js'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { defaultCategory, initLogger } from 'config-schema'
|
||||
|
||||
const options = JSON.parse(readFileSync(CONFIG.LOG4JS_CONFIG, 'utf-8'))
|
||||
|
||||
options.categories.backend.level = CONFIG.LOG_LEVEL
|
||||
options.categories.apollo.level = CONFIG.LOG_LEVEL
|
||||
|
||||
configure(options)
|
||||
|
||||
const apolloLogger = getLogger('apollo')
|
||||
const backendLogger = getLogger('backend')
|
||||
const klickTippLogger = getLogger('klicktipp')
|
||||
const gmsLogger = getLogger('gms')
|
||||
|
||||
backendLogger.addContext('user', 'unknown')
|
||||
|
||||
export { apolloLogger, backendLogger, klickTippLogger, gmsLogger }
|
||||
export function initLogging() {
|
||||
// init logger
|
||||
initLogger(
|
||||
[
|
||||
defaultCategory('backend', CONFIG.LOG_LEVEL),
|
||||
defaultCategory('apollo', CONFIG.LOG_LEVEL),
|
||||
defaultCategory('klicktipp', CONFIG.LOG_LEVEL),
|
||||
defaultCategory('gms', CONFIG.LOG_LEVEL),
|
||||
defaultCategory('seed', CONFIG.LOG_LEVEL),
|
||||
],
|
||||
CONFIG.LOG_FILES_BASE_PATH,
|
||||
CONFIG.LOG4JS_CONFIG,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
export enum MonitorNames {
|
||||
SEND_DLT_TRANSACTIONS = 1,
|
||||
@ -15,6 +16,7 @@ registerEnumType(MonitorNames, {
|
||||
/* @typescript-eslint/no-extraneous-class */
|
||||
export class Monitor {
|
||||
private static locks = new Map<MonitorNames, boolean>()
|
||||
private static logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.util.Monitor`)
|
||||
|
||||
private constructor() {}
|
||||
|
||||
@ -25,15 +27,15 @@ export class Monitor {
|
||||
|
||||
public static isLocked(key: MonitorNames): boolean | undefined {
|
||||
if (this.locks.has(key)) {
|
||||
logger.debug(`Monitor isLocked key=${key} = `, this.locks.get(key))
|
||||
this.logger.debug(`Monitor isLocked key=${key} = `, this.locks.get(key))
|
||||
return this.locks.get(key)
|
||||
}
|
||||
logger.debug(`Monitor isLocked key=${key} not exists`)
|
||||
this.logger.debug(`Monitor isLocked key=${key} not exists`)
|
||||
return false
|
||||
}
|
||||
|
||||
public static lockIt(key: MonitorNames): void {
|
||||
logger.debug(`Monitor lockIt key=`, key)
|
||||
this.logger.debug(`Monitor lockIt key=`, key)
|
||||
if (this.locks.has(key)) {
|
||||
throw new LogError('still existing Monitor with key=', key)
|
||||
}
|
||||
@ -41,7 +43,7 @@ export class Monitor {
|
||||
}
|
||||
|
||||
public static releaseIt(key: MonitorNames): void {
|
||||
logger.debug(`Monitor releaseIt key=`, key)
|
||||
this.logger.debug(`Monitor releaseIt key=`, key)
|
||||
if (this.locks.has(key)) {
|
||||
this.locks.delete(key)
|
||||
}
|
||||
|
||||
@ -2,8 +2,13 @@ import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { Decay } from '@model/Decay'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { DECAY_START_TIME } from 'config-schema'
|
||||
|
||||
Decimal.set({
|
||||
precision: 25,
|
||||
rounding: Decimal.ROUND_HALF_UP,
|
||||
})
|
||||
|
||||
// TODO: externalize all those definitions and functions into an external decay library
|
||||
|
||||
@ -18,7 +23,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()
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { AppDatabase } from 'database'
|
||||
|
||||
import { exportEventDataToKlickTipp } from './klicktipp'
|
||||
import { initLogging } from '@/server/logger'
|
||||
|
||||
async function executeKlicktipp(): Promise<boolean> {
|
||||
initLogging()
|
||||
const connection = AppDatabase.getInstance()
|
||||
await connection.init()
|
||||
if (connection.isConnected()) {
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
/**
|
||||
* @param {number} time - in minutes
|
||||
*/
|
||||
export const getTimeDurationObject = (
|
||||
time: number,
|
||||
): {
|
||||
@ -13,11 +16,27 @@ export const getTimeDurationObject = (
|
||||
return { minutes: time }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startDate
|
||||
* @param endDate
|
||||
* @returns duration in minutes
|
||||
*/
|
||||
export const durationInMinutesFromDates = (startDate: Date, endDate: Date): number => {
|
||||
const diff = endDate.getTime() - startDate.getTime()
|
||||
return Math.floor(diff / (1000 * 60))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param duration in minutes
|
||||
*/
|
||||
export const printTimeDuration = (duration: number): string => {
|
||||
const time = getTimeDurationObject(duration)
|
||||
const result = time.minutes > 0 ? `${time.minutes} minutes` : ''
|
||||
if (time.hours) {
|
||||
return `${time.hours} hours` + (result !== '' ? ` and ${result}` : '')
|
||||
}
|
||||
if (result === '') {
|
||||
return '0 minutes'
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@ -25,10 +25,13 @@
|
||||
I assume that the webhook arrives via POST and transmits a string as shown above
|
||||
*/
|
||||
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { LoginElopageBuys, UserContact as dbUserContact } from 'database'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { UserResolver } from '@/graphql/resolver/UserResolver'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.webhook.elopage`)
|
||||
|
||||
export const elopageWebhook = async (req: any, res: any): Promise<void> => {
|
||||
logger.info('Elopage Hook received')
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { User as DbUser } from 'database'
|
||||
|
||||
import { decode } from '@/auth/JWT'
|
||||
import { gmsLogger as logger } from '@/server/logger'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.webhook.gms`)
|
||||
|
||||
export const gmsWebhook = async (req: any, res: any): Promise<void> => {
|
||||
logger.info('GMS Hook received')
|
||||
|
||||
@ -3,7 +3,9 @@ import { entities } from 'database'
|
||||
|
||||
import { createServer } from '@/server/createServer'
|
||||
|
||||
import { i18n, logger } from './testSetup'
|
||||
import { i18n } from './testSetup'
|
||||
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
export const headerPushMock = jest.fn((t) => {
|
||||
context.token = t.value
|
||||
@ -25,8 +27,8 @@ export const cleanDB = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const testEnvironment = async (testLogger = logger, testI18n = i18n) => {
|
||||
const server = await createServer(context, testLogger, testI18n)
|
||||
export const testEnvironment = async (testLogger = getLogger('apollo'), testI18n = i18n) => {
|
||||
const server = await createServer( testLogger, context, testI18n)
|
||||
const con = server.con
|
||||
const testClient = createTestClient(server.apollo)
|
||||
const mutate = testClient.mutate
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import 'openai/shims/node'
|
||||
import { CONFIG } from '@/config'
|
||||
import { i18n } from '@/server/localization'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { getLogger, printLogs, clearLogs } from 'config-schema/test/testSetup'
|
||||
|
||||
CONFIG.EMAIL = true
|
||||
CONFIG.EMAIL_TEST_MODUS = false
|
||||
@ -10,23 +10,6 @@ CONFIG.GMS_ACTIVE = 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 {
|
||||
@ -41,4 +24,4 @@ jest.mock('@/server/localization', () => {
|
||||
}
|
||||
})
|
||||
|
||||
export { logger, i18n }
|
||||
export { i18n, getLogger, printLogs, clearLogs as cleanLogs }
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
"sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "./build", /* Redirect output structure to the directory. */
|
||||
// "rootDir" : ".",
|
||||
|
||||
@ -24,6 +24,9 @@
|
||||
},
|
||||
"start": {
|
||||
"dependsOn": ["database#up", "build"]
|
||||
},
|
||||
"start:bundle": {
|
||||
"dependsOn": ["build:bundle"]
|
||||
}
|
||||
}
|
||||
}
|
||||
48
biome.json
48
biome.json
@ -1,16 +1,18 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
|
||||
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"ignore": ["build", "node_modules", "coverage"],
|
||||
"include": [
|
||||
"package.json",
|
||||
"./src/**/*.js",
|
||||
"./src/**/*.ts",
|
||||
"./entity/**/*.ts",
|
||||
"./logging/**/*.ts",
|
||||
"./migrations/**/*.ts"
|
||||
"includes": [
|
||||
"**/package.json",
|
||||
"src/**/*.js",
|
||||
"src/**/*.ts",
|
||||
"entity/**/*.ts",
|
||||
"logging/**/*.ts",
|
||||
"migrations/**/*.ts",
|
||||
"!**/build",
|
||||
"!**/node_modules",
|
||||
"!**/coverage"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
@ -24,14 +26,13 @@
|
||||
"attributePosition": "auto",
|
||||
"bracketSpacing": true
|
||||
},
|
||||
"organizeImports": { "enabled": true },
|
||||
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": false,
|
||||
"complexity": {
|
||||
"noExtraBooleanCast": "error",
|
||||
"noMultipleSpacesInRegularExpressionLiterals": "error",
|
||||
"noUselessCatch": "error",
|
||||
"noUselessConstructor": "error",
|
||||
"noUselessLoneBlockStatements": "error",
|
||||
@ -39,9 +40,10 @@
|
||||
"noUselessTernary": "error",
|
||||
"noUselessUndefinedInitialization": "error",
|
||||
"noVoid": "error",
|
||||
"noWith": "error",
|
||||
"useLiteralKeys": "error",
|
||||
"useRegexLiterals": "error"
|
||||
"useRegexLiterals": "error",
|
||||
"noAdjacentSpacesInRegex": "error",
|
||||
"noCommaOperator": "error"
|
||||
},
|
||||
"correctness": {
|
||||
"noConstAssign": "error",
|
||||
@ -52,7 +54,6 @@
|
||||
"noInnerDeclarations": "error",
|
||||
"noInvalidConstructorSuper": "error",
|
||||
"noInvalidUseBeforeDeclaration": "error",
|
||||
"noNewSymbol": "error",
|
||||
"noNodejsModules": "off",
|
||||
"noNonoctalDecimalEscape": "error",
|
||||
"noPrecisionLoss": "error",
|
||||
@ -66,22 +67,22 @@
|
||||
"noUnsafeOptionalChaining": "error",
|
||||
"noUnusedLabels": "error",
|
||||
"noUnusedVariables": "error",
|
||||
"useArrayLiterals": "error",
|
||||
"useIsNan": "error",
|
||||
"useValidForDirection": "error",
|
||||
"useYield": "error"
|
||||
"useYield": "error",
|
||||
"noInvalidBuiltinInstantiation": "error",
|
||||
"useValidTypeof": "error"
|
||||
},
|
||||
"security": { "noGlobalEval": "error" },
|
||||
"style": {
|
||||
"noCommaOperator": "error",
|
||||
"noDefaultExport": "error",
|
||||
"noVar": "warn",
|
||||
"noYodaExpression": "error",
|
||||
"useBlockStatements": "error",
|
||||
"useConsistentBuiltinInstantiation": "error",
|
||||
"useConst": "error",
|
||||
"useImportType": "off",
|
||||
"useSingleVarDeclarator": "error"
|
||||
"useSingleVarDeclarator": "error",
|
||||
"useArrayLiterals": "error"
|
||||
},
|
||||
"suspicious": {
|
||||
"noAsyncPromiseExecutor": "error",
|
||||
@ -110,10 +111,11 @@
|
||||
"noUnsafeNegation": "error",
|
||||
"useDefaultSwitchClauseLast": "error",
|
||||
"useGetterReturn": "error",
|
||||
"useValidTypeof": "error"
|
||||
"noWith": "error",
|
||||
"noVar": "warn"
|
||||
}
|
||||
},
|
||||
"ignore": ["**/node_modules", "**/*.min.js", "**/build", "**/coverage"]
|
||||
"includes": ["**", "!**/node_modules", "!**/*.min.js", "!**/build", "!**/coverage"]
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
@ -147,9 +149,9 @@
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"include": ["*.ts", "*.tsx"],
|
||||
"includes": ["**/*.ts", "**/*.tsx"],
|
||||
"linter": { "rules": { "complexity": { "noVoid": "error" } } }
|
||||
},
|
||||
{ "include": ["*.test.ts"], "linter": { "rules": {} } }
|
||||
{ "includes": ["**/*.test.ts"], "linter": { "rules": {} } }
|
||||
]
|
||||
}
|
||||
|
||||
65
bun.lock
65
bun.lock
@ -10,7 +10,7 @@
|
||||
"uuid": "^8.3.2",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@biomejs/biome": "2.0.0",
|
||||
},
|
||||
},
|
||||
"admin": {
|
||||
@ -92,7 +92,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anatine/esbuild-decorators": "^0.2.19",
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@swc/cli": "^0.7.3",
|
||||
"@swc/core": "^1.11.24",
|
||||
"@swc/helpers": "^0.5.17",
|
||||
@ -105,6 +105,7 @@
|
||||
"@types/node": "^17.0.21",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/sodium-native": "^2.3.5",
|
||||
"@types/source-map-support": "^0.5.10",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"apollo-server-express": "^2.25.2",
|
||||
"apollo-server-testing": "^2.25.2",
|
||||
@ -143,6 +144,7 @@
|
||||
"random-bigint": "^0.0.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-jest": "27.0.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsconfig-paths": "^4.1.1",
|
||||
@ -161,10 +163,15 @@
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.2",
|
||||
"joi": "^17.13.3",
|
||||
"log4js": "^6.9.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"yoctocolors-cjs": "^2.1.2",
|
||||
"zod": "^3.25.61",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@types/node": "^17.0.21",
|
||||
"jest": "27.2.4",
|
||||
"typescript": "^4.9.5",
|
||||
},
|
||||
},
|
||||
@ -182,6 +189,7 @@
|
||||
"log4js": "^6.9.1",
|
||||
"mysql2": "^2.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-mysql-migrate": "^1.0.2",
|
||||
"tsx": "^4.19.4",
|
||||
"typeorm": "^0.3.22",
|
||||
@ -189,7 +197,7 @@
|
||||
"wkx": "^0.5.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@types/faker": "^5.5.9",
|
||||
"@types/geojson": "^7946.0.13",
|
||||
"@types/node": "^17.0.21",
|
||||
@ -205,8 +213,12 @@
|
||||
"sodium-universal": "4.0.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@hyperswarm/dht": "6.5.1",
|
||||
"@swc/cli": "^0.7.3",
|
||||
"@swc/core": "^1.11.24",
|
||||
"@swc/helpers": "^0.5.17",
|
||||
"@swc/jest": "^0.2.38",
|
||||
"@types/dotenv": "^8.2.3",
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/joi": "^17.2.3",
|
||||
@ -219,7 +231,9 @@
|
||||
"jest": "27.5.1",
|
||||
"joi": "^17.13.3",
|
||||
"log4js": "^6.9.1",
|
||||
"nodemon": "^2.0.7",
|
||||
"prettier": "^2.8.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-jest": "27.1.4",
|
||||
"tsx": "^4.19.4",
|
||||
"typeorm": "^0.3.22",
|
||||
@ -236,7 +250,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anatine/esbuild-decorators": "^0.2.19",
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@swc/cli": "^0.7.3",
|
||||
"@swc/core": "^1.11.24",
|
||||
"@swc/helpers": "^0.5.17",
|
||||
@ -269,6 +283,7 @@
|
||||
"nodemon": "^2.0.7",
|
||||
"prettier": "^3.5.3",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ts-jest": "27.0.5",
|
||||
"tsconfig-paths": "^4.1.1",
|
||||
"type-graphql": "^1.1.1",
|
||||
@ -464,23 +479,23 @@
|
||||
|
||||
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="],
|
||||
|
||||
"@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="],
|
||||
"@biomejs/biome": ["@biomejs/biome@2.0.0", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.0", "@biomejs/cli-darwin-x64": "2.0.0", "@biomejs/cli-linux-arm64": "2.0.0", "@biomejs/cli-linux-arm64-musl": "2.0.0", "@biomejs/cli-linux-x64": "2.0.0", "@biomejs/cli-linux-x64-musl": "2.0.0", "@biomejs/cli-win32-arm64": "2.0.0", "@biomejs/cli-win32-x64": "2.0.0" }, "bin": { "biome": "bin/biome" } }, "sha512-BlUoXEOI/UQTDEj/pVfnkMo8SrZw3oOWBDrXYFT43V7HTkIUDkBRY53IC5Jx1QkZbaB+0ai1wJIfYwp9+qaJTQ=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="],
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="],
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="],
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="],
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="],
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="],
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="],
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2USVQ0hklNsph/KIR72ZdeptyXNnQ3JdzPn3NbjI4Sna34CnxeiYAaZcZzXPDl5PYNFBivV4xmvT3Z3rTmyDBg=="],
|
||||
|
||||
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
|
||||
|
||||
@ -626,6 +641,8 @@
|
||||
|
||||
"@jest/core": ["@jest/core@27.5.1", "", { "dependencies": { "@jest/console": "^27.5.1", "@jest/reporters": "^27.5.1", "@jest/test-result": "^27.5.1", "@jest/transform": "^27.5.1", "@jest/types": "^27.5.1", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.8.1", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "jest-changed-files": "^27.5.1", "jest-config": "^27.5.1", "jest-haste-map": "^27.5.1", "jest-message-util": "^27.5.1", "jest-regex-util": "^27.5.1", "jest-resolve": "^27.5.1", "jest-resolve-dependencies": "^27.5.1", "jest-runner": "^27.5.1", "jest-runtime": "^27.5.1", "jest-snapshot": "^27.5.1", "jest-util": "^27.5.1", "jest-validate": "^27.5.1", "jest-watcher": "^27.5.1", "micromatch": "^4.0.4", "rimraf": "^3.0.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "optionalPeers": ["node-notifier"] }, "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ=="],
|
||||
|
||||
"@jest/create-cache-key-function": ["@jest/create-cache-key-function@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3" } }, "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA=="],
|
||||
|
||||
"@jest/environment": ["@jest/environment@27.5.1", "", { "dependencies": { "@jest/fake-timers": "^27.5.1", "@jest/types": "^27.5.1", "@types/node": "*", "jest-mock": "^27.5.1" } }, "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA=="],
|
||||
|
||||
"@jest/fake-timers": ["@jest/fake-timers@27.5.1", "", { "dependencies": { "@jest/types": "^27.5.1", "@sinonjs/fake-timers": "^8.0.1", "@types/node": "*", "jest-message-util": "^27.5.1", "jest-mock": "^27.5.1", "jest-util": "^27.5.1" } }, "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ=="],
|
||||
@ -870,6 +887,8 @@
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
|
||||
|
||||
"@swc/jest": ["@swc/jest@0.2.38", "", { "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@swc/counter": "^0.1.3", "jsonc-parser": "^3.2.0" }, "peerDependencies": { "@swc/core": "*" } }, "sha512-HMoZgXWMqChJwffdDjvplH53g9G2ALQes3HKXDEdliB/b85OQ0CTSbxG8VSeCwiAn7cOaDVEt4mwmZvbHcS52w=="],
|
||||
|
||||
"@swc/types": ["@swc/types@0.1.21", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ=="],
|
||||
|
||||
"@szmarczak/http-timer": ["@szmarczak/http-timer@5.0.1", "", { "dependencies": { "defer-to-connect": "^2.0.1" } }, "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw=="],
|
||||
@ -996,6 +1015,8 @@
|
||||
|
||||
"@types/sodium-native": ["@types/sodium-native@2.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-jZIg5ltGH1okmnH3FrLQsgwjcjOVozMSHwSiEm1/LpMekhOMHbQqp21P4H24mizh1BjwI6Q8qmphmD/HJuAqWg=="],
|
||||
|
||||
"@types/source-map-support": ["@types/source-map-support@0.5.10", "", { "dependencies": { "source-map": "^0.6.0" } }, "sha512-tgVP2H469x9zq34Z0m/fgPewGhg/MLClalNOiPIzQlXrSS2YrKu/xCdSCKnEDwkFha51VKEKB6A9wW26/ZNwzA=="],
|
||||
|
||||
"@types/stack-utils": ["@types/stack-utils@2.0.3", "", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="],
|
||||
|
||||
"@types/uuid": ["@types/uuid@8.3.4", "", {}, "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="],
|
||||
@ -2228,6 +2249,8 @@
|
||||
|
||||
"jsonc-eslint-parser": ["jsonc-eslint-parser@2.4.0", "", { "dependencies": { "acorn": "^8.5.0", "eslint-visitor-keys": "^3.0.0", "espree": "^9.0.0", "semver": "^7.3.5" } }, "sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg=="],
|
||||
|
||||
"jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
|
||||
|
||||
"jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
|
||||
|
||||
"jstransformer": ["jstransformer@1.0.0", "", { "dependencies": { "is-promise": "^2.0.0", "promise": "^7.0.1" } }, "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A=="],
|
||||
@ -3286,13 +3309,15 @@
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="],
|
||||
|
||||
"yup": ["yup@1.6.1", "", { "dependencies": { "property-expr": "^2.0.5", "tiny-case": "^1.0.3", "toposort": "^2.0.2", "type-fest": "^2.19.0" } }, "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA=="],
|
||||
|
||||
"zen-observable": ["zen-observable@0.8.15", "", {}, "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ=="],
|
||||
|
||||
"zen-observable-ts": ["zen-observable-ts@1.2.5", "", { "dependencies": { "zen-observable": "0.8.15" } }, "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg=="],
|
||||
|
||||
"zod": ["zod@3.25.55", "", {}, "sha512-219huNnkSLQnLsQ3uaRjXsxMrVm5C9W3OOpEVt2k5tvMKuA8nBSu38e0B//a+he9Iq2dvmk2VyYVlHqiHa4YBA=="],
|
||||
"zod": ["zod@3.25.61", "", {}, "sha512-fzfJgUw78LTNnHujj9re1Ov/JJQkRZZGDMcYqSx7Hp4rPOkKywaFHq0S6GoHeXs0wGNE/sIOutkXgnwzrVOGCQ=="],
|
||||
|
||||
"@apollo/protobufjs/@types/node": ["@types/node@10.17.60", "", {}, "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="],
|
||||
|
||||
@ -3360,6 +3385,8 @@
|
||||
|
||||
"@jest/core/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
|
||||
|
||||
"@jest/create-cache-key-function/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
|
||||
|
||||
"@jest/environment/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
|
||||
|
||||
"@jest/fake-timers/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
|
||||
@ -3434,6 +3461,8 @@
|
||||
|
||||
"@types/sodium-native/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
|
||||
|
||||
"@types/source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"@types/ws/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
@ -3910,6 +3939,10 @@
|
||||
|
||||
"@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
|
||||
|
||||
"@jest/create-cache-key-function/@jest/types/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="],
|
||||
|
||||
"@jest/create-cache-key-function/@jest/types/@types/yargs": ["@types/yargs@17.0.33", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA=="],
|
||||
|
||||
"@jest/reporters/jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
||||
|
||||
"@jest/transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
|
||||
@ -1,131 +0,0 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"ignore": ["build", "node_modules"],
|
||||
"include": ["./src/**/*.js", "./src/**/*.ts"]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"useEditorconfig": true,
|
||||
"formatWithErrors": false,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineEnding": "lf",
|
||||
"lineWidth": 100,
|
||||
"attributePosition": "auto",
|
||||
"bracketSpacing": true
|
||||
},
|
||||
"organizeImports": { "enabled": true },
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": false,
|
||||
"complexity": {
|
||||
"noExtraBooleanCast": "error",
|
||||
"noMultipleSpacesInRegularExpressionLiterals": "error",
|
||||
"noUselessCatch": "error",
|
||||
"noUselessConstructor": "error",
|
||||
"noUselessLoneBlockStatements": "error",
|
||||
"noUselessRename": "error",
|
||||
"noUselessTernary": "error",
|
||||
"noUselessUndefinedInitialization": "error",
|
||||
"noVoid": "error",
|
||||
"noWith": "error",
|
||||
"useLiteralKeys": "error",
|
||||
"useRegexLiterals": "error"
|
||||
},
|
||||
"correctness": {
|
||||
"noConstAssign": "error",
|
||||
"noConstantCondition": "error",
|
||||
"noEmptyCharacterClassInRegex": "error",
|
||||
"noEmptyPattern": "error",
|
||||
"noGlobalObjectCalls": "error",
|
||||
"noInnerDeclarations": "error",
|
||||
"noInvalidConstructorSuper": "error",
|
||||
"noInvalidUseBeforeDeclaration": "error",
|
||||
"noNewSymbol": "error",
|
||||
"noNodejsModules": "off",
|
||||
"noNonoctalDecimalEscape": "error",
|
||||
"noPrecisionLoss": "error",
|
||||
"noSelfAssign": "error",
|
||||
"noSetterReturn": "error",
|
||||
"noSwitchDeclarations": "error",
|
||||
"noUndeclaredVariables": "error",
|
||||
"noUnreachable": "error",
|
||||
"noUnreachableSuper": "error",
|
||||
"noUnsafeFinally": "error",
|
||||
"noUnsafeOptionalChaining": "error",
|
||||
"noUnusedLabels": "error",
|
||||
"noUnusedVariables": "error",
|
||||
"useArrayLiterals": "error",
|
||||
"useIsNan": "error",
|
||||
"useValidForDirection": "error",
|
||||
"useYield": "error"
|
||||
},
|
||||
"security": { "noGlobalEval": "error" },
|
||||
"style": {
|
||||
"noCommaOperator": "error",
|
||||
"noDefaultExport": "error",
|
||||
"noVar": "warn",
|
||||
"noYodaExpression": "error",
|
||||
"useBlockStatements": "error",
|
||||
"useConsistentBuiltinInstantiation": "error",
|
||||
"useConst": "error",
|
||||
"useSingleVarDeclarator": "error"
|
||||
},
|
||||
"suspicious": {
|
||||
"noAsyncPromiseExecutor": "error",
|
||||
"noCatchAssign": "error",
|
||||
"noClassAssign": "error",
|
||||
"noCompareNegZero": "error",
|
||||
"noConsole": "error",
|
||||
"noControlCharactersInRegex": "error",
|
||||
"noDebugger": "error",
|
||||
"noDoubleEquals": "error",
|
||||
"noDuplicateCase": "error",
|
||||
"noDuplicateClassMembers": "error",
|
||||
"noDuplicateObjectKeys": "error",
|
||||
"noDuplicateParameters": "error",
|
||||
"noEmptyBlockStatements": "error",
|
||||
"noFallthroughSwitchClause": "error",
|
||||
"noFunctionAssign": "error",
|
||||
"noGlobalAssign": "error",
|
||||
"noImportAssign": "error",
|
||||
"noMisleadingCharacterClass": "error",
|
||||
"noPrototypeBuiltins": "error",
|
||||
"noRedeclare": "error",
|
||||
"noSelfCompare": "error",
|
||||
"noShadowRestrictedNames": "error",
|
||||
"noSparseArray": "error",
|
||||
"noUnsafeNegation": "error",
|
||||
"useDefaultSwitchClauseLast": "error",
|
||||
"useGetterReturn": "error",
|
||||
"useValidTypeof": "error"
|
||||
}
|
||||
},
|
||||
"ignore": ["**/node_modules", "**/*.min.js", "**/build", "**/coverage"]
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"jsxQuoteStyle": "single",
|
||||
"quoteProperties": "asNeeded",
|
||||
"trailingCommas": "all",
|
||||
"semicolons": "asNeeded",
|
||||
"arrowParentheses": "always",
|
||||
"bracketSameLine": false,
|
||||
"quoteStyle": "single",
|
||||
"attributePosition": "auto",
|
||||
"bracketSpacing": true
|
||||
},
|
||||
"globals": ["document", "navigator", "window"]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"include": ["*.ts", "*.tsx"],
|
||||
"linter": { "rules": { "complexity": { "noVoid": "error" } } }
|
||||
},
|
||||
{ "include": ["*.test.ts"], "linter": { "rules": {} } }
|
||||
]
|
||||
}
|
||||
@ -15,20 +15,26 @@
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "esbuild src/index.ts --outdir=build --platform=node --target=node18.20.7 --bundle --packages=external",
|
||||
"build": "esbuild src/index.ts --outdir=build --sourcemap --platform=node --target=node18.20.7 --bundle --packages=external",
|
||||
"build:bun": "bun build src/index.ts --outdir=build --target=bun --packages=external",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "bun test",
|
||||
"lint": "biome check --error-on-warnings .",
|
||||
"lint:fix": "biome check --error-on-warnings . --write"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@types/node": "^17.0.21",
|
||||
"jest": "27.2.4",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.2",
|
||||
"joi": "^17.13.3"
|
||||
"joi": "^17.13.3",
|
||||
"log4js": "^6.9.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
"yoctocolors-cjs": "^2.1.2",
|
||||
"zod": "^3.25.61"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
@ -21,12 +21,6 @@ export const browserUrls = Joi.array()
|
||||
.required()
|
||||
.description('All URLs need to have same protocol to prevent mixed block errors')
|
||||
|
||||
export const DECAY_START_TIME = Joi.date()
|
||||
.iso() // ISO 8601 format for date validation
|
||||
.description('The start time for decay, expected in ISO 8601 format (e.g. 2021-05-13T17:46:31Z)')
|
||||
.default(new Date('2021-05-13T17:46:31Z')) // default to the specified date if not provided
|
||||
.required()
|
||||
|
||||
export const COMMUNITY_URL = Joi.string()
|
||||
.uri({ scheme: ['http', 'https'] })
|
||||
.custom((value: string, helpers: Joi.CustomHelpers<string>) => {
|
||||
@ -131,6 +125,22 @@ export const LOG4JS_CONFIG = Joi.string()
|
||||
.default('log4js-config.json')
|
||||
.required()
|
||||
|
||||
export const LOG4JS_CONFIG_PLACEHOLDER = Joi.string()
|
||||
.pattern(/^[a-zA-Z0-9-_]+(%v)?\.json$/)
|
||||
.message(
|
||||
'LOG4JS_CONFIG_PLACEHOLDER must be a valid filename ending with .json can contain %v as API Version placeholder before ending',
|
||||
)
|
||||
.description('config file name for log4js config file')
|
||||
.default('log4js-config.json')
|
||||
.required()
|
||||
|
||||
export const LOG_FILES_BASE_PATH = Joi.string()
|
||||
.pattern(/^[a-zA-Z0-9-_\/\.]+$/)
|
||||
.message('LOG_FILES_BASE_PATH must be a valid folder name, relative or absolute')
|
||||
.description('log folder name for module log files')
|
||||
.default('../logs/backend')
|
||||
.optional()
|
||||
|
||||
export const LOGIN_APP_SECRET = Joi.string()
|
||||
.pattern(/^[a-fA-F0-9]+$/)
|
||||
.message('need to be valid hex')
|
||||
|
||||
1
config-schema/src/const.ts
Normal file
1
config-schema/src/const.ts
Normal file
@ -0,0 +1 @@
|
||||
export const DECAY_START_TIME = new Date('2021-05-13T17:46:31Z')
|
||||
@ -1,3 +1,7 @@
|
||||
import 'source-map-support/register'
|
||||
export * from './commonSchema'
|
||||
export { DatabaseConfigSchema } from './DatabaseConfigSchema'
|
||||
export { validate } from './validate'
|
||||
export type { LogLevel, Category } from './log4js-config'
|
||||
export { createLog4jsConfig, initLogger, defaultCategory } from './log4js-config'
|
||||
export { DECAY_START_TIME } from './const'
|
||||
|
||||
76
config-schema/src/log4js-config/appenders.ts
Normal file
76
config-schema/src/log4js-config/appenders.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import type {
|
||||
Appender,
|
||||
DateFileAppender,
|
||||
LogLevelFilterAppender,
|
||||
StandardOutputAppender,
|
||||
} from 'log4js'
|
||||
import { CustomFileAppender } from './types'
|
||||
|
||||
const fileAppenderTemplate = {
|
||||
type: 'dateFile' as const,
|
||||
pattern: 'yyyy-MM-dd',
|
||||
compress: true,
|
||||
keepFileExt: true,
|
||||
fileNameSep: '_',
|
||||
numBackups: 30,
|
||||
}
|
||||
|
||||
const defaultAppenders = {
|
||||
errorFile: {
|
||||
type: 'dateFile' as const,
|
||||
filename: 'errors.log',
|
||||
pattern: 'yyyy-MM-dd',
|
||||
layout: { type: 'coloredContext' as const, withStack: true },
|
||||
compress: true,
|
||||
keepFileExt: true,
|
||||
fileNameSep: '_',
|
||||
numBackups: 30,
|
||||
} as DateFileAppender,
|
||||
errors: {
|
||||
type: 'logLevelFilter' as const,
|
||||
level: 'error' as const,
|
||||
appender: 'errorFile' as const,
|
||||
} as LogLevelFilterAppender,
|
||||
out: {
|
||||
type: 'stdout' as const,
|
||||
layout: { type: 'coloredContext' as const, withStack: 'error' },
|
||||
} as StandardOutputAppender,
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the appender configuration for log4js.
|
||||
*
|
||||
* @param {CustomFileAppender[]} fileAppenders
|
||||
* the list of custom file appenders to add to the standard
|
||||
* appenders.
|
||||
* @param {string} [basePath]
|
||||
* the base path for all log files.
|
||||
* @param {boolean} [stacktraceOnStdout=false]
|
||||
* whether to show the stacktrace on the standard output
|
||||
* appender.
|
||||
* @returns {Object<string, Appender>}
|
||||
* the appender configuration as a map
|
||||
*/
|
||||
export function createAppenderConfig(
|
||||
fileAppenders: CustomFileAppender[],
|
||||
basePath?: string,
|
||||
): { [name: string]: Appender } {
|
||||
if (basePath) {
|
||||
defaultAppenders.errorFile.filename = `${basePath}/errors.log`
|
||||
}
|
||||
const customAppender: { [name: string]: Appender } = { ...defaultAppenders }
|
||||
|
||||
fileAppenders.forEach((appender) => {
|
||||
const filename = appender.filename ?? `${appender.name}.log`
|
||||
const dateFile: DateFileAppender = {
|
||||
...fileAppenderTemplate,
|
||||
filename: basePath ? `${basePath}/${filename}` : filename,
|
||||
}
|
||||
dateFile.layout = {
|
||||
type: 'coloredContext',
|
||||
...appender.layout,
|
||||
}
|
||||
customAppender[appender.name] = dateFile
|
||||
})
|
||||
return customAppender
|
||||
}
|
||||
135
config-schema/src/log4js-config/coloredContext.test.ts
Normal file
135
config-schema/src/log4js-config/coloredContext.test.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import { LoggingEvent, levels } from 'log4js'
|
||||
import colors from 'yoctocolors-cjs'
|
||||
import { createColoredContextLayout } from './coloredContext'
|
||||
|
||||
let defaultLogEvent: LoggingEvent
|
||||
let colorFn: (input: string) => string
|
||||
const startTime = new Date()
|
||||
const startTimeString = startTime.toISOString()
|
||||
|
||||
describe('createColoredContextLayout', () => {
|
||||
beforeEach(() => {
|
||||
defaultLogEvent = {
|
||||
level: levels.INFO,
|
||||
categoryName: 'config',
|
||||
data: ['message'],
|
||||
context: { user: 1 },
|
||||
startTime,
|
||||
fileName: 'file',
|
||||
lineNumber: 1,
|
||||
callStack: 'stack',
|
||||
pid: 1,
|
||||
serialise: () => {
|
||||
throw new Error('Function not implemented.')
|
||||
},
|
||||
}
|
||||
})
|
||||
it('returns a function', () => {
|
||||
expect(typeof createColoredContextLayout({})).toBe('function')
|
||||
})
|
||||
describe('level:info, color:green', () => {
|
||||
beforeEach(() => {
|
||||
defaultLogEvent.level = levels.INFO
|
||||
colorFn = colors.green
|
||||
})
|
||||
it('format with all undefined', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.INFO}] config -`)
|
||||
expect(createColoredContextLayout({})(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message`,
|
||||
)
|
||||
})
|
||||
it('format with stack', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.INFO}] config -`)
|
||||
expect(createColoredContextLayout({ withStack: true })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message \nstack`,
|
||||
)
|
||||
})
|
||||
it('format with file', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.INFO}] config -`)
|
||||
expect(createColoredContextLayout({ withFile: true })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message \n at file:1`,
|
||||
)
|
||||
})
|
||||
it('format with file only if it where level:warn', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.INFO}] config -`)
|
||||
expect(createColoredContextLayout({ withFile: 'warn' })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message`,
|
||||
)
|
||||
})
|
||||
it('format with line', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.INFO}] config:1 -`)
|
||||
expect(createColoredContextLayout({ withLine: true })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message`,
|
||||
)
|
||||
})
|
||||
it('format with line only if it where level:warn', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.INFO}] config -`)
|
||||
expect(createColoredContextLayout({ withLine: 'warn' })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message`,
|
||||
)
|
||||
})
|
||||
it('format with file and line', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.INFO}] config -`)
|
||||
expect(createColoredContextLayout({ withFile: true, withLine: true })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message \n at file:1`,
|
||||
)
|
||||
})
|
||||
it('format withStack: error, withLine: true, withFile: warn', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.INFO}] config:1 -`)
|
||||
expect(
|
||||
createColoredContextLayout({
|
||||
withStack: 'error',
|
||||
withFile: 'warn',
|
||||
withLine: true,
|
||||
})(defaultLogEvent),
|
||||
).toBe(`${coloredString} user=1 message`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('level:error, color:red', () => {
|
||||
beforeEach(() => {
|
||||
defaultLogEvent.level = levels.ERROR
|
||||
colorFn = colors.redBright
|
||||
})
|
||||
it('format with all undefined', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.ERROR}] config -`)
|
||||
expect(createColoredContextLayout({})(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message`,
|
||||
)
|
||||
})
|
||||
it('format with stack', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.ERROR}] config -`)
|
||||
expect(createColoredContextLayout({ withStack: true })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message \nstack`,
|
||||
)
|
||||
})
|
||||
it('format with file', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.ERROR}] config -`)
|
||||
expect(createColoredContextLayout({ withFile: true })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message \n at file:1`,
|
||||
)
|
||||
})
|
||||
it('format with line', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.ERROR}] config:1 -`)
|
||||
expect(createColoredContextLayout({ withLine: true })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message`,
|
||||
)
|
||||
})
|
||||
it('format with file and line', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.ERROR}] config -`)
|
||||
expect(createColoredContextLayout({ withFile: true, withLine: true })(defaultLogEvent)).toBe(
|
||||
`${coloredString} user=1 message \n at file:1`,
|
||||
)
|
||||
})
|
||||
it('format withStack: error, withLine: true, withFile: warn', () => {
|
||||
const coloredString = colorFn(`[${startTimeString}] [${levels.ERROR}] config -`)
|
||||
expect(
|
||||
createColoredContextLayout({
|
||||
withStack: 'error',
|
||||
withFile: 'warn',
|
||||
withLine: true,
|
||||
})(defaultLogEvent),
|
||||
).toBe(`${coloredString} user=1 message \nstack`)
|
||||
})
|
||||
})
|
||||
})
|
||||
115
config-schema/src/log4js-config/coloredContext.ts
Normal file
115
config-schema/src/log4js-config/coloredContext.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import { Level, LoggingEvent } from 'log4js'
|
||||
import colors from 'yoctocolors-cjs'
|
||||
import { ColoredContextLayoutConfig, LogLevel } from './types'
|
||||
import { inspect } from 'node:util'
|
||||
|
||||
function colorize(str: string, level: Level): string {
|
||||
switch (level.colour) {
|
||||
case 'white':
|
||||
return colors.white(str)
|
||||
case 'grey':
|
||||
return colors.gray(str)
|
||||
case 'black':
|
||||
return colors.black(str)
|
||||
case 'blue':
|
||||
return colors.blue(str)
|
||||
case 'cyan':
|
||||
return colors.cyan(str)
|
||||
case 'green':
|
||||
return colors.green(str)
|
||||
case 'magenta':
|
||||
return colors.magenta(str)
|
||||
case 'red':
|
||||
return colors.redBright(str)
|
||||
case 'yellow':
|
||||
return colors.yellow(str)
|
||||
default:
|
||||
return colors.gray(str)
|
||||
}
|
||||
}
|
||||
|
||||
// distinguish between objects with valid toString function (for examples classes derived from AbstractLoggingView) and other objects
|
||||
function composeDataString(data: (string | Object)[]): string {
|
||||
return data
|
||||
.map((d) => {
|
||||
// if it is a object and his toString function return only garbage
|
||||
if (typeof d === 'object' && d.toString() === '[object Object]') {
|
||||
return inspect(d, )
|
||||
}
|
||||
if (d) {
|
||||
return d.toString()
|
||||
}
|
||||
})
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
// automatic detect context objects and list them in logfmt style
|
||||
function composeContextString(data: Object): string {
|
||||
return Object.entries(data)
|
||||
.map(([key, value]) => {
|
||||
return `${key}=${value} `
|
||||
})
|
||||
.join(' ')
|
||||
.trimEnd()
|
||||
}
|
||||
|
||||
// check if option is enabled, either if option is them self a boolean or a valid log level and <= eventLogLevel
|
||||
function isEnabledByLogLevel(eventLogLevel: Level, targetLogLevel?: LogLevel | boolean): boolean {
|
||||
if (!targetLogLevel) {
|
||||
return false
|
||||
}
|
||||
if (typeof targetLogLevel === 'boolean') {
|
||||
return targetLogLevel
|
||||
}
|
||||
return eventLogLevel.isGreaterThanOrEqualTo(targetLogLevel)
|
||||
}
|
||||
|
||||
enum DetailKind {
|
||||
Callstack = 'callstack',
|
||||
File = 'file',
|
||||
Line = 'line',
|
||||
}
|
||||
function resolveDetailKind(
|
||||
logEvent: LoggingEvent,
|
||||
config: ColoredContextLayoutConfig,
|
||||
): DetailKind | undefined {
|
||||
if (logEvent.callStack && isEnabledByLogLevel(logEvent.level, config.withStack)) {
|
||||
return DetailKind.Callstack
|
||||
}
|
||||
if (isEnabledByLogLevel(logEvent.level, config.withFile)) {
|
||||
return DetailKind.File
|
||||
}
|
||||
if (isEnabledByLogLevel(logEvent.level, config.withLine)) {
|
||||
return DetailKind.Line
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function createColoredContextLayout(config: ColoredContextLayoutConfig) {
|
||||
return (logEvent: LoggingEvent) => {
|
||||
const result: string[] = []
|
||||
const detailKind = resolveDetailKind(logEvent, config)
|
||||
let categoryName = logEvent.categoryName
|
||||
if (detailKind === DetailKind.Line) {
|
||||
categoryName += `:${logEvent.lineNumber}`
|
||||
}
|
||||
result.push(
|
||||
colorize(
|
||||
`[${logEvent.startTime.toISOString()}] [${logEvent.level}] ${categoryName} -`,
|
||||
logEvent.level,
|
||||
),
|
||||
)
|
||||
if (Object.keys(logEvent.context).length > 0) {
|
||||
result.push(composeContextString(logEvent.context))
|
||||
}
|
||||
result.push(composeDataString(logEvent.data))
|
||||
|
||||
if (detailKind === DetailKind.File) {
|
||||
result.push(`\n at ${logEvent.fileName}:${logEvent.lineNumber}`)
|
||||
}
|
||||
if (detailKind === DetailKind.Callstack) {
|
||||
result.push(`\n${logEvent.callStack}`)
|
||||
}
|
||||
return result.join(' ')
|
||||
}
|
||||
}
|
||||
26
config-schema/src/log4js-config/index.test.ts
Normal file
26
config-schema/src/log4js-config/index.test.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { createLog4jsConfig, defaultCategory } from '.'
|
||||
|
||||
describe('createLog4jsConfig', () => {
|
||||
it('should create a log4js config', () => {
|
||||
const config = createLog4jsConfig([defaultCategory('test', 'debug')])
|
||||
expect(config).toBeDefined()
|
||||
expect(config.appenders).toBeDefined()
|
||||
expect(config.categories).toBeDefined()
|
||||
expect(config.appenders).toHaveProperty('test')
|
||||
expect(config.categories).toHaveProperty('test')
|
||||
expect(config.appenders.test).toMatchObject({
|
||||
type: 'dateFile',
|
||||
pattern: 'yyyy-MM-dd',
|
||||
compress: true,
|
||||
keepFileExt: true,
|
||||
fileNameSep: '_',
|
||||
numBackups: 30,
|
||||
filename: 'test.log',
|
||||
layout: {
|
||||
type: 'coloredContext',
|
||||
withStack: 'error',
|
||||
withLine: true,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user