From 601e12ef5212eb84cda6e332e5d3fa9052b95346 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 3 Jun 2025 21:29:41 +0200 Subject: [PATCH 01/46] first draft --- bun.lock | 18 ++++++ package.json | 3 +- shared/package.json | 37 +++++++++++ shared/src/index.test.ts | 5 ++ shared/src/index.ts | 0 shared/src/server/LogError.ts | 8 +++ shared/src/server/logger.ts | 21 ++++++ shared/src/user/schema/alias.test.ts | 97 ++++++++++++++++++++++++++++ shared/src/user/schema/alias.ts | 66 +++++++++++++++++++ shared/tsconfig.json | 73 +++++++++++++++++++++ shared/turbo.json | 23 +++++++ 11 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 shared/package.json create mode 100644 shared/src/index.test.ts create mode 100644 shared/src/index.ts create mode 100644 shared/src/server/LogError.ts create mode 100644 shared/src/server/logger.ts create mode 100644 shared/src/user/schema/alias.test.ts create mode 100644 shared/src/user/schema/alias.ts create mode 100644 shared/tsconfig.json create mode 100644 shared/turbo.json diff --git a/bun.lock b/bun.lock index 2e1249299..8c9420dc5 100644 --- a/bun.lock +++ b/bun.lock @@ -370,6 +370,20 @@ "webpack": "^5", }, }, + "shared": { + "name": "shared", + "version": "1.0.0", + "dependencies": { + "database": "*", + "esbuild": "^0.25.2", + "zod": "^3.25.20", + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@types/node": "^17.0.21", + "typescript": "^4.9.5", + }, + }, }, "packages": { "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], @@ -2806,6 +2820,8 @@ "sha.js": ["sha.js@2.4.11", "", { "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" }, "bin": { "sha.js": "./bin.js" } }, "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ=="], + "shared": ["shared@workspace:shared"], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], @@ -3280,6 +3296,8 @@ "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.20", "", {}, "sha512-z03fqpTMDF1G02VLKUMt6vyACE7rNWkh3gpXVHgPTw28NPtDFRGvcpTtPwn2kMKtQ0idtYJUTxchytmnqYswcw=="], + "@apollo/protobufjs/@types/node": ["@types/node@10.17.60", "", {}, "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="], "@apollographql/graphql-upload-8-fork/http-errors": ["http-errors@1.8.1", "", { "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.1" } }, "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g=="], diff --git a/package.json b/package.json index 0d73289a6..7979b9e77 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "database", "dht-node", "federation", - "frontend" + "frontend", + "shared" ], "scripts": { "release": "scripts/release.sh", diff --git a/shared/package.json b/shared/package.json new file mode 100644 index 000000000..f0434567a --- /dev/null +++ b/shared/package.json @@ -0,0 +1,37 @@ +{ + "name": "shared", + "version": "1.0.0", + "description": "Gradido Shared Code", + "main": "./build/index.js", + "types": "./src/index.ts", + "exports": { + ".": { + "import": "./build/index.js", + "require": "./build/index.js" + } + }, + "repository": "https://github.com/gradido/gradido/shared", + "author": "Gradido Academy - https://www.gradido.net", + "license": "Apache-2.0", + "private": true, + "scripts": { + "build": "esbuild src/index.ts --outdir=build --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", + "lint": "biome check --error-on-warnings .", + "lint:fix": "biome check --error-on-warnings . --write" + }, + "devDependencies": { + "@biomejs/biome": "1.9.4", + "@types/node": "^17.0.21", + "typescript": "^4.9.5" + }, + "dependencies": { + "database": "*", + "esbuild": "^0.25.2", + "zod": "^3.25.20" + }, + "engines": { + "node": ">=18" + } +} diff --git a/shared/src/index.test.ts b/shared/src/index.test.ts new file mode 100644 index 000000000..b942d6baf --- /dev/null +++ b/shared/src/index.test.ts @@ -0,0 +1,5 @@ +describe('shared', () => { + it('should be true', () => { + expect(true).toBe(true) + }) +}) \ No newline at end of file diff --git a/shared/src/index.ts b/shared/src/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/shared/src/server/LogError.ts b/shared/src/server/LogError.ts new file mode 100644 index 000000000..346923019 --- /dev/null +++ b/shared/src/server/LogError.ts @@ -0,0 +1,8 @@ +import { backendLogger as logger } from './logger' + +export class LogError extends Error { + constructor(msg: string, ...details: any[]) { + super(msg) + logger.error(msg, ...details) + } +} diff --git a/shared/src/server/logger.ts b/shared/src/server/logger.ts new file mode 100644 index 000000000..24ae79c67 --- /dev/null +++ b/shared/src/server/logger.ts @@ -0,0 +1,21 @@ +import { readFileSync } from 'fs' + +import { configure, getLogger } from 'log4js' + +import { CONFIG } from '@/config' + +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 } diff --git a/shared/src/user/schema/alias.test.ts b/shared/src/user/schema/alias.test.ts new file mode 100644 index 000000000..0bdfb59a6 --- /dev/null +++ b/shared/src/user/schema/alias.test.ts @@ -0,0 +1,97 @@ +import { validateAlias } from './alias' + +describe('validate alias', () => { + beforeAll(() => { + jest.clearAllMocks() + }) + + describe('alias too short', () => { + it('throws and logs an error', async () => { + await expect(validateAlias('Bi')).rejects.toEqual(new Error('Given alias is too short')) + expect(logger.error).toBeCalledWith('Given alias is too short', 'Bi') + }) + }) + + describe('alias too long', () => { + it('throws and logs an error', async () => { + await expect(validateAlias('BibiBloxbergHexHexHex')).rejects.toEqual( + new Error('Given alias is too long'), + ) + expect(logger.error).toBeCalledWith('Given alias is too long', 'BibiBloxbergHexHexHex') + }) + }) + + describe('alias contains invalid characters', () => { + it('throws and logs an error', async () => { + await expect(validateAlias('Bibi.Bloxberg')).rejects.toEqual( + new Error('Invalid characters in alias'), + ) + expect(logger.error).toBeCalledWith('Invalid characters in alias', 'Bibi.Bloxberg') + }) + }) + + describe('alias is a reserved word', () => { + it('throws and logs an error', async () => { + await expect(validateAlias('admin')).rejects.toEqual(new Error('Alias is not allowed')) + expect(logger.error).toBeCalledWith('Alias is not allowed', 'admin') + }) + }) + + describe('alias is a reserved word with uppercase characters', () => { + it('throws and logs an error', async () => { + await expect(validateAlias('Admin')).rejects.toEqual(new Error('Alias is not allowed')) + expect(logger.error).toBeCalledWith('Alias is not allowed', 'Admin') + }) + }) + + describe('hyphens and underscore', () => { + describe('alias starts with underscore', () => { + it('throws and logs an error', async () => { + await expect(validateAlias('_bibi')).rejects.toEqual( + new Error('Invalid characters in alias'), + ) + expect(logger.error).toBeCalledWith('Invalid characters in alias', '_bibi') + }) + }) + + describe('alias contains two following hyphens', () => { + it('throws and logs an error', async () => { + await expect(validateAlias('bi--bi')).rejects.toEqual( + new Error('Invalid characters in alias'), + ) + expect(logger.error).toBeCalledWith('Invalid characters in alias', 'bi--bi') + }) + }) + }) + + describe('test against existing alias in database', () => { + beforeAll(async () => { + const bibi = await userFactory(testEnv, bibiBloxberg) + const user = await User.findOne({ where: { id: bibi.id } }) + if (user) { + user.alias = 'b-b' + await user.save() + } + }) + + describe('alias exists in database', () => { + it('throws and logs an error', async () => { + await expect(validateAlias('b-b')).rejects.toEqual(new Error('Alias already in use')) + expect(logger.error).toBeCalledWith('Alias already in use', 'b-b') + }) + }) + + describe('alias exists in database with in lower-case', () => { + it('throws and logs an error', async () => { + await expect(validateAlias('b-B')).rejects.toEqual(new Error('Alias already in use')) + expect(logger.error).toBeCalledWith('Alias already in use', 'b-B') + }) + }) + + describe('valid alias', () => { + it('resolves to true', async () => { + await expect(validateAlias('bibi')).resolves.toEqual(true) + }) + }) + }) +}) diff --git a/shared/src/user/schema/alias.ts b/shared/src/user/schema/alias.ts new file mode 100644 index 000000000..c273f12ba --- /dev/null +++ b/shared/src/user/schema/alias.ts @@ -0,0 +1,66 @@ +import { z } from 'zod' +import { User as DbUser } from 'database' +import { Raw } from 'typeorm' +// import { LogError } from '@/server/LogError' + +class LogError extends Error { + details: any[] + constructor(msg: string, ...details: any[]) { + super(msg) + this.name = 'LogError' + this.message = msg + this.stack = new Error().stack + this.details = details + } +} + +export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/ + +const RESERVED_ALIAS = [ + 'admin', + 'email', + 'gast', + 'gdd', + 'gradido', + 'guest', + 'home', + 'root', + 'support', + 'temp', + 'tmp', + 'tmp', + 'user', + 'usr', + 'var', +] + +export const aliasSchema = z + .string() + .min(3, 'Alias is too short') + .max(20, 'Alias is too long') + .regex(VALID_ALIAS_REGEX, 'Invalid characters in alias') + .refine((val) => !RESERVED_ALIAS.includes(val.toLowerCase()), { + message: 'Alias is not allowed', + }) + +export const validateAlias = async (alias: string): Promise => { + try { + aliasSchema.parse(alias) + } catch (err) { + if (err instanceof z.ZodError) { + console.log(err) + throw new LogError(err.errors[0].message, alias) + } + throw err + } + + const aliasInUse = await DbUser.find({ + where: { alias: Raw((a) => `LOWER(${a}) = "${alias.toLowerCase()}"`) }, + }) + + if (aliasInUse.length !== 0) { + throw new LogError('Alias already in use', alias) + } + + return true +} diff --git a/shared/tsconfig.json b/shared/tsconfig.json new file mode 100644 index 000000000..04306edad --- /dev/null +++ b/shared/tsconfig.json @@ -0,0 +1,73 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "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. */ + // "outFile": "./build/outfile.js", /* Concatenate and emit output to single file. */ + "outDir": "./build", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "references": [] /* Any project that is referenced must itself have a `references` array (which may be empty). */ +} diff --git a/shared/turbo.json b/shared/turbo.json new file mode 100644 index 000000000..2d56da524 --- /dev/null +++ b/shared/turbo.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "lint": { + }, + "lint:fix": { + }, + "test": { + }, + "typecheck": { + }, + "dev": { + "dependsOn": ["database#build"], + "persistent": true, + "cache": false + }, + "build": { + "dependsOn": ["^build"], + "outputs": ["build/**"], + "cache": true + } + } +} \ No newline at end of file From 646959700827ee7a3ca67a8d0e7ad5b627658446 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 11 Jun 2025 12:11:07 +0200 Subject: [PATCH 02/46] extend log4js config generator, add own layouts --- bun.lock | 10 +++ config-schema/package.json | 6 +- config-schema/src/commonSchema.ts | 7 ++ config-schema/src/index.ts | 2 + config-schema/src/log4js-config/appenders.ts | 77 ++++++++++++++++++ .../src/log4js-config/coloredContext.ts | 76 ++++++++++++++++++ config-schema/src/log4js-config/index.ts | 79 +++++++++++++++++++ .../src/log4js-config/types/Category.ts | 28 +++++++ .../log4js-config/types/CustomFileAppender.ts | 31 ++++++++ .../src/log4js-config/types/LogLevel.ts | 15 ++++ .../src/log4js-config/types/index.ts | 3 + yarn.lock | 7 +- 12 files changed, 338 insertions(+), 3 deletions(-) create mode 100644 config-schema/src/log4js-config/appenders.ts create mode 100644 config-schema/src/log4js-config/coloredContext.ts create mode 100644 config-schema/src/log4js-config/index.ts create mode 100644 config-schema/src/log4js-config/types/Category.ts create mode 100644 config-schema/src/log4js-config/types/CustomFileAppender.ts create mode 100644 config-schema/src/log4js-config/types/LogLevel.ts create mode 100644 config-schema/src/log4js-config/types/index.ts diff --git a/bun.lock b/bun.lock index 45509db09..877b643f9 100644 --- a/bun.lock +++ b/bun.lock @@ -161,6 +161,8 @@ "dependencies": { "esbuild": "^0.25.2", "joi": "^17.13.3", + "source-map-support": "^0.5.21", + "yoctocolors-cjs": "^2.1.2", }, "devDependencies": { "@biomejs/biome": "1.9.4", @@ -182,6 +184,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", @@ -207,6 +210,9 @@ "devDependencies": { "@biomejs/biome": "1.9.4", "@hyperswarm/dht": "6.5.1", + "@swc/cli": "^0.7.3", + "@swc/core": "^1.11.24", + "@swc/helpers": "^0.5.17", "@types/dotenv": "^8.2.3", "@types/jest": "27.5.1", "@types/joi": "^17.2.3", @@ -219,7 +225,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", @@ -3286,6 +3294,8 @@ "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=="], diff --git a/config-schema/package.json b/config-schema/package.json index 097cb329e..1ec4ed1f1 100644 --- a/config-schema/package.json +++ b/config-schema/package.json @@ -15,7 +15,7 @@ "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", "lint": "biome check --error-on-warnings .", @@ -28,7 +28,9 @@ }, "dependencies": { "esbuild": "^0.25.2", - "joi": "^17.13.3" + "joi": "^17.13.3", + "source-map-support": "^0.5.21", + "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" diff --git a/config-schema/src/commonSchema.ts b/config-schema/src/commonSchema.ts index cd5716392..3f7b0a13e 100644 --- a/config-schema/src/commonSchema.ts +++ b/config-schema/src/commonSchema.ts @@ -131,6 +131,13 @@ export const LOG4JS_CONFIG = Joi.string() .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') diff --git a/config-schema/src/index.ts b/config-schema/src/index.ts index f9e136a34..e38fa190f 100644 --- a/config-schema/src/index.ts +++ b/config-schema/src/index.ts @@ -1,3 +1,5 @@ +import 'source-map-support/register' export * from './commonSchema' export { DatabaseConfigSchema } from './DatabaseConfigSchema' export { validate } from './validate' +export { createLog4jsConfig, type Category, initLogger, defaultCategory } from './log4js-config' \ No newline at end of file diff --git a/config-schema/src/log4js-config/appenders.ts b/config-schema/src/log4js-config/appenders.ts new file mode 100644 index 000000000..5c9c93f30 --- /dev/null +++ b/config-schema/src/log4js-config/appenders.ts @@ -0,0 +1,77 @@ +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} + * the appender configuration as a map + */ +export function createAppenderConfig( + fileAppenders: CustomFileAppender[], + basePath?: string, +): { [name: string]: Appender } { + if (basePath) { + defaultAppenders.errorFile.filename = `${basePath}/errors` + } + 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', + withStack: appender.withStack, + withFile: appender.withFile, + } + customAppender[appender.name] = dateFile + }) + return customAppender +} diff --git a/config-schema/src/log4js-config/coloredContext.ts b/config-schema/src/log4js-config/coloredContext.ts new file mode 100644 index 000000000..18243a3f8 --- /dev/null +++ b/config-schema/src/log4js-config/coloredContext.ts @@ -0,0 +1,76 @@ +import { LoggingEvent, Level } from 'log4js' +import colors from 'yoctocolors-cjs' +import { LogLevel } from './types' + +export type coloredContextLayoutConfig = { + withStack?: LogLevel | boolean + withFile?: LogLevel | boolean +} + + +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((data) => { + // if it is a object and his toString function return only garbage + if (typeof data === 'object' && data.toString() === '[object Object]') { + return JSON.stringify(data) + } + return data.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) +} + +export function createColoredContextLayout(config: coloredContextLayoutConfig) { + return (logEvent: LoggingEvent) => { + const result: string[] = [] + result.push(colorize( + `[${logEvent.startTime.toISOString()}] [${logEvent.level}] ${logEvent.categoryName} -`, + logEvent.level + )) + if (Object.keys(logEvent.context).length > 0) { + result.push(composeContextString(logEvent.context)) + } + result.push(composeDataString(logEvent.data)) + const showCallstack = logEvent.callStack && isEnabledByLogLevel(logEvent.level, config.withStack) + if (!showCallstack && isEnabledByLogLevel(logEvent.level, config.withFile)) { + result.push(`\n at ${logEvent.fileName}:${logEvent.lineNumber}`) + } + if (showCallstack) { + result.push(`\n${logEvent.callStack}`) + } + return result.join(' ') + } +} + \ No newline at end of file diff --git a/config-schema/src/log4js-config/index.ts b/config-schema/src/log4js-config/index.ts new file mode 100644 index 000000000..aef4d6099 --- /dev/null +++ b/config-schema/src/log4js-config/index.ts @@ -0,0 +1,79 @@ +import { readFileSync, writeFileSync } from 'node:fs' +import { addLayout, Configuration, LoggingEvent, configure } from 'log4js' +import { createAppenderConfig } from './appenders' +import { Category, CustomFileAppender, LogLevel, defaultCategory } from './types' +import { createColoredContextLayout } from './coloredContext' + +export { Category, LogLevel, defaultCategory } + +/** + * Creates the log4js configuration. + * + * @param {Category[]} categories - the categories to add to the configuration + * @param {string} [basePath] - the base path for log files + * @returns {Configuration} the log4js configuration + */ + +addLayout("json", function() { + return function (logEvent: LoggingEvent) { + return JSON.stringify(logEvent) + } +}) + +addLayout("coloredContext", createColoredContextLayout) + +export function createLog4jsConfig(categories: Category[], basePath?: string): Configuration { + const customFileAppenders: CustomFileAppender[] = [] + const result: Configuration = { + appenders: {}, + categories: {}, + } + + categories.forEach((category: Category) => { + customFileAppenders.push({ + name: category.name, + filename: category.filename, + withFile: true, + withStack: 'error', + }) + // needed by log4js, show all error message accidentally without (proper) Category + result.categories['default'] = { + level: 'debug', + appenders: [ + 'out', + 'errors', + ], + enableCallStack: true, + } + const appenders = [category.name, 'out'] + if (category.additionalErrorsFile) { + appenders.push('errors') + } + result.categories[category.name] = { + level: category.level, + appenders, + enableCallStack: true, + } + }) + + result.appenders = createAppenderConfig(customFileAppenders, basePath) + return result +} + +/** + * Initializes the logger. + * + * @param {Category[]} categories - the categories to add to the configuration + * @param {string} logFilesPath - the base path for log files + * @param {string} [log4jsConfigFileName] - the name of the log4js config file + */ +export function initLogger(categories: Category[], logFilesPath: string, log4jsConfigFileName: string = 'log4js-config.json'): void { + // if not log4js config file exists, create a default one + try { + configure(JSON.parse(readFileSync(log4jsConfigFileName, 'utf-8'))) + } catch(_e) { + const options = createLog4jsConfig(categories, logFilesPath) + writeFileSync(log4jsConfigFileName, JSON.stringify(options, null, 2), {encoding: 'utf-8'}) + configure(options) + } +} diff --git a/config-schema/src/log4js-config/types/Category.ts b/config-schema/src/log4js-config/types/Category.ts new file mode 100644 index 000000000..3c00c5077 --- /dev/null +++ b/config-schema/src/log4js-config/types/Category.ts @@ -0,0 +1,28 @@ +import { LogLevel } from './LogLevel' + +/** + * Configuration for a log4js category. + * + * @property {string} name - The name of the category. + * @property {string} [filename] - The filename for the category, use name if not set. + * @property {boolean} [stdout] - Whether to log to stdout. + * @property {boolean} [additionalErrorsFile] - Whether to log errors additional to the default error file. + * @property {LogLevel} level - The logging level. + */ +export type Category = { + name: string + filename?: string + stdout?: boolean + additionalErrorsFile?: boolean + level: LogLevel +} + +export function defaultCategory(name: string, level: LogLevel): Category { + return { + name, + level, + stdout: true, + additionalErrorsFile: true, + } +} + \ No newline at end of file diff --git a/config-schema/src/log4js-config/types/CustomFileAppender.ts b/config-schema/src/log4js-config/types/CustomFileAppender.ts new file mode 100644 index 000000000..5aafc13b0 --- /dev/null +++ b/config-schema/src/log4js-config/types/CustomFileAppender.ts @@ -0,0 +1,31 @@ +import { LogLevel } from './LogLevel' +/** + * use default dateFile Template for custom file appenders + * + * @example use name for key and filename, add .log to name + * ``` + * const appenderConfig = createAppenderConfig([ + * { name: 'info' }, + * ]) + * ``` + * + * @example if log file should contain the stacktrace + * ``` + * const appenderConfig = createAppenderConfig([ + * { name: 'warn', filename: 'warn.log', withStack: true }, + * ]) + * ``` + * + * @example if log file should contain the stacktrace only from log level debug and higher + * ``` + * const appenderConfig = createAppenderConfig([ + * { name: 'warn', filename: 'warn.log', withStack: 'debug' }, + * ]) + * ``` + */ +export type CustomFileAppender = { + name: string + filename?: string + withStack?: LogLevel | boolean // with stack if boolean or from log level on or above + withFile?: LogLevel | boolean // with filename and line if boolean or from log level on or above +} diff --git a/config-schema/src/log4js-config/types/LogLevel.ts b/config-schema/src/log4js-config/types/LogLevel.ts new file mode 100644 index 000000000..d669c0c96 --- /dev/null +++ b/config-schema/src/log4js-config/types/LogLevel.ts @@ -0,0 +1,15 @@ +import { z } from 'zod' + +export const LOG_LEVEL = z.enum([ + 'all', + 'mark', + 'trace', + 'debug', + 'info', + 'warn', + 'error', + 'fatal', + 'off', +]) + +export type LogLevel = z.infer diff --git a/config-schema/src/log4js-config/types/index.ts b/config-schema/src/log4js-config/types/index.ts new file mode 100644 index 000000000..e79261487 --- /dev/null +++ b/config-schema/src/log4js-config/types/index.ts @@ -0,0 +1,3 @@ +export * from './Category' +export * from './CustomFileAppender' +export * from './LogLevel' diff --git a/yarn.lock b/yarn.lock index f93aec1bb..08ce15dbe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10594,7 +10594,7 @@ sort-keys@^1.0.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== -source-map-support@^0.5.6, source-map-support@~0.5.20: +source-map-support@^0.5.21, source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -12660,6 +12660,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +yoctocolors-cjs@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242" + integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA== + yup@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/yup/-/yup-1.6.1.tgz#8defcff9daaf9feac178029c0e13b616563ada4b" From 5a8376cccf88023a6db1dfa32d917339e8884cd1 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 11 Jun 2025 12:12:23 +0200 Subject: [PATCH 03/46] use sourcemap for correct filename and line number in logging --- database/esbuild.config.ts | 1 + database/package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/database/esbuild.config.ts b/database/esbuild.config.ts index 98cb7543f..078228f7d 100644 --- a/database/esbuild.config.ts +++ b/database/esbuild.config.ts @@ -9,6 +9,7 @@ build({ platform: 'node', packages: 'external', outdir: './build', + sourcemap: true, plugins: [ { // hardcode last db version string into index.ts, before parsing diff --git a/database/package.json b/database/package.json index fd146fbc5..6aa4002c7 100644 --- a/database/package.json +++ b/database/package.json @@ -45,6 +45,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", From c315d841bd1fdbbfd31c8d8a99f99cbd603557f6 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 11 Jun 2025 12:17:37 +0200 Subject: [PATCH 04/46] fix linting --- bun.lock | 3 +- config-schema/package.json | 3 +- config-schema/src/index.ts | 2 +- config-schema/src/log4js-config/appenders.ts | 4 +- .../src/log4js-config/coloredContext.ts | 80 +++++++++++-------- config-schema/src/log4js-config/index.ts | 29 +++---- .../src/log4js-config/types/Category.ts | 1 - .../log4js-config/types/CustomFileAppender.ts | 2 +- yarn.lock | 5 ++ 9 files changed, 76 insertions(+), 53 deletions(-) diff --git a/bun.lock b/bun.lock index 877b643f9..24180713e 100644 --- a/bun.lock +++ b/bun.lock @@ -163,6 +163,7 @@ "joi": "^17.13.3", "source-map-support": "^0.5.21", "yoctocolors-cjs": "^2.1.2", + "zod": "^3.25.61", }, "devDependencies": { "@biomejs/biome": "1.9.4", @@ -3302,7 +3303,7 @@ "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=="], diff --git a/config-schema/package.json b/config-schema/package.json index 1ec4ed1f1..3759d9373 100644 --- a/config-schema/package.json +++ b/config-schema/package.json @@ -30,7 +30,8 @@ "esbuild": "^0.25.2", "joi": "^17.13.3", "source-map-support": "^0.5.21", - "yoctocolors-cjs": "^2.1.2" + "yoctocolors-cjs": "^2.1.2", + "zod": "^3.25.61" }, "engines": { "node": ">=18" diff --git a/config-schema/src/index.ts b/config-schema/src/index.ts index e38fa190f..c2ad9fc96 100644 --- a/config-schema/src/index.ts +++ b/config-schema/src/index.ts @@ -2,4 +2,4 @@ import 'source-map-support/register' export * from './commonSchema' export { DatabaseConfigSchema } from './DatabaseConfigSchema' export { validate } from './validate' -export { createLog4jsConfig, type Category, initLogger, defaultCategory } from './log4js-config' \ No newline at end of file +export { createLog4jsConfig, type Category, initLogger, defaultCategory } from './log4js-config' diff --git a/config-schema/src/log4js-config/appenders.ts b/config-schema/src/log4js-config/appenders.ts index 5c9c93f30..59b42aec3 100644 --- a/config-schema/src/log4js-config/appenders.ts +++ b/config-schema/src/log4js-config/appenders.ts @@ -66,8 +66,8 @@ export function createAppenderConfig( ...fileAppenderTemplate, filename: basePath ? `${basePath}/${filename}` : filename, } - dateFile.layout = { - type: 'coloredContext', + dateFile.layout = { + type: 'coloredContext', withStack: appender.withStack, withFile: appender.withFile, } diff --git a/config-schema/src/log4js-config/coloredContext.ts b/config-schema/src/log4js-config/coloredContext.ts index 18243a3f8..a221c7c9d 100644 --- a/config-schema/src/log4js-config/coloredContext.ts +++ b/config-schema/src/log4js-config/coloredContext.ts @@ -1,5 +1,5 @@ -import { LoggingEvent, Level } from 'log4js' -import colors from 'yoctocolors-cjs' +import { Level, LoggingEvent } from 'log4js' +import colors from 'yoctocolors-cjs' import { LogLevel } from './types' export type coloredContextLayoutConfig = { @@ -7,38 +7,52 @@ export type coloredContextLayoutConfig = { withFile?: LogLevel | boolean } - 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) + 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((data) => { - // if it is a object and his toString function return only garbage - if (typeof data === 'object' && data.toString() === '[object Object]') { - return JSON.stringify(data) - } - return data.toString() - }).join(' ') + return data + .map((data) => { + // if it is a object and his toString function return only garbage + if (typeof data === 'object' && data.toString() === '[object Object]') { + return JSON.stringify(data) + } + return data.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() + 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 @@ -55,15 +69,18 @@ function isEnabledByLogLevel(eventLogLevel: Level, targetLogLevel?: LogLevel | b export function createColoredContextLayout(config: coloredContextLayoutConfig) { return (logEvent: LoggingEvent) => { const result: string[] = [] - result.push(colorize( - `[${logEvent.startTime.toISOString()}] [${logEvent.level}] ${logEvent.categoryName} -`, - logEvent.level - )) + result.push( + colorize( + `[${logEvent.startTime.toISOString()}] [${logEvent.level}] ${logEvent.categoryName} -`, + logEvent.level, + ), + ) if (Object.keys(logEvent.context).length > 0) { result.push(composeContextString(logEvent.context)) - } + } result.push(composeDataString(logEvent.data)) - const showCallstack = logEvent.callStack && isEnabledByLogLevel(logEvent.level, config.withStack) + const showCallstack = + logEvent.callStack && isEnabledByLogLevel(logEvent.level, config.withStack) if (!showCallstack && isEnabledByLogLevel(logEvent.level, config.withFile)) { result.push(`\n at ${logEvent.fileName}:${logEvent.lineNumber}`) } @@ -71,6 +88,5 @@ export function createColoredContextLayout(config: coloredContextLayoutConfig) { result.push(`\n${logEvent.callStack}`) } return result.join(' ') - } + } } - \ No newline at end of file diff --git a/config-schema/src/log4js-config/index.ts b/config-schema/src/log4js-config/index.ts index aef4d6099..9652726ce 100644 --- a/config-schema/src/log4js-config/index.ts +++ b/config-schema/src/log4js-config/index.ts @@ -1,8 +1,8 @@ import { readFileSync, writeFileSync } from 'node:fs' -import { addLayout, Configuration, LoggingEvent, configure } from 'log4js' +import { Configuration, LoggingEvent, addLayout, configure } from 'log4js' import { createAppenderConfig } from './appenders' -import { Category, CustomFileAppender, LogLevel, defaultCategory } from './types' import { createColoredContextLayout } from './coloredContext' +import { Category, CustomFileAppender, LogLevel, defaultCategory } from './types' export { Category, LogLevel, defaultCategory } @@ -14,13 +14,13 @@ export { Category, LogLevel, defaultCategory } * @returns {Configuration} the log4js configuration */ -addLayout("json", function() { +addLayout('json', function () { return function (logEvent: LoggingEvent) { return JSON.stringify(logEvent) } }) -addLayout("coloredContext", createColoredContextLayout) +addLayout('coloredContext', createColoredContextLayout) export function createLog4jsConfig(categories: Category[], basePath?: string): Configuration { const customFileAppenders: CustomFileAppender[] = [] @@ -37,12 +37,9 @@ export function createLog4jsConfig(categories: Category[], basePath?: string): C withStack: 'error', }) // needed by log4js, show all error message accidentally without (proper) Category - result.categories['default'] = { + result.categories.default = { level: 'debug', - appenders: [ - 'out', - 'errors', - ], + appenders: ['out', 'errors'], enableCallStack: true, } const appenders = [category.name, 'out'] @@ -67,13 +64,17 @@ export function createLog4jsConfig(categories: Category[], basePath?: string): C * @param {string} logFilesPath - the base path for log files * @param {string} [log4jsConfigFileName] - the name of the log4js config file */ -export function initLogger(categories: Category[], logFilesPath: string, log4jsConfigFileName: string = 'log4js-config.json'): void { - // if not log4js config file exists, create a default one - try { +export function initLogger( + categories: Category[], + logFilesPath: string, + log4jsConfigFileName: string = 'log4js-config.json', +): void { + // if not log4js config file exists, create a default one + try { configure(JSON.parse(readFileSync(log4jsConfigFileName, 'utf-8'))) - } catch(_e) { + } catch (_e) { const options = createLog4jsConfig(categories, logFilesPath) - writeFileSync(log4jsConfigFileName, JSON.stringify(options, null, 2), {encoding: 'utf-8'}) + writeFileSync(log4jsConfigFileName, JSON.stringify(options, null, 2), { encoding: 'utf-8' }) configure(options) } } diff --git a/config-schema/src/log4js-config/types/Category.ts b/config-schema/src/log4js-config/types/Category.ts index 3c00c5077..889401b30 100644 --- a/config-schema/src/log4js-config/types/Category.ts +++ b/config-schema/src/log4js-config/types/Category.ts @@ -25,4 +25,3 @@ export function defaultCategory(name: string, level: LogLevel): Category { additionalErrorsFile: true, } } - \ No newline at end of file diff --git a/config-schema/src/log4js-config/types/CustomFileAppender.ts b/config-schema/src/log4js-config/types/CustomFileAppender.ts index 5aafc13b0..f2281805e 100644 --- a/config-schema/src/log4js-config/types/CustomFileAppender.ts +++ b/config-schema/src/log4js-config/types/CustomFileAppender.ts @@ -15,7 +15,7 @@ import { LogLevel } from './LogLevel' * { name: 'warn', filename: 'warn.log', withStack: true }, * ]) * ``` - * + * * @example if log file should contain the stacktrace only from log level debug and higher * ``` * const appenderConfig = createAppenderConfig([ diff --git a/yarn.lock b/yarn.lock index 08ce15dbe..235de4b00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12694,3 +12694,8 @@ zen-observable@0.8.15, zen-observable@^0.8.0: version "0.8.15" resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== + +zod@^3.25.61: + version "3.25.61" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.61.tgz#2e09ece796c182d44b7defc8efb27aa792eb4c8b" + integrity sha512-fzfJgUw78LTNnHujj9re1Ov/JJQkRZZGDMcYqSx7Hp4rPOkKywaFHq0S6GoHeXs0wGNE/sIOutkXgnwzrVOGCQ== From a9651d6940738d30d31fbbdc7345d8bf32d2995b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 15 Jun 2025 08:37:02 +0200 Subject: [PATCH 05/46] move logger testSetup into config-schema, capture logs in test, add debugging config --- .vscode/launch.json | 18 +++++++- bun.lock | 13 ++++++ config-schema/biome.json | 2 +- config-schema/test/testSetup.ts | 59 ++++++++++++++++++++++++ dht-node/esbuild.config.ts | 2 +- dht-node/jest.config.js | 9 ++-- dht-node/log4js-config.json | 71 ----------------------------- dht-node/package.json | 13 +++++- dht-node/src/config/const.ts | 1 + dht-node/src/config/index.ts | 11 +++-- dht-node/src/config/schema.ts | 2 + dht-node/src/dht_node/index.test.ts | 13 +++++- dht-node/src/dht_node/index.ts | 7 ++- dht-node/src/index.ts | 12 ++++- dht-node/src/server/logger.ts | 21 --------- dht-node/test/helpers.ts | 1 - dht-node/test/testSetup.ts | 22 --------- dht-node/tsconfig.json | 5 +- dht-node/turbo.json | 4 +- yarn.lock | 21 +++++++++ 20 files changed, 172 insertions(+), 135 deletions(-) create mode 100644 config-schema/test/testSetup.ts delete mode 100644 dht-node/log4js-config.json create mode 100644 dht-node/src/config/const.ts delete mode 100644 dht-node/src/server/logger.ts delete mode 100644 dht-node/test/testSetup.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index faf3cff12..9a7177fa2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,23 @@ "request": "launch", "name": "Launch Chrome", "url": "http://localhost:3000", - "webRoot": "${workspaceFolder}/frontend", + "webRoot": "${workspaceFolder}/frontend" + }, + { + "type": "node", + "request": "launch", + "name": "DHT-Node Debug Tests", + "runtimeExecutable": "yarn", + "runtimeArgs": [ + "run", + "test:debug" + ], + "skipFiles": [ + "/**" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "cwd": "${workspaceFolder}/dht-node" } ] } \ No newline at end of file diff --git a/bun.lock b/bun.lock index 24180713e..4711384ee 100644 --- a/bun.lock +++ b/bun.lock @@ -214,6 +214,7 @@ "@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", @@ -635,6 +636,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=="], @@ -879,6 +882,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=="], @@ -2237,6 +2242,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=="], @@ -3371,6 +3378,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=="], @@ -3921,6 +3930,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=="], diff --git a/config-schema/biome.json b/config-schema/biome.json index 52b921260..5b6f222fc 100644 --- a/config-schema/biome.json +++ b/config-schema/biome.json @@ -3,7 +3,7 @@ "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, "files": { "ignoreUnknown": false, - "ignore": ["build", "node_modules"], + "ignore": ["build", "node_modules", "testSetup.ts"], "include": ["./src/**/*.js", "./src/**/*.ts"] }, "formatter": { diff --git a/config-schema/test/testSetup.ts b/config-schema/test/testSetup.ts new file mode 100644 index 000000000..1d1b63f23 --- /dev/null +++ b/config-schema/test/testSetup.ts @@ -0,0 +1,59 @@ +jest.setTimeout(1000000) + +const loggers: { [key: string]: any } = {} +const logs: { level: string; message: string; logger: string; additional: string }[] = [] + +function addLog(level: string, message: string, logger: string, additional: any[]) { + logs.push({ level, message, logger, additional: JSON.stringify(additional, null, 2) }) +} + +export function printLogs() { + for (const log of logs) { + process.stdout.write(`${log.logger} [${log.level}] ${log.message} ${log.additional}\n`) + } +} + +export function cleanLogs(): void { + logs.length = 0 +} + +jest.mock('log4js', () => { + const originalModule = jest.requireActual('log4js') + return { + __esModule: true, + ...originalModule, + getLogger: jest.fn().mockImplementation((param: any) => { + // console.log('getLogger called with: ', param) + const fakeLogger = { + addContext: jest.fn(), + trace: jest.fn((message: string, ...args: any[]) => { + addLog('trace', message, param, args) + }), + debug: jest.fn((message: string, ...args: any[]) => { + addLog('debug', message, param, args) + }), + warn: jest.fn((message: string, ...args: any[]) => { + addLog('warn', message, param, args) + }), + info: jest.fn((message: string, ...args: any[]) => { + addLog('info', message, param, args) + }), + error: jest.fn((message: string, ...args: any[]) => { + addLog('error', message, param, args) + }), + fatal: jest.fn((message: string, ...args: any[]) => { + addLog('fatal', message, param, args) + }), + } + loggers[param] = fakeLogger + return fakeLogger + }), + } +}) + +export function getLogger(name: string) { + if (!loggers[name]) { + throw new Error(`No logger with name ${name} was requested from code`) + } + return loggers[name] +} diff --git a/dht-node/esbuild.config.ts b/dht-node/esbuild.config.ts index f38039c43..42d9c329d 100644 --- a/dht-node/esbuild.config.ts +++ b/dht-node/esbuild.config.ts @@ -10,5 +10,5 @@ build({ // legalComments: 'inline', external: ['dht-rpc', 'sodium-universal'], minify: true, - sourcemap: false, + sourcemap: true, }) diff --git a/dht-node/jest.config.js b/dht-node/jest.config.js index 18170ac48..bca83b5ce 100644 --- a/dht-node/jest.config.js +++ b/dht-node/jest.config.js @@ -1,19 +1,22 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { - verbose: true, + verbose: false, preset: 'ts-jest', - collectCoverage: true, + collectCoverage: false, collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { lines: 82, }, }, - setupFiles: ['/test/testSetup.ts'], + setupFiles: ['config-schema/test/testSetup.ts'], setupFilesAfterEnv: [], modulePathIgnorePatterns: ['/build/'], moduleNameMapper: { '@/(.*)': '/src/$1', '@test/(.*)': '/test/$1', }, + transform: { + '^.+\\.(t|j)sx?$': '@swc/jest', + }, } diff --git a/dht-node/log4js-config.json b/dht-node/log4js-config.json deleted file mode 100644 index ee5207550..000000000 --- a/dht-node/log4js-config.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "appenders": - { - "dht": - { - "type": "dateFile", - "filename": "../logs/dht-node/apiversion-%v.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/dht-node/errors.log", - "pattern": "yyyy-MM-dd", - "layout": - { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" - }, - "compress": true, - "keepFileExt" : true, - "fileNameSep" : "_", - "numBackups" : 30 - }, - "errors": - { - "type": "logLevelFilter", - "level": "error", - "appender": "errorFile" - }, - "out": - { - "type": "stdout", - "layout": - { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" - } - } - }, - "categories": - { - "default": - { - "appenders": - [ - "out", - "errors" - ], - "level": "debug", - "enableCallStack": true - }, - "dht": - { - "appenders": - [ - "dht", - "out", - "errors" - ], - "level": "debug", - "enableCallStack": true - } - } -} diff --git a/dht-node/package.json b/dht-node/package.json index 5a9d6a8e8..1f4f7c7df 100644 --- a/dht-node/package.json +++ b/dht-node/package.json @@ -10,11 +10,14 @@ "scripts": { "build": "tsx esbuild.config.ts", "start": "cross-env TZ=UTC NODE_ENV=production node build/index.js", - "dev": "cross-env TZ=UTC tsx watch src/index.ts", + "dev": "cross-env TZ=UTC nodemon -w src --ext ts,json -r tsconfig-paths/register src/index.ts", + "devFast": "cross-env TZ=UTC tsx src/index.ts", "typecheck": "tsc --noEmit", "lint": "biome check --error-on-warnings .", "lint:fix": "biome check --error-on-warnings . --write", - "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_dht jest --runInBand --forceExit --detectOpenHandles" + "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_dht jest --verbose --runInBand --forceExit --detectOpenHandles", + "test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_dht node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles", + "test:coverage": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_dht jest --coverage --runInBand --forceExit --detectOpenHandles" }, "dependencies": { "cross-env": "^7.0.3", @@ -24,6 +27,10 @@ "devDependencies": { "@biomejs/biome": "1.9.4", "@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", @@ -36,7 +43,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", diff --git a/dht-node/src/config/const.ts b/dht-node/src/config/const.ts new file mode 100644 index 000000000..47350c075 --- /dev/null +++ b/dht-node/src/config/const.ts @@ -0,0 +1 @@ +export const LOG4JS_BASE_CATEGORY_NAME = 'dht' diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index 018f074aa..edcb8a04f 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -1,14 +1,17 @@ import { validate } from 'config-schema' import dotenv from 'dotenv' +import { LogLevel } from 'config-schema/src/log4js-config' import { schema } from './schema' dotenv.config() -const constants = { - 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: process.env.LOG_LEVEL ?? 'info', + // log level for default log4js-config.json, don't change existing log4js-config.json + LOG_LEVEL: (process.env.LOG_LEVEL ?? 'info') as LogLevel, + LOG_FILES_BASE_PATH: process.env.LOG_FILES_BASE_PATH ?? '../logs/dht-node', } const server = { @@ -33,7 +36,7 @@ const federation = { } export const CONFIG = { - ...constants, + ...logging, ...server, ...community, ...federation, diff --git a/dht-node/src/config/schema.ts b/dht-node/src/config/schema.ts index b65525ac7..a205a51f3 100644 --- a/dht-node/src/config/schema.ts +++ b/dht-node/src/config/schema.ts @@ -2,6 +2,7 @@ import { COMMUNITY_DESCRIPTION, COMMUNITY_NAME, LOG4JS_CONFIG, + LOG_FILES_BASE_PATH, LOG_LEVEL, NODE_ENV, PRODUCTION, @@ -12,6 +13,7 @@ export const schema = Joi.object({ COMMUNITY_NAME, COMMUNITY_DESCRIPTION, LOG4JS_CONFIG, + LOG_FILES_BASE_PATH, LOG_LEVEL, NODE_ENV, PRODUCTION, diff --git a/dht-node/src/dht_node/index.test.ts b/dht-node/src/dht_node/index.test.ts index 5ffc3cb5f..9953bee9b 100644 --- a/dht-node/src/dht_node/index.test.ts +++ b/dht-node/src/dht_node/index.test.ts @@ -3,11 +3,11 @@ import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } import { validate as validateUUID, version as versionUUID } from 'uuid' import { cleanDB, testEnvironment } from '@test/helpers' -import { logger } from '@test/testSetup' +import { cleanLogs, getLogger, printLogs } from 'config-schema/test/testSetup' import { CONFIG } from '@/config' -import { startDHT } from './index' +import { LOG_CATEGORY_DHT_NODE, startDHT } from './index' CONFIG.FEDERATION_DHT_SEED = '64ebcb0e3ad547848fef4197c6e2332f' CONFIG.FEDERATION_COMMUNITY_APIS = '1_0,1_1,2_0' @@ -21,6 +21,8 @@ const keyPairMock = { secretKey: Buffer.from('secretKey'), } +const logger = getLogger(LOG_CATEGORY_DHT_NODE) + const serverListenSpy = jest.fn() const serverEventMocks: { [key: string]: any } = {} @@ -112,6 +114,13 @@ describe('federation', () => { jest.useFakeTimers() }) + afterEach(() => { + // print logs which where captured during test + // printLogs() + // clean logs after, else they will be printed in next test again + // cleanLogs() + }) + describe('call startDHT', () => { const hashSpy = jest.spyOn(DHT, 'hash') const keyPairSpy = jest.spyOn(DHT, 'keyPair') diff --git a/dht-node/src/dht_node/index.ts b/dht-node/src/dht_node/index.ts index 69cf86681..3c88bc1c8 100644 --- a/dht-node/src/dht_node/index.ts +++ b/dht-node/src/dht_node/index.ts @@ -7,8 +7,9 @@ import { import { v4 as uuidv4 } from 'uuid' import { CONFIG } from '@/config' -import { logger } from '@/server/logger' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' +import { getLogger } from 'log4js' import { ApiVersionType } from './ApiVersionType' const KEY_SECRET_SEEDBYTES = 32 @@ -17,6 +18,8 @@ const POLLTIME = 20000 const SUCCESSTIME = 120000 const ERRORTIME = 240000 const ANNOUNCETIME = 30000 +export const LOG_CATEGORY_DHT_NODE = `${LOG4JS_BASE_CATEGORY_NAME}.dht_node` +const logger = getLogger(LOG_CATEGORY_DHT_NODE) type CommunityApi = { api: string @@ -56,6 +59,7 @@ export const startDHT = async (topic: string): Promise => { const server = node.createServer() server.on('connection', function (socket: any) { + logger.addContext('pubkey', socket.remotePublicKey.toString('hex')) logger.info(`server on... with Remote public key: ${socket.remotePublicKey.toString('hex')}`) socket.on('data', async (data: Buffer) => { @@ -82,7 +86,6 @@ export const startDHT = async (topic: string): Promise => { ) return } - for (const recApiVersion of recApiVersions) { if ( !recApiVersion.api || diff --git a/dht-node/src/index.ts b/dht-node/src/index.ts index ec2d30554..36c866eaa 100644 --- a/dht-node/src/index.ts +++ b/dht-node/src/index.ts @@ -1,10 +1,20 @@ +import 'source-map-support/register' import { startDHT } from '@/dht_node/index' import { CONFIG } from '@/config' -import { logger } from '@/server/logger' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' +import { defaultCategory, initLogger } from 'config-schema' import { AppDatabase } from 'database' +import { getLogger } from 'log4js' async function main() { + // init logger + initLogger( + [defaultCategory(LOG4JS_BASE_CATEGORY_NAME, CONFIG.LOG_LEVEL)], + CONFIG.LOG_FILES_BASE_PATH, + CONFIG.LOG4JS_CONFIG, + ) + const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}`) // open mysql connection await AppDatabase.getInstance().init() logger.debug(`dhtseed set by CONFIG.FEDERATION_DHT_SEED=${CONFIG.FEDERATION_DHT_SEED}`) diff --git a/dht-node/src/server/logger.ts b/dht-node/src/server/logger.ts deleted file mode 100644 index 99ea8518c..000000000 --- a/dht-node/src/server/logger.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { readFileSync } from 'fs' - -import { configure, getLogger } from 'log4js' - -import { CONFIG } from '@/config' - -const options = JSON.parse(readFileSync(CONFIG.LOG4JS_CONFIG, 'utf-8')) - -options.categories.dht.level = CONFIG.LOG_LEVEL -let filename: string = options.appenders.dht.filename -options.appenders.dht.filename = filename.replace( - 'apiversion-%v', - 'dht-' + CONFIG.FEDERATION_DHT_TOPIC, -) -filename = options.appenders.errorFile.filename - -configure(options) - -const logger = getLogger('dht') - -export { logger } diff --git a/dht-node/test/helpers.ts b/dht-node/test/helpers.ts index 9d1829a88..52b847b4c 100644 --- a/dht-node/test/helpers.ts +++ b/dht-node/test/helpers.ts @@ -1,5 +1,4 @@ import { AppDatabase, entities } from 'database' -import { CONFIG } from '@/config' export const headerPushMock = jest.fn((t) => { context.token = t.value diff --git a/dht-node/test/testSetup.ts b/dht-node/test/testSetup.ts deleted file mode 100644 index ff619e95d..000000000 --- a/dht-node/test/testSetup.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { logger } from '@/server/logger' - -jest.setTimeout(1000000) - -jest.mock('@/server/logger', () => { - const originalModule = jest.requireActual('@/server/logger') - return { - __esModule: true, - ...originalModule, - logger: { - addContext: jest.fn(), - trace: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - info: jest.fn(), - error: jest.fn(), - fatal: jest.fn(), - }, - } -}) - -export { logger } diff --git a/dht-node/tsconfig.json b/dht-node/tsconfig.json index 757d61e02..9628b7c41 100644 --- a/dht-node/tsconfig.json +++ b/dht-node/tsconfig.json @@ -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": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ @@ -74,5 +74,8 @@ /* Advanced Options */ "skipLibCheck": true, /* Skip type checking of declaration files. */ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "ts-node": { + "swc": true } } diff --git a/dht-node/turbo.json b/dht-node/turbo.json index f6211afa1..3044e8840 100644 --- a/dht-node/turbo.json +++ b/dht-node/turbo.json @@ -2,10 +2,10 @@ "extends": ["//"], "tasks": { "test": { - "dependsOn": ["database#build", "config-schema#build", "database#up:dht_test"] + "dependsOn": ["config-schema#build", "database#build", "database#up:dht_test"] }, "dev": { - "dependsOn": ["database#up"] + "dependsOn": ["config-schema#build", "database#build", "database#up"] }, "start": { "dependsOn": ["database#up", "build"] diff --git a/yarn.lock b/yarn.lock index 235de4b00..9f39a5e23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1185,6 +1185,13 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/create-cache-key-function@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0" + integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA== + dependencies: + "@jest/types" "^29.6.3" + "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" @@ -2069,6 +2076,15 @@ dependencies: tslib "^2.8.0" +"@swc/jest@^0.2.38": + version "0.2.38" + resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.38.tgz#8b137e344c6c021d4e49ee2bc62b0e5e564d2c7c" + integrity sha512-HMoZgXWMqChJwffdDjvplH53g9G2ALQes3HKXDEdliB/b85OQ0CTSbxG8VSeCwiAn7cOaDVEt4mwmZvbHcS52w== + dependencies: + "@jest/create-cache-key-function" "^29.7.0" + "@swc/counter" "^0.1.3" + jsonc-parser "^3.2.0" + "@swc/types@^0.1.21": version "0.1.21" resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.21.tgz#6fcadbeca1d8bc89e1ab3de4948cef12344a38c0" @@ -8110,6 +8126,11 @@ jsonc-eslint-parser@^2.0.0, jsonc-eslint-parser@^2.3.0: espree "^9.0.0" semver "^7.3.5" +jsonc-parser@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" + integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" From a7a29623a88b85b387c0dd04ca48d20c1cf72e70 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 15 Jun 2025 08:49:18 +0200 Subject: [PATCH 06/46] fix missing ending --- config-schema/src/log4js-config/appenders.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config-schema/src/log4js-config/appenders.ts b/config-schema/src/log4js-config/appenders.ts index 59b42aec3..6648d2924 100644 --- a/config-schema/src/log4js-config/appenders.ts +++ b/config-schema/src/log4js-config/appenders.ts @@ -56,7 +56,7 @@ export function createAppenderConfig( basePath?: string, ): { [name: string]: Appender } { if (basePath) { - defaultAppenders.errorFile.filename = `${basePath}/errors` + defaultAppenders.errorFile.filename = `${basePath}/errors.log` } const customAppender: { [name: string]: Appender } = { ...defaultAppenders } From 1f3235a34f87d1e4c71e85808b4177277480472f Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 15 Jun 2025 09:23:14 +0200 Subject: [PATCH 07/46] fix github worker errors --- config-schema/package.json | 1 + dht-node/Dockerfile | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/config-schema/package.json b/config-schema/package.json index 3759d9373..cee1cfa70 100644 --- a/config-schema/package.json +++ b/config-schema/package.json @@ -29,6 +29,7 @@ "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" diff --git a/dht-node/Dockerfile b/dht-node/Dockerfile index bd397fea0..c19888a5d 100644 --- a/dht-node/Dockerfile +++ b/dht-node/Dockerfile @@ -116,8 +116,5 @@ COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/dht-node/build/index.js ./in # 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}/dht-node/log4js-config.json ./log4js-config.json - # Run command CMD ["node", "index.js"] From bb9759ace323af848ee55072dc9c96d66b526cd3 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 15 Jun 2025 14:06:24 +0200 Subject: [PATCH 08/46] add option for file line to colored context layout --- config-schema/package.json | 1 + config-schema/src/commonSchema.ts | 13 ++++---- config-schema/src/const.ts | 1 + config-schema/src/index.ts | 4 ++- .../src/log4js-config/coloredContext.ts | 33 ++++++++++++++++--- config-schema/src/log4js-config/index.ts | 2 +- .../log4js-config/types/CustomFileAppender.ts | 4 +++ 7 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 config-schema/src/const.ts diff --git a/config-schema/package.json b/config-schema/package.json index cee1cfa70..0b0487255 100644 --- a/config-schema/package.json +++ b/config-schema/package.json @@ -24,6 +24,7 @@ "devDependencies": { "@biomejs/biome": "1.9.4", "@types/node": "^17.0.21", + "jest": "27.2.4", "typescript": "^4.9.5" }, "dependencies": { diff --git a/config-schema/src/commonSchema.ts b/config-schema/src/commonSchema.ts index 3f7b0a13e..bee046c7a 100644 --- a/config-schema/src/commonSchema.ts +++ b/config-schema/src/commonSchema.ts @@ -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) => { @@ -131,6 +125,13 @@ 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') diff --git a/config-schema/src/const.ts b/config-schema/src/const.ts new file mode 100644 index 000000000..87df0f804 --- /dev/null +++ b/config-schema/src/const.ts @@ -0,0 +1 @@ +export const DECAY_START_TIME = new Date('2021-05-13T17:46:31Z') \ No newline at end of file diff --git a/config-schema/src/index.ts b/config-schema/src/index.ts index c2ad9fc96..9c7c32653 100644 --- a/config-schema/src/index.ts +++ b/config-schema/src/index.ts @@ -2,4 +2,6 @@ import 'source-map-support/register' export * from './commonSchema' export { DatabaseConfigSchema } from './DatabaseConfigSchema' export { validate } from './validate' -export { createLog4jsConfig, type Category, initLogger, defaultCategory } from './log4js-config' +export type { LogLevel, Category } from './log4js-config' +export { createLog4jsConfig, initLogger, defaultCategory } from './log4js-config' +export { DECAY_START_TIME } from './const' diff --git a/config-schema/src/log4js-config/coloredContext.ts b/config-schema/src/log4js-config/coloredContext.ts index a221c7c9d..21a2d7b28 100644 --- a/config-schema/src/log4js-config/coloredContext.ts +++ b/config-schema/src/log4js-config/coloredContext.ts @@ -5,6 +5,7 @@ import { LogLevel } from './types' export type coloredContextLayoutConfig = { withStack?: LogLevel | boolean withFile?: LogLevel | boolean + withLine?: LogLevel | boolean } function colorize(str: string, level: Level): string { @@ -66,12 +67,35 @@ function isEnabledByLogLevel(eventLogLevel: Level, targetLogLevel?: LogLevel | b 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}] ${logEvent.categoryName} -`, + `[${logEvent.startTime.toISOString()}] [${logEvent.level}] ${categoryName} -`, logEvent.level, ), ) @@ -79,12 +103,11 @@ export function createColoredContextLayout(config: coloredContextLayoutConfig) { result.push(composeContextString(logEvent.context)) } result.push(composeDataString(logEvent.data)) - const showCallstack = - logEvent.callStack && isEnabledByLogLevel(logEvent.level, config.withStack) - if (!showCallstack && isEnabledByLogLevel(logEvent.level, config.withFile)) { + + if (detailKind === DetailKind.File) { result.push(`\n at ${logEvent.fileName}:${logEvent.lineNumber}`) } - if (showCallstack) { + if (detailKind === DetailKind.Callstack) { result.push(`\n${logEvent.callStack}`) } return result.join(' ') diff --git a/config-schema/src/log4js-config/index.ts b/config-schema/src/log4js-config/index.ts index 9652726ce..18699639f 100644 --- a/config-schema/src/log4js-config/index.ts +++ b/config-schema/src/log4js-config/index.ts @@ -33,7 +33,7 @@ export function createLog4jsConfig(categories: Category[], basePath?: string): C customFileAppenders.push({ name: category.name, filename: category.filename, - withFile: true, + withLine: true, withStack: 'error', }) // needed by log4js, show all error message accidentally without (proper) Category diff --git a/config-schema/src/log4js-config/types/CustomFileAppender.ts b/config-schema/src/log4js-config/types/CustomFileAppender.ts index f2281805e..46c292349 100644 --- a/config-schema/src/log4js-config/types/CustomFileAppender.ts +++ b/config-schema/src/log4js-config/types/CustomFileAppender.ts @@ -22,10 +22,14 @@ import { LogLevel } from './LogLevel' * { name: 'warn', filename: 'warn.log', withStack: 'debug' }, * ]) * ``` + * if stack is shown, no file and no line is shown, because it is already in the stack trace + * if file:line is shown, no extra line is shown + * line will be shown after category name:line */ export type CustomFileAppender = { name: string filename?: string withStack?: LogLevel | boolean // with stack if boolean or from log level on or above withFile?: LogLevel | boolean // with filename and line if boolean or from log level on or above + withLine?: LogLevel | boolean // with line if boolean or from log level on or above } From a7d011b81411ba6ce9c3120b76d37079dee5ba86 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 15 Jun 2025 14:07:42 +0200 Subject: [PATCH 09/46] use new logging approach in federation --- .vscode/launch.json | 16 ++ dht-node/src/config/index.ts | 3 +- federation/esbuild.config.ts | 1 + federation/jest.config.js | 4 +- federation/log4js-config.json | 151 ------------------ federation/package.json | 2 + .../src/client/1_0/AuthenticationClient.ts | 27 ++-- federation/src/client/1_0/index.ts | 3 + federation/src/config/const.ts | 1 + federation/src/config/index.ts | 14 +- federation/src/config/schema.ts | 6 +- federation/src/graphql/api/1_0/index.ts | 3 + .../1_0/resolver/AuthenticationResolver.ts | 24 +-- .../PublicCommunityInfoResolver.test.ts | 4 +- .../resolver/PublicCommunityInfoResolver.ts | 5 +- .../1_0/resolver/PublicKeyResolver.test.ts | 4 +- .../api/1_0/resolver/PublicKeyResolver.ts | 5 +- .../1_0/resolver/SendCoinsResolver.test.ts | 5 +- .../api/1_0/resolver/SendCoinsResolver.ts | 5 +- .../src/graphql/api/1_0/resolver/index.ts | 3 + .../api/1_0/util/authenticateCommunity.ts | 30 ++-- federation/src/graphql/api/1_0/util/index.ts | 3 + .../util/revertSettledReceiveTransaction.ts | 4 +- .../util/settlePendingReceiveTransaction.ts | 4 +- .../graphql/api/1_0/util/storeForeignUser.ts | 5 +- .../1_1/resolver/PublicKeyResolver.test.ts | 4 +- .../api/1_1/resolver/PublicKeyResolver.ts | 9 +- .../src/graphql/api/1_1/resolver/index.ts | 3 + federation/src/graphql/api/index.ts | 3 + federation/src/graphql/api/schema.ts | 5 +- federation/src/graphql/index.ts | 3 + .../src/graphql/util/checkTradingLevel.ts | 5 +- federation/src/graphql/util/decay.ts | 12 +- federation/src/graphql/util/index.ts | 3 + federation/src/index.ts | 25 +-- federation/src/server/LogError.ts | 17 +- federation/src/server/createServer.ts | 17 +- federation/src/server/logger.ts | 31 ---- federation/test/helpers.ts | 5 +- federation/test/testSetup.ts | 43 ----- 40 files changed, 190 insertions(+), 327 deletions(-) delete mode 100644 federation/log4js-config.json create mode 100644 federation/src/client/1_0/index.ts create mode 100644 federation/src/config/const.ts create mode 100644 federation/src/graphql/api/1_0/index.ts create mode 100644 federation/src/graphql/api/1_0/resolver/index.ts create mode 100644 federation/src/graphql/api/1_0/util/index.ts create mode 100644 federation/src/graphql/api/1_1/resolver/index.ts create mode 100644 federation/src/graphql/api/index.ts create mode 100644 federation/src/graphql/index.ts create mode 100644 federation/src/graphql/util/index.ts delete mode 100644 federation/src/server/logger.ts delete mode 100644 federation/test/testSetup.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 9a7177fa2..5ef63cc68 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -26,6 +26,22 @@ "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "cwd": "${workspaceFolder}/dht-node" + }, + { + "type": "node", + "request": "launch", + "name": "Federation Debug Tests", + "runtimeExecutable": "yarn", + "runtimeArgs": [ + "run", + "test:debug" + ], + "skipFiles": [ + "/**" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "cwd": "${workspaceFolder}/federation" } ] } \ No newline at end of file diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index edcb8a04f..7f403ef26 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -1,7 +1,6 @@ import { validate } from 'config-schema' +import type { LogLevel } from 'config-schema' import dotenv from 'dotenv' - -import { LogLevel } from 'config-schema/src/log4js-config' import { schema } from './schema' dotenv.config() diff --git a/federation/esbuild.config.ts b/federation/esbuild.config.ts index b4154f008..2ef5f88e5 100644 --- a/federation/esbuild.config.ts +++ b/federation/esbuild.config.ts @@ -12,4 +12,5 @@ build({ external: ['sodium-native'], plugins: [esbuildDecorators()], minify: true, + sourcemap: true, }) \ No newline at end of file diff --git a/federation/jest.config.js b/federation/jest.config.js index 44ddf9bf5..9766c8848 100644 --- a/federation/jest.config.js +++ b/federation/jest.config.js @@ -2,14 +2,14 @@ module.exports = { verbose: true, preset: 'ts-jest', - collectCoverage: true, + collectCoverage: false, collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { lines: 68, }, }, - setupFiles: ['/test/testSetup.ts'], + setupFiles: ['config-schema/test/testSetup.ts'], setupFilesAfterEnv: [], modulePathIgnorePatterns: ['/build/'], moduleNameMapper: { diff --git a/federation/log4js-config.json b/federation/log4js-config.json deleted file mode 100644 index e9336f83c..000000000 --- a/federation/log4js-config.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "appenders": - { - "access": - { - "type": "dateFile", - "filename": "../logs/federation/access-%p.log", - "pattern": "yyyy-MM-dd", - "layout": - { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" - }, - "compress": true, - "keepFileExt" : true, - "fileNameSep" : "_", - "numBackups" : 30 - }, - "apollo": - { - "type": "dateFile", - "filename": "../logs/federation/apollo-%p.log", - "pattern": "yyyy-MM-dd", - "layout": - { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" - }, - "compress": true, - "keepFileExt" : true, - "fileNameSep" : "_", - "numBackups" : 30 - }, - "backend": - { - "type": "dateFile", - "filename": "../logs/federation/backend-%p.log", - "pattern": "yyyy-MM-dd", - "layout": - { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" - }, - "compress": true, - "keepFileExt" : true, - "fileNameSep" : "_", - "numBackups" : 30 - }, - "federation": - { - "type": "dateFile", - "filename": "../logs/federation/apiversion-%v-%p.log", - "pattern": "yyyy-MM-dd", - "layout": - { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" - }, - "compress": true, - "keepFileExt" : true, - "fileNameSep" : "_", - "numBackups" : 30 - }, - "errorFile": - { - "type": "dateFile", - "filename": "../logs/federation/errors-%p.log", - "pattern": "yyyy-MM-dd", - "layout": - { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" - }, - "compress": true, - "keepFileExt" : true, - "fileNameSep" : "_", - "numBackups" : 30 - }, - "errors": - { - "type": "logLevelFilter", - "level": "error", - "appender": "errorFile" - }, - "out": - { - "type": "stdout", - "layout": - { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" - } - }, - "apolloOut": - { - "type": "stdout", - "layout": - { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" - } - } - }, - "categories": - { - "default": - { - "appenders": - [ - "out", - "errors" - ], - "level": "debug", - "enableCallStack": true - }, - "apollo": - { - "appenders": - [ - "apollo", - "apolloOut", - "errors" - ], - "level": "debug", - "enableCallStack": true - }, - "backend": - { - "appenders": - [ - "backend", - "out", - "errors" - ], - "level": "debug", - "enableCallStack": true - }, - "federation": - { - "appenders": - [ - "federation", - "out", - "errors" - ], - "level": "debug", - "enableCallStack": true - }, - "http": - { - "appenders": - [ - "access" - ], - "level": "info" - } - } -} diff --git a/federation/package.json b/federation/package.json index f98b6adf7..cf0ab77d4 100644 --- a/federation/package.json +++ b/federation/package.json @@ -15,6 +15,8 @@ "dev:bun": "cross-env TZ=UTC bun --hot src/index.ts", "typecheck": "tsc --noEmit", "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --runInBand --forceExit --detectOpenHandles", + "test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles", + "test:coverage": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --coverage --runInBand --forceExit --detectOpenHandles", "lint": "biome check --error-on-warnings .", "lint:fix": "biome check --error-on-warnings . --write" }, diff --git a/federation/src/client/1_0/AuthenticationClient.ts b/federation/src/client/1_0/AuthenticationClient.ts index 2aa7b55d3..8535e06ab 100644 --- a/federation/src/client/1_0/AuthenticationClient.ts +++ b/federation/src/client/1_0/AuthenticationClient.ts @@ -1,12 +1,15 @@ -import { federationLogger as logger } from '@/server/logger' import { FederatedCommunity as DbFederatedCommunity } from 'database' import { GraphQLClient } from 'graphql-request' +import { getLogger } from 'log4js' import { AuthenticationArgs } from '@/graphql/api/1_0/model/AuthenticationArgs' import { OpenConnectionCallbackArgs } from '@/graphql/api/1_0/model/OpenConnectionCallbackArgs' +import { LOG4JS_CLIENT_1_0_CATEGORY_NAME } from '.' import { authenticate } from './query/authenticate' import { openConnectionCallback } from './query/openConnectionCallback' +const logger = getLogger(`${LOG4JS_CLIENT_1_0_CATEGORY_NAME}.AuthenticationClient`) + export class AuthenticationClient { dbCom: DbFederatedCommunity endpoint: string @@ -27,41 +30,35 @@ export class AuthenticationClient { } async openConnectionCallback(args: OpenConnectionCallbackArgs): Promise { - logger.debug('Authentication: openConnectionCallback with endpoint', this.endpoint, args) + logger.debug('openConnectionCallback with endpoint', this.endpoint, args) try { const { data } = await this.client.rawRequest(openConnectionCallback, { args }) if (data && data.openConnectionCallback) { - logger.warn( - 'Authentication: openConnectionCallback without response data from endpoint', - this.endpoint, - ) + logger.warn('openConnectionCallback without response data from endpoint', this.endpoint) return false } - logger.debug( - 'Authentication: openConnectionCallback successfully started with endpoint', - this.endpoint, - ) + logger.debug('openConnectionCallback successfully started with endpoint', this.endpoint) return true } catch (err) { - logger.error('Authentication: error on openConnectionCallback', err) + logger.error('error on openConnectionCallback', err) } return false } async authenticate(args: AuthenticationArgs): Promise { - logger.debug('Authentication: authenticate with endpoint=', this.endpoint) + logger.debug('authenticate with endpoint=', this.endpoint) try { const { data } = await this.client.rawRequest(authenticate, { args }) - logger.debug('Authentication: after authenticate: data:', data) + logger.debug('after authenticate: data:', data) const authUuid: string = data?.authenticate if (authUuid) { - logger.debug('Authentication: received authenticated uuid', authUuid) + logger.debug('received authenticated uuid', authUuid) return authUuid } } catch (err) { - logger.error('Authentication: authenticate failed', { + logger.error('authenticate failed', { endpoint: this.endpoint, err, }) diff --git a/federation/src/client/1_0/index.ts b/federation/src/client/1_0/index.ts new file mode 100644 index 000000000..18e8aaeed --- /dev/null +++ b/federation/src/client/1_0/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' + +export const LOG4JS_CLIENT_1_0_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.client.1_0` diff --git a/federation/src/config/const.ts b/federation/src/config/const.ts new file mode 100644 index 000000000..4d0e8c1db --- /dev/null +++ b/federation/src/config/const.ts @@ -0,0 +1 @@ +export const LOG4JS_BASE_CATEGORY_NAME = 'federation' diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index 2d0ab8b87..6e9d5437e 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -3,6 +3,7 @@ import { Decimal } from 'decimal.js-light' import dotenv from 'dotenv' import { validate } from 'config-schema' +import type { LogLevel } from 'config-schema' import { schema } from './schema' @@ -13,11 +14,12 @@ Decimal.set({ rounding: Decimal.ROUND_HALF_UP, }) -const constants = { - DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 - LOG4JS_CONFIG: 'log4js-config.json', +const logging = { + LOG4JS_CONFIG_PLACEHOLDER: process.env.LOG4JS_CONFIG_PLACEHOLDER ?? 'log4js-config-%v.json', // default log level on production should be info - LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', + // log level for default log4js-config.json, don't change existing log4js-config.json + LOG_LEVEL: (process.env.LOG_LEVEL ?? 'info') as LogLevel, + LOG_FILES_BASE_PATH: process.env.LOG_FILES_BASE_PATH ?? '../logs/federation', } const server = { @@ -44,10 +46,8 @@ const federation = { } export const CONFIG = { - ...constants, + ...logging, ...server, - // ...community, - // ...eventProtocol, ...federation, } diff --git a/federation/src/config/schema.ts b/federation/src/config/schema.ts index 14e6168cd..82e05d3bc 100644 --- a/federation/src/config/schema.ts +++ b/federation/src/config/schema.ts @@ -1,7 +1,8 @@ import { DECAY_START_TIME, GRAPHIQL, - LOG4JS_CONFIG, + LOG4JS_CONFIG_PLACEHOLDER, + LOG_FILES_BASE_PATH, LOG_LEVEL, NODE_ENV, PRODUCTION, @@ -11,7 +12,8 @@ import Joi from 'joi' export const schema = Joi.object({ DECAY_START_TIME, GRAPHIQL, - LOG4JS_CONFIG, + LOG4JS_CONFIG_PLACEHOLDER, + LOG_FILES_BASE_PATH, LOG_LEVEL, NODE_ENV, PRODUCTION, diff --git a/federation/src/graphql/api/1_0/index.ts b/federation/src/graphql/api/1_0/index.ts new file mode 100644 index 000000000..3a995fd28 --- /dev/null +++ b/federation/src/graphql/api/1_0/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_API_CATEGORY_NAME } from '@/graphql/api' + +export const LOG4JS_API_1_0_CATEGORY_NAME = `${LOG4JS_API_CATEGORY_NAME}.1_0` diff --git a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts index 2479bb8be..89e891b34 100644 --- a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts @@ -1,18 +1,21 @@ import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' -import { federationLogger as logger } from '@/server/logger' import { CommunityLoggingView, Community as DbCommunity, FederatedCommunity as DbFedCommunity, FederatedCommunityLoggingView, } from 'database' +import { getLogger } from 'log4js' import { Arg, Mutation, Resolver } from 'type-graphql' +import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.' import { AuthenticationArgs } from '../model/AuthenticationArgs' import { OpenConnectionArgs } from '../model/OpenConnectionArgs' import { OpenConnectionCallbackArgs } from '../model/OpenConnectionCallbackArgs' import { startAuthentication, startOpenConnectionCallback } from '../util/authenticateCommunity' +const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.AuthenticationResolver`) + @Resolver() export class AuthenticationResolver { @Mutation(() => Boolean) @@ -21,7 +24,7 @@ export class AuthenticationResolver { args: OpenConnectionArgs, ): Promise { const pubKeyBuf = Buffer.from(args.publicKey, 'hex') - logger.debug(`Authentication: openConnection() via apiVersion=1_0:`, args) + logger.debug(`openConnection() via apiVersion=1_0:`, args) // first find with args.publicKey the community 'comA', which starts openConnection request const comA = await DbCommunity.findOneBy({ @@ -30,7 +33,7 @@ export class AuthenticationResolver { if (!comA) { throw new LogError(`unknown requesting community with publicKey`, pubKeyBuf.toString('hex')) } - logger.debug(`Authentication: found requestedCom:`, new CommunityLoggingView(comA)) + logger.debug(`found requestedCom:`, new CommunityLoggingView(comA)) // biome-ignore lint/complexity/noVoid: no await to respond immediately and invoke callback-request asynchronously void startOpenConnectionCallback(args, comA, CONFIG.FEDERATION_API) return true @@ -41,17 +44,17 @@ export class AuthenticationResolver { @Arg('data') args: OpenConnectionCallbackArgs, ): Promise { - logger.debug(`Authentication: openConnectionCallback() via apiVersion=1_0 ...`, args) + logger.debug(`openConnectionCallback() via apiVersion=1_0 ...`, args) // TODO decrypt args.url with homeCom.privateKey and verify signing with callbackFedCom.publicKey const endPoint = args.url.slice(0, args.url.lastIndexOf('/') + 1) const apiVersion = args.url.slice(args.url.lastIndexOf('/') + 1, args.url.length) - logger.debug(`Authentication: search fedComB per:`, endPoint, apiVersion) + logger.debug(`search fedComB per:`, endPoint, apiVersion) const fedComB = await DbFedCommunity.findOneBy({ endPoint, apiVersion }) if (!fedComB) { throw new LogError(`unknown callback community with url`, args.url) } logger.debug( - `Authentication: found fedComB and start authentication:`, + `found fedComB and start authentication:`, new FederatedCommunityLoggingView(fedComB), ) // biome-ignore lint/complexity/noVoid: no await to respond immediately and invoke authenticate-request asynchronously @@ -64,18 +67,15 @@ export class AuthenticationResolver { @Arg('data') args: AuthenticationArgs, ): Promise { - logger.debug(`Authentication: authenticate() via apiVersion=1_0 ...`, args) + logger.debug(`authenticate() via apiVersion=1_0 ...`, args) const authCom = await DbCommunity.findOneByOrFail({ communityUuid: args.oneTimeCode }) - logger.debug('Authentication: found authCom:', new CommunityLoggingView(authCom)) + logger.debug('found authCom:', new CommunityLoggingView(authCom)) if (authCom) { // TODO decrypt args.uuid with authCom.publicKey authCom.communityUuid = args.uuid authCom.authenticatedAt = new Date() await DbCommunity.save(authCom) - logger.debug( - 'Authentication: store authCom.uuid successfully:', - new CommunityLoggingView(authCom), - ) + logger.debug('store authCom.uuid successfully:', new CommunityLoggingView(authCom)) const homeCom = await DbCommunity.findOneByOrFail({ foreign: false }) // TODO encrypt homeCom.uuid with homeCom.privateKey if (homeCom.communityUuid) { diff --git a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts index b3a094b4f..781da1a54 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts @@ -1,6 +1,8 @@ import { CONFIG } from '@/config' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { createServer } from '@/server/createServer' import { createTestClient } from 'apollo-server-testing' +import { getLogger } from 'log4js' import { Community as DbCommunity } from 'database' import { DataSource } from 'typeorm' @@ -12,7 +14,7 @@ let con: DataSource CONFIG.FEDERATION_API = '1_0' beforeAll(async () => { - const server = await createServer() + const server = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`)) con = server.con query = createTestClient(server.apollo).query DbCommunity.clear() diff --git a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.ts b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.ts index e019f27f6..9bd2898f3 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.ts @@ -1,9 +1,12 @@ -import { federationLogger as logger } from '@/server/logger' import { Community as DbCommunity } from 'database' +import { getLogger } from 'log4js' import { Query, Resolver } from 'type-graphql' +import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.' import { GetPublicCommunityInfoResultLoggingView } from '../logger/GetPublicCommunityInfoResultLogging.view' import { GetPublicCommunityInfoResult } from '../model/GetPublicCommunityInfoResult' +const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.PublicCommunityInfoResolver`) + @Resolver() export class PublicCommunityInfoResolver { @Query(() => GetPublicCommunityInfoResult) diff --git a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts index 894570972..c6d516342 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts @@ -1,6 +1,8 @@ import { CONFIG } from '@/config' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { createServer } from '@/server/createServer' import { createTestClient } from 'apollo-server-testing' +import { getLogger } from 'log4js' import { FederatedCommunity as DbFederatedCommunity } from 'database' let query: any @@ -11,7 +13,7 @@ let con: any CONFIG.FEDERATION_API = '1_0' beforeAll(async () => { - const server = await createServer() + const server = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`)) con = server.con query = createTestClient(server.apollo).query DbFederatedCommunity.clear() diff --git a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts index 7c4d734c1..f849941e0 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts @@ -1,8 +1,11 @@ -import { federationLogger as logger } from '@/server/logger' import { FederatedCommunity as DbFederatedCommunity } from 'database' +import { getLogger } from 'log4js' import { Query, Resolver } from 'type-graphql' +import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.' import { GetPublicKeyResult } from '../model/GetPublicKeyResult' +const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.PublicKeyResolver`) + @Resolver() export class PublicKeyResolver { @Query(() => GetPublicKeyResult) diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts index 44273b4c5..6ec69493e 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -1,8 +1,9 @@ import { CONFIG } from '@/config' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { fullName } from '@/graphql/util/fullName' import { cleanDB, testEnvironment } from '@test/helpers' -import { logger } from '@test/testSetup' import { ApolloServerTestClient } from 'apollo-server-testing' +import { getLogger } from 'log4js' import { Community as DbCommunity, User as DbUser, UserContact as DbUserContact } from 'database' import Decimal from 'decimal.js-light' import { GraphQLError } from 'graphql' @@ -28,7 +29,7 @@ let recipUser: DbUser let recipContact: DbUserContact beforeAll(async () => { - testEnv = await testEnvironment(logger) + testEnv = await testEnvironment(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`)) mutate = testEnv.mutate // query = testEnv.query // con = testEnv.con diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 7f9d1a684..a6eeef7f8 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -1,14 +1,15 @@ import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier' import { fullName } from '@/graphql/util/fullName' import { LogError } from '@/server/LogError' -import { federationLogger as logger } from '@/server/logger' import { Community as DbCommunity, PendingTransaction as DbPendingTransaction, PendingTransactionLoggingView, } from 'database' import Decimal from 'decimal.js-light' +import { getLogger } from 'log4js' import { Arg, Mutation, Resolver } from 'type-graphql' +import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.' import { PendingTransactionState } from '../enum/PendingTransactionState' import { TransactionTypeId } from '../enum/TransactionTypeId' import { SendCoinsArgsLoggingView } from '../logger/SendCoinsArgsLogging.view' @@ -20,6 +21,8 @@ import { revertSettledReceiveTransaction } from '../util/revertSettledReceiveTra import { settlePendingReceiveTransaction } from '../util/settlePendingReceiveTransaction' import { storeForeignUser } from '../util/storeForeignUser' +const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.SendCoinsResolver`) + @Resolver() export class SendCoinsResolver { @Mutation(() => SendCoinsResult) diff --git a/federation/src/graphql/api/1_0/resolver/index.ts b/federation/src/graphql/api/1_0/resolver/index.ts new file mode 100644 index 000000000..adec2adb9 --- /dev/null +++ b/federation/src/graphql/api/1_0/resolver/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_API_1_0_CATEGORY_NAME } from '@/graphql/api/1_0' + +export const LOG4JS_RESOLVER_1_0_CATEGORY_NAME = `${LOG4JS_API_1_0_CATEGORY_NAME}.resolver` diff --git a/federation/src/graphql/api/1_0/util/authenticateCommunity.ts b/federation/src/graphql/api/1_0/util/authenticateCommunity.ts index 0004fb5b6..e4d27e47c 100644 --- a/federation/src/graphql/api/1_0/util/authenticateCommunity.ts +++ b/federation/src/graphql/api/1_0/util/authenticateCommunity.ts @@ -1,10 +1,10 @@ -import { federationLogger as logger } from '@/server/logger' import { CommunityLoggingView, Community as DbCommunity, FederatedCommunity as DbFedCommunity, FederatedCommunityLoggingView, } from 'database' +import { getLogger } from 'log4js' import { OpenConnectionArgs } from '../model/OpenConnectionArgs' import { OpenConnectionCallbackArgs } from '../model/OpenConnectionCallbackArgs' @@ -12,14 +12,17 @@ import { AuthenticationClientFactory } from '@/client/AuthenticationClientFactor import { randombytes_random } from 'sodium-native' import { AuthenticationClient as V1_0_AuthenticationClient } from '@/client/1_0/AuthenticationClient' +import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.' import { AuthenticationArgs } from '../model/AuthenticationArgs' +const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.authenticateCommunity`) + export async function startOpenConnectionCallback( args: OpenConnectionArgs, comA: DbCommunity, api: string, ): Promise { - logger.debug(`Authentication: startOpenConnectionCallback() with:`, { + logger.debug(`startOpenConnectionCallback() with:`, { args, comA: new CommunityLoggingView(comA), }) @@ -53,13 +56,13 @@ export async function startOpenConnectionCallback( : homeFedCom.endPoint + '/' + homeFedCom.apiVersion logger.debug(`Authentication: start openConnectionCallback with args:`, callbackArgs) if (await client.openConnectionCallback(callbackArgs)) { - logger.debug('Authentication: startOpenConnectionCallback() successful:', callbackArgs) + logger.debug('startOpenConnectionCallback() successful:', callbackArgs) } else { - logger.error('Authentication: startOpenConnectionCallback() failed:', callbackArgs) + logger.error('startOpenConnectionCallback() failed:', callbackArgs) } } } catch (err) { - logger.error('Authentication: error in startOpenConnectionCallback:', err) + logger.error('error in startOpenConnectionCallback:', err) } } @@ -67,7 +70,7 @@ export async function startAuthentication( oneTimeCode: string, fedComB: DbFedCommunity, ): Promise { - logger.debug(`Authentication: startAuthentication()...`, { + logger.debug(`startAuthentication()...`, { oneTimeCode, fedComB: new FederatedCommunityLoggingView(fedComB), }) @@ -84,12 +87,12 @@ export async function startAuthentication( if (homeCom.communityUuid) { authenticationArgs.uuid = homeCom.communityUuid } - logger.debug(`Authentication: invoke authenticate() with:`, authenticationArgs) + logger.debug(`invoke authenticate() with:`, authenticationArgs) const fedComUuid = await client.authenticate(authenticationArgs) - logger.debug(`Authentication: response of authenticate():`, fedComUuid) + logger.debug(`response of authenticate():`, fedComUuid) if (fedComUuid !== null) { logger.debug( - `Authentication: received communityUUid for callbackFedCom:`, + `received communityUUid for callbackFedCom:`, fedComUuid, new FederatedCommunityLoggingView(fedComB), ) @@ -101,15 +104,12 @@ export async function startAuthentication( callbackCom.communityUuid = fedComUuid callbackCom.authenticatedAt = new Date() await DbCommunity.save(callbackCom) - logger.debug( - 'Authentication: Community Authentication successful:', - new CommunityLoggingView(callbackCom), - ) + logger.debug('Community Authentication successful:', new CommunityLoggingView(callbackCom)) } else { - logger.error('Authentication: Community Authentication failed:', authenticationArgs) + logger.error('Community Authentication failed:', authenticationArgs) } } } catch (err) { - logger.error('Authentication: error in startOpenConnectionCallback:', err) + logger.error('error in startAuthentication:', err) } } diff --git a/federation/src/graphql/api/1_0/util/index.ts b/federation/src/graphql/api/1_0/util/index.ts new file mode 100644 index 000000000..109466d5c --- /dev/null +++ b/federation/src/graphql/api/1_0/util/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_API_1_0_CATEGORY_NAME } from '@/graphql/api/1_0' + +export const LOG4JS_1_0_UTIL_CATEGORY_NAME = `${LOG4JS_API_1_0_CATEGORY_NAME}.util` diff --git a/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts index 8a2bd7663..cdb8a4563 100644 --- a/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts @@ -13,12 +13,14 @@ import { import { PendingTransactionState } from '../enum/PendingTransactionState' import { LogError } from '@/server/LogError' -import { federationLogger as logger } from '@/server/logger' +import { getLogger } from 'log4js' +import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.' import { TRANSACTIONS_LOCK } from '@/graphql/util/TRANSACTIONS_LOCK' import { getLastTransaction } from '@/graphql/util/getLastTransaction' const db = AppDatabase.getInstance() +const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.revertSettledReceiveTransaction`) export async function revertSettledReceiveTransaction( homeCom: DbCommunity, diff --git a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts index 306313f82..b10d40a03 100644 --- a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts @@ -12,14 +12,16 @@ import { import { PendingTransactionState } from '../enum/PendingTransactionState' import { LogError } from '@/server/LogError' -import { federationLogger as logger } from '@/server/logger' import { TRANSACTIONS_LOCK } from '@/graphql/util/TRANSACTIONS_LOCK' import { getLastTransaction } from '@/graphql/util/getLastTransaction' import Decimal from 'decimal.js-light' +import { getLogger } from 'log4js' +import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.' import { calculateRecipientBalance } from './calculateRecipientBalance' const db = AppDatabase.getInstance() +const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.settlePendingReceiveTransaction`) export async function settlePendingReceiveTransaction( homeCom: DbCommunity, diff --git a/federation/src/graphql/api/1_0/util/storeForeignUser.ts b/federation/src/graphql/api/1_0/util/storeForeignUser.ts index d0f05ae1e..c7991f482 100644 --- a/federation/src/graphql/api/1_0/util/storeForeignUser.ts +++ b/federation/src/graphql/api/1_0/util/storeForeignUser.ts @@ -1,9 +1,12 @@ import { User as DbUser, UserLoggingView } from 'database' -import { federationLogger as logger } from '@/server/logger' +import { getLogger } from 'log4js' +import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.' import { SendCoinsArgsLoggingView } from '../logger/SendCoinsArgsLogging.view' import { SendCoinsArgs } from '../model/SendCoinsArgs' +const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.storeForeignUser`) + export async function storeForeignUser(args: SendCoinsArgs): Promise { if (args.senderCommunityUuid !== null && args.senderUserUuid !== null) { try { diff --git a/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.test.ts b/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.test.ts index 2c2b3bb48..9d543eec5 100644 --- a/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.test.ts +++ b/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.test.ts @@ -1,6 +1,8 @@ import { CONFIG } from '@/config' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { createServer } from '@/server/createServer' import { createTestClient } from 'apollo-server-testing' +import { getLogger } from 'log4js' import { FederatedCommunity as DbFederatedCommunity } from 'database' let query: any @@ -11,7 +13,7 @@ let con: any CONFIG.FEDERATION_API = '1_1' beforeAll(async () => { - const server = await createServer() + const server = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`)) con = server.con query = createTestClient(server.apollo).query DbFederatedCommunity.clear() diff --git a/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.ts b/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.ts index 6e621eb59..181ccb78c 100644 --- a/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.ts +++ b/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.ts @@ -1,13 +1,16 @@ -import { federationLogger as logger } from '@/server/logger' import { FederatedCommunity as DbFederatedCommunity } from 'database' +import { getLogger } from 'log4js' import { Query, Resolver } from 'type-graphql' +import { LOG4JS_RESOLVER_1_1_CATEGORY_NAME } from '.' import { GetPublicKeyResult } from '../../1_0/model/GetPublicKeyResult' +const logger = getLogger(`${LOG4JS_RESOLVER_1_1_CATEGORY_NAME}.PublicKeyResolver`) + @Resolver() export class PublicKeyResolver { @Query(() => GetPublicKeyResult) async getPublicKey(): Promise { - logger.debug(`getPublicKey() via apiVersion=1_0 ...`) + logger.debug(`getPublicKey()...`) const homeCom = await DbFederatedCommunity.findOneOrFail({ where: { foreign: false, @@ -15,7 +18,7 @@ export class PublicKeyResolver { }, }) const publicKeyHex = homeCom.publicKey.toString('hex') - logger.debug(`getPublicKey()-1_1... return publicKey=${publicKeyHex}`) + logger.debug(`getPublicKey()... return publicKey=${publicKeyHex}`) return new GetPublicKeyResult(publicKeyHex) } } diff --git a/federation/src/graphql/api/1_1/resolver/index.ts b/federation/src/graphql/api/1_1/resolver/index.ts new file mode 100644 index 000000000..f2b9283eb --- /dev/null +++ b/federation/src/graphql/api/1_1/resolver/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_API_CATEGORY_NAME } from '@/graphql/api' + +export const LOG4JS_RESOLVER_1_1_CATEGORY_NAME = `${LOG4JS_API_CATEGORY_NAME}.1_1.resolver` diff --git a/federation/src/graphql/api/index.ts b/federation/src/graphql/api/index.ts new file mode 100644 index 000000000..b6e810c61 --- /dev/null +++ b/federation/src/graphql/api/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_GRAPHQL_CATEGORY_NAME } from '@/graphql' + +export const LOG4JS_API_CATEGORY_NAME = `${LOG4JS_GRAPHQL_CATEGORY_NAME}.api` diff --git a/federation/src/graphql/api/schema.ts b/federation/src/graphql/api/schema.ts index 661e203d2..00ae50b21 100644 --- a/federation/src/graphql/api/schema.ts +++ b/federation/src/graphql/api/schema.ts @@ -1,12 +1,13 @@ -import { federationLogger as logger } from '@/server/logger' +import { getLogger } from 'log4js' import { NonEmptyArray } from 'type-graphql' +import { LOG4JS_API_CATEGORY_NAME } from '.' // config import { CONFIG } from '../../config' import { getApiResolvers as getApiResolvers_1_0 } from './1_0/schema' import { getApiResolvers as getApiResolvers_1_1 } from './1_1/schema' export const getApiResolvers = (): NonEmptyArray => { - logger.info(`getApiResolvers...${CONFIG.FEDERATION_API}`) + getLogger(LOG4JS_API_CATEGORY_NAME).info(`getApiResolvers...${CONFIG.FEDERATION_API}`) if (CONFIG.FEDERATION_API === '1_0') { return getApiResolvers_1_0() diff --git a/federation/src/graphql/index.ts b/federation/src/graphql/index.ts new file mode 100644 index 000000000..c3b774479 --- /dev/null +++ b/federation/src/graphql/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' + +export const LOG4JS_GRAPHQL_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.graphql` diff --git a/federation/src/graphql/util/checkTradingLevel.ts b/federation/src/graphql/util/checkTradingLevel.ts index 773e76c03..458970c9f 100644 --- a/federation/src/graphql/util/checkTradingLevel.ts +++ b/federation/src/graphql/util/checkTradingLevel.ts @@ -1,9 +1,12 @@ import { CONFIG } from '@/config' -import { federationLogger as logger } from '@/server/logger' +import { LOG4JS_GRAPHQL_UTIL_CATEGORY_NAME } from '@/graphql/util' import { Community as DbCommunity } from 'database' import { Decimal } from 'decimal.js-light' +import { getLogger } from 'log4js' export async function checkTradingLevel(homeCom: DbCommunity, amount: Decimal): Promise { + const logger = getLogger(`${LOG4JS_GRAPHQL_UTIL_CATEGORY_NAME}.checkTradingLevel`) + const tradingLevel = CONFIG.FEDERATION_TRADING_LEVEL if (homeCom.url !== tradingLevel.RECEIVER_COMMUNITY_URL) { logger.warn( diff --git a/federation/src/graphql/util/decay.ts b/federation/src/graphql/util/decay.ts index 331d3b5b4..9f2908b71 100644 --- a/federation/src/graphql/util/decay.ts +++ b/federation/src/graphql/util/decay.ts @@ -1,13 +1,17 @@ import { Decimal } from 'decimal.js-light' -import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' +import { DECAY_START_TIME } from 'config-schema' import { Decay } from '../api/1_0/model/Decay' -// TODO: externalize all those definitions and functions into an external decay library +Decimal.set({ + precision: 25, + rounding: Decimal.ROUND_HALF_UP, +}) +// TODO: externalize all those definitions and functions into an external decay library function decayFormula(value: Decimal, seconds: number): Decimal { - // TODO why do we need to convert this here to a stting to work properly? + // TODO why do we need to convert this here to a string to work properly? return value.mul( new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds).toString(), ) @@ -17,7 +21,7 @@ function calculateDecay( amount: Decimal, from: Date, to: Date, - startBlock: Date = CONFIG.DECAY_START_TIME, + startBlock: Date = DECAY_START_TIME, ): Decay { const fromMs = from.getTime() const toMs = to.getTime() diff --git a/federation/src/graphql/util/index.ts b/federation/src/graphql/util/index.ts new file mode 100644 index 000000000..d7f06f692 --- /dev/null +++ b/federation/src/graphql/util/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_GRAPHQL_CATEGORY_NAME } from '@/graphql' + +export const LOG4JS_GRAPHQL_UTIL_CATEGORY_NAME = `${LOG4JS_GRAPHQL_CATEGORY_NAME}.util` diff --git a/federation/src/index.ts b/federation/src/index.ts index 5c3cc6f89..eb6404d5c 100644 --- a/federation/src/index.ts +++ b/federation/src/index.ts @@ -1,21 +1,28 @@ import { createServer } from './server/createServer' +import { defaultCategory, initLogger } from 'config-schema' +import { getLogger } from 'log4js' // config import { CONFIG } from './config' +import { LOG4JS_BASE_CATEGORY_NAME } from './config/const' async function main() { - // biome-ignore lint/suspicious/noConsole: no logger needed fot startup infos - console.log(`FEDERATION_PORT=${CONFIG.FEDERATION_PORT}`) - // biome-ignore lint/suspicious/noConsole: no logger needed fot startup infos - console.log(`FEDERATION_API=${CONFIG.FEDERATION_API}`) - const { app } = await createServer() + // init logger + const log4jsConfigFileName = CONFIG.LOG4JS_CONFIG_PLACEHOLDER.replace('%v', CONFIG.FEDERATION_API) + initLogger( + [defaultCategory('federation', CONFIG.LOG_LEVEL), defaultCategory('apollo', CONFIG.LOG_LEVEL)], + `${CONFIG.LOG_FILES_BASE_PATH}_${CONFIG.FEDERATION_API}`, + log4jsConfigFileName, + ) + + // init server + const { app } = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`)) app.listen(CONFIG.FEDERATION_PORT, () => { - // biome-ignore lint/suspicious/noConsole: no logger needed fot startup infos - console.log(`Server is running at http://localhost:${CONFIG.FEDERATION_PORT}`) + const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}`) + logger.info(`Server is running at http://localhost:${CONFIG.FEDERATION_PORT}`) if (CONFIG.GRAPHIQL) { - // biome-ignore lint/suspicious/noConsole: no logger needed fot startup infos - console.log( + logger.info( `GraphIQL available at ${CONFIG.FEDERATION_COMMUNITY_URL}/api/${CONFIG.FEDERATION_API}`, ) } diff --git a/federation/src/server/LogError.ts b/federation/src/server/LogError.ts index fbd6e6d18..0a91b17c6 100644 --- a/federation/src/server/LogError.ts +++ b/federation/src/server/LogError.ts @@ -1,8 +1,23 @@ -import { federationLogger as logger } from './logger' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' +import { getLogger } from 'log4js' +/** + * A custom Error that logs itself immediately upon instantiation. + * + * TODO: Anti-pattern warning: + * Logging inside the constructor introduces side effects during object creation, + * which breaks separation of concerns and can lead to duplicate or unwanted logs. + * It is generally better to log errors where they are caught, not where they are thrown. + * + * @class LogError + * @extends {Error} + * @param {string} msg - The error message. + * @param {...any} details - Additional details passed to the logger. + */ export class LogError extends Error { constructor(msg: string, ...details: any[]) { super(msg) + const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.logError`) logger.error(msg, ...details) } } diff --git a/federation/src/server/createServer.ts b/federation/src/server/createServer.ts index 0575e72fc..015fbba48 100644 --- a/federation/src/server/createServer.ts +++ b/federation/src/server/createServer.ts @@ -11,15 +11,12 @@ import { plugins } from './plugins' // graphql import { schema } from '@/graphql/schema' -// webhooks -// import { elopageWebhook } from '@/webhook/elopage' - import { AppDatabase } from 'database' import { slowDown } from 'express-slow-down' import helmet from 'helmet' import { Logger } from 'log4js' import { DataSource } from 'typeorm' -import { apolloLogger } from './logger' + // i18n // import { i18n } from './localization' @@ -30,12 +27,9 @@ type ServerDef = { apollo: ApolloServer; app: Express; con: DataSource } export const createServer = async ( // context: any = serverContext, - logger: Logger = apolloLogger, + apolloLogger: Logger, // localization: i18n.I18n = i18n, ): Promise => { - logger.addContext('user', 'unknown') - logger.debug('createServer...') - // open mysql connection const db = AppDatabase.getInstance() await db.init() @@ -77,9 +71,6 @@ export const createServer = async ( // i18n // app.use(localization.init) - // Elopage Webhook - // app.post('/hook/elopage/' + CONFIG.WEBHOOK_ELOPAGE_SECRET, elopageWebhook) - // Apollo Server const apollo = new ApolloServer({ schema: await schema(), @@ -87,10 +78,8 @@ export const createServer = async ( // introspection: CONFIG.GRAPHIQL, // context, plugins, - logger, + logger: apolloLogger, }) apollo.applyMiddleware({ app, path: '/' }) - logger.debug('createServer...successful') - return { apollo, app, con: db.getDataSource() } } diff --git a/federation/src/server/logger.ts b/federation/src/server/logger.ts deleted file mode 100644 index 505c7e4d8..000000000 --- a/federation/src/server/logger.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { CONFIG } from '@/config' -import log4js from 'log4js' - -import { readFileSync } from 'fs' - -const options = JSON.parse(readFileSync(CONFIG.LOG4JS_CONFIG, 'utf-8')) - -options.categories.backend.level = CONFIG.LOG_LEVEL -options.categories.apollo.level = CONFIG.LOG_LEVEL -let filename: string = options.appenders.federation.filename -options.appenders.federation.filename = filename - .replace('%v', CONFIG.FEDERATION_API) - .replace('%p', CONFIG.FEDERATION_PORT.toString()) -filename = options.appenders.access.filename -options.appenders.access.filename = filename.replace('%p', CONFIG.FEDERATION_PORT.toString()) -filename = options.appenders.apollo.filename -options.appenders.apollo.filename = filename.replace('%p', CONFIG.FEDERATION_PORT.toString()) -filename = options.appenders.backend.filename -options.appenders.backend.filename = filename.replace('%p', CONFIG.FEDERATION_PORT.toString()) -filename = options.appenders.errorFile.filename -options.appenders.errorFile.filename = filename.replace('%p', CONFIG.FEDERATION_PORT.toString()) - -log4js.configure(options) - -const apolloLogger = log4js.getLogger('apollo') -// const backendLogger = log4js.getLogger('backend') -const federationLogger = log4js.getLogger('federation') - -// backendLogger.addContext('user', 'unknown') - -export { apolloLogger, federationLogger } diff --git a/federation/test/helpers.ts b/federation/test/helpers.ts index 9a56caedd..148ef6c54 100644 --- a/federation/test/helpers.ts +++ b/federation/test/helpers.ts @@ -3,7 +3,8 @@ import { createTestClient } from 'apollo-server-testing' import { createServer } from '@/server/createServer' -import { logger } from './testSetup' +import { getLogger } from 'config-schema/test/testSetup' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' export const headerPushMock = jest.fn((t) => { context.token = t.value @@ -25,7 +26,7 @@ export const cleanDB = async () => { } } -export const testEnvironment = async (testLogger = logger /*, testI18n = i18n */) => { +export const testEnvironment = async (testLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`) /*, testI18n = i18n */) => { const server = await createServer(/* context, */ testLogger /* , testI18n */) const con = server.con const testClient = createTestClient(server.apollo) diff --git a/federation/test/testSetup.ts b/federation/test/testSetup.ts deleted file mode 100644 index 85008799f..000000000 --- a/federation/test/testSetup.ts +++ /dev/null @@ -1,43 +0,0 @@ -// import { CONFIG } from '@/config' -// import { i18n } from '@/server/localization' -import { federationLogger as logger } from '@/server/logger' - -// CONFIG.EMAIL = true -// CONFIG.EMAIL_TEST_MODUS = false - -jest.setTimeout(1000000) - -jest.mock('@/server/logger', () => { - const originalModule = jest.requireActual('@/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('@/server/localization') - return { - __esModule: true, - ...originalModule, - i18n: { - init: jest.fn(), - // configure: jest.fn(), - // __: jest.fn(), - // setLocale: jest.fn(), - }, - } -}) -*/ - -export { logger } From bfa93670505134c76097a99907ba0d6f990160ec Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 17 Jun 2025 15:32:51 +0200 Subject: [PATCH 10/46] integrate new logging in backend, update some logging messages --- backend/Dockerfile | 2 - backend/esbuild.config.ts | 1 + backend/jest.config.js | 2 +- backend/log4js-config.json | 173 ------------ backend/package.json | 6 +- backend/src/apis/HttpRequest.ts | 5 +- backend/src/apis/KlicktippController.ts | 5 +- .../dltConnector/DltConnectorClient.test.ts | 3 +- .../apis/dltConnector/DltConnectorClient.ts | 5 +- backend/src/apis/gms/ExportUsers.ts | 10 +- backend/src/apis/gms/GmsClient.ts | 5 +- backend/src/apis/gms/index.ts | 3 + backend/src/apis/humhub/ExportUsers.ts | 15 +- backend/src/apis/humhub/HumHubClient.ts | 5 +- backend/src/apis/humhub/index.ts | 3 + backend/src/apis/humhub/syncUser.ts | 5 +- backend/src/apis/index.ts | 3 + backend/src/apis/openai/OpenaiClient.ts | 6 +- backend/src/auth/jwt/JWT.ts | 10 +- backend/src/config/const.ts | 1 + backend/src/config/index.test.ts | 9 - backend/src/config/index.ts | 21 +- backend/src/config/schema.ts | 2 + backend/src/emails/index.ts | 3 + .../src/emails/sendEmailTranslated.test.ts | 6 +- backend/src/emails/sendEmailTranslated.ts | 9 +- backend/src/emails/sendEmailVariants.test.ts | 6 +- .../src/federation/authenticateCommunities.ts | 5 +- .../client/1_0/AuthenticationClient.ts | 19 +- .../federation/client/1_0/FederationClient.ts | 24 +- .../federation/client/1_0/SendCoinsClient.ts | 87 ++---- backend/src/federation/client/1_0/index.ts | 3 + backend/src/federation/index.ts | 3 + .../federation/validateCommunities.test.ts | 67 ++--- backend/src/federation/validateCommunities.ts | 32 +-- .../src/graphql/resolver/BalanceResolver.ts | 6 +- .../resolver/CommunityResolver.test.ts | 6 +- .../resolver/ContributionLinkResolver.test.ts | 17 +- .../ContributionMessageResolver.test.ts | 38 ++- .../resolver/ContributionMessageResolver.ts | 7 +- .../resolver/ContributionResolver.test.ts | 9 +- .../graphql/resolver/ContributionResolver.ts | 19 +- .../graphql/resolver/EmailOptinCodes.test.ts | 1 + backend/src/graphql/resolver/GdtResolver.ts | 4 +- .../resolver/KlicktippResolver.test.ts | 6 +- .../resolver/ProjectBrandingResolver.ts | 5 +- .../resolver/TransactionLinkResolver.test.ts | 37 +-- .../resolver/TransactionLinkResolver.ts | 23 +- .../resolver/TransactionResolver.test.ts | 13 +- .../graphql/resolver/TransactionResolver.ts | 64 +++-- .../src/graphql/resolver/UserResolver.test.ts | 117 ++++---- backend/src/graphql/resolver/UserResolver.ts | 253 ++++++++++++------ backend/src/graphql/resolver/index.ts | 3 + .../util/authenticateGmsUserPlayground.ts | 7 +- .../util/compareGmsRelevantUserSettings.ts | 7 +- .../src/graphql/resolver/util/creations.ts | 4 +- .../resolver/util/getKlicktippState.ts | 7 +- backend/src/graphql/resolver/util/index.ts | 3 + .../resolver/util/processXComSendCoins.ts | 46 ++-- .../sendTransactionsToDltConnector.test.ts | 10 +- .../util/sendTransactionsToDltConnector.ts | 9 +- .../graphql/resolver/util/sendUserToGms.ts | 5 +- .../util/settlePendingSenderTransaction.ts | 9 +- .../graphql/resolver/util/storeForeignUser.ts | 15 +- .../graphql/resolver/util/syncHumhub.test.ts | 10 +- .../src/graphql/resolver/util/syncHumhub.ts | 7 +- .../resolver/util/validateAlias.test.ts | 8 +- backend/src/index.ts | 6 +- backend/src/interactions/index.ts | 3 + .../AbstractUnconfirmedContribution.role.ts | 5 + .../UnconfirmedContributionAdmin.role.ts | 4 +- ...nfirmedContributionAdminAddMessage.role.ts | 3 +- .../UnconfirmedContributionUser.role.ts | 3 +- ...onfirmedContributionUserAddMessage.role.ts | 3 +- backend/src/password/EncryptorUtils.ts | 2 - backend/src/password/PasswordEncryptor.ts | 1 - .../src/password/__mocks__/EncryptorUtils.ts | 5 +- backend/src/seeds/index.ts | 7 +- backend/src/server/LogError.test.ts | 5 +- backend/src/server/LogError.ts | 18 +- backend/src/server/createServer.ts | 10 +- backend/src/server/localization.ts | 10 +- backend/src/server/logger.ts | 34 ++- backend/src/util/Monitor.ts | 12 +- backend/src/util/decay.ts | 9 +- backend/src/util/time.ts | 19 ++ backend/src/webhook/elopage.ts | 5 +- backend/src/webhook/gms.ts | 4 +- backend/test/helpers.ts | 8 +- backend/test/testSetup.ts | 21 +- backend/tsconfig.json | 2 +- backend/turbo.json | 3 + 92 files changed, 793 insertions(+), 748 deletions(-) delete mode 100644 backend/log4js-config.json create mode 100644 backend/src/apis/gms/index.ts create mode 100644 backend/src/apis/humhub/index.ts create mode 100644 backend/src/apis/index.ts create mode 100644 backend/src/config/const.ts delete mode 100644 backend/src/config/index.test.ts create mode 100644 backend/src/emails/index.ts create mode 100644 backend/src/federation/client/1_0/index.ts create mode 100644 backend/src/federation/index.ts create mode 100644 backend/src/graphql/resolver/index.ts create mode 100644 backend/src/graphql/resolver/util/index.ts create mode 100644 backend/src/interactions/index.ts diff --git a/backend/Dockerfile b/backend/Dockerfile index 4c1ceb36e..4ab2f1883 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -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 diff --git a/backend/esbuild.config.ts b/backend/esbuild.config.ts index 87efef48e..9cdd51cc1 100644 --- a/backend/esbuild.config.ts +++ b/backend/esbuild.config.ts @@ -13,4 +13,5 @@ build({ external: ['sodium-native', 'email-templates'], plugins: [esbuildDecorators()], minify: true, + sourcemap: true, }) diff --git a/backend/jest.config.js b/backend/jest.config.js index 87f32599d..ddf94f977 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -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: { diff --git a/backend/log4js-config.json b/backend/log4js-config.json deleted file mode 100644 index 0807e6a12..000000000 --- a/backend/log4js-config.json +++ /dev/null @@ -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" - } - } -} diff --git a/backend/package.json b/backend/package.json index 1ab660238..8673ff42d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -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,7 +21,7 @@ "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": { @@ -47,6 +47,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 +86,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", diff --git a/backend/src/apis/HttpRequest.ts b/backend/src/apis/HttpRequest.ts index ef6f540b7..0b63c4af9 100644 --- a/backend/src/apis/HttpRequest.ts +++ b/backend/src/apis/HttpRequest.ts @@ -1,7 +1,10 @@ import axios from 'axios' +import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis' import { LogError } from '@/server/LogError' -import { backendLogger as logger } from '@/server/logger' +import { getLogger } from 'log4js' + +const logger = getLogger(`${LOG4JS_APIS_CATEGORY_NAME}.HttpRequest`) import { httpAgent, httpsAgent } from './ConnectionAgents' diff --git a/backend/src/apis/KlicktippController.ts b/backend/src/apis/KlicktippController.ts index cb665ea7f..5fe55fb2e 100644 --- a/backend/src/apis/KlicktippController.ts +++ b/backend/src/apis/KlicktippController.ts @@ -1,9 +1,10 @@ +import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis' 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_APIS_CATEGORY_NAME}.KlicktippController`) export const subscribe = async ( email: string, diff --git a/backend/src/apis/dltConnector/DltConnectorClient.test.ts b/backend/src/apis/dltConnector/DltConnectorClient.test.ts index 0367c6350..00b15348d 100644 --- a/backend/src/apis/dltConnector/DltConnectorClient.test.ts +++ b/backend/src/apis/dltConnector/DltConnectorClient.test.ts @@ -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() }) diff --git a/backend/src/apis/dltConnector/DltConnectorClient.ts b/backend/src/apis/dltConnector/DltConnectorClient.ts index 2bebc84c0..8eb8de3fb 100644 --- a/backend/src/apis/dltConnector/DltConnectorClient.ts +++ b/backend/src/apis/dltConnector/DltConnectorClient.ts @@ -1,14 +1,17 @@ import { Transaction as DbTransaction } from 'database' import { GraphQLClient, gql } from 'graphql-request' +import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis/index' import { CONFIG } from '@/config' 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_APIS_CATEGORY_NAME}.dltConnector`) + const sendTransaction = gql` mutation ($input: TransactionInput!) { sendTransaction(data: $input) { diff --git a/backend/src/apis/gms/ExportUsers.ts b/backend/src/apis/gms/ExportUsers.ts index 981f9c90e..fb7cc81dc 100644 --- a/backend/src/apis/gms/ExportUsers.ts +++ b/backend/src/apis/gms/ExportUsers.ts @@ -1,14 +1,18 @@ import { User as DbUser } from 'database' // import { createTestClient } from 'apollo-server-testing' +import { LOG4JS_GMS_CATEGORY_NAME } from '@/apis/gms/index' // 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_GMS_CATEGORY_NAME}.ExportUsers`) CONFIG.EMAIL = false // use force to copy over all user even if gmsRegistered is set to true @@ -70,7 +74,7 @@ async function main() { } main().catch((e) => { - // biome-ignore lint/suspicious/noConsole: logger isn't used here - console.error(e) + initLogging() + logger.error(e) process.exit(1) }) diff --git a/backend/src/apis/gms/GmsClient.ts b/backend/src/apis/gms/GmsClient.ts index 537fe36f2..0dd5eccc5 100644 --- a/backend/src/apis/gms/GmsClient.ts +++ b/backend/src/apis/gms/GmsClient.ts @@ -1,13 +1,16 @@ import axios from 'axios' import { httpAgent, httpsAgent } from '@/apis/ConnectionAgents' +import { LOG4JS_GMS_CATEGORY_NAME } from '@/apis/gms/index' 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_GMS_CATEGORY_NAME}.GmsClient`) + /* export async function communityList(): Promise { const baseUrl = ensureUrlEndsWithSlash(CONFIG.GMS_URL) diff --git a/backend/src/apis/gms/index.ts b/backend/src/apis/gms/index.ts new file mode 100644 index 000000000..620aaca4b --- /dev/null +++ b/backend/src/apis/gms/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis' + +export const LOG4JS_GMS_CATEGORY_NAME = `${LOG4JS_APIS_CATEGORY_NAME}.gms` diff --git a/backend/src/apis/humhub/ExportUsers.ts b/backend/src/apis/humhub/ExportUsers.ts index dc1f4fb5d..c0746ceb4 100644 --- a/backend/src/apis/humhub/ExportUsers.ts +++ b/backend/src/apis/humhub/ExportUsers.ts @@ -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_HUMHUB_CATEGORY_NAME } from '@/apis/humhub/index' +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_HUMHUB_CATEGORY_NAME}.ExportUsers`) function getUsersPage(page: number, limit: number): Promise<[User[], number]> { return User.findAndCount({ @@ -34,7 +35,7 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise { - // biome-ignore lint/suspicious/noConsole: logger isn't used here - console.error(e) + initLogging() + logger.error(e) process.exit(1) }) diff --git a/backend/src/apis/humhub/HumHubClient.ts b/backend/src/apis/humhub/HumHubClient.ts index daa19b5b8..ff4529234 100644 --- a/backend/src/apis/humhub/HumHubClient.ts +++ b/backend/src/apis/humhub/HumHubClient.ts @@ -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_HUMHUB_CATEGORY_NAME } from '@/apis/humhub/index' +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_HUMHUB_CATEGORY_NAME}.HumHubClient`) + /** * HumHubClient as singleton class */ diff --git a/backend/src/apis/humhub/index.ts b/backend/src/apis/humhub/index.ts new file mode 100644 index 000000000..dda319145 --- /dev/null +++ b/backend/src/apis/humhub/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis' + +export const LOG4JS_HUMHUB_CATEGORY_NAME = `${LOG4JS_APIS_CATEGORY_NAME}.humhub` diff --git a/backend/src/apis/humhub/syncUser.ts b/backend/src/apis/humhub/syncUser.ts index 1e62871be..257f16683 100644 --- a/backend/src/apis/humhub/syncUser.ts +++ b/backend/src/apis/humhub/syncUser.ts @@ -1,13 +1,16 @@ import { User } from 'database' +import { LOG4JS_HUMHUB_CATEGORY_NAME } from '@/apis/humhub/index' 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_HUMHUB_CATEGORY_NAME}.syncUser`) + export enum ExecutedHumhubAction { UPDATE, CREATE, diff --git a/backend/src/apis/index.ts b/backend/src/apis/index.ts new file mode 100644 index 000000000..c4e45826d --- /dev/null +++ b/backend/src/apis/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' + +export const LOG4JS_APIS_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.apis` diff --git a/backend/src/apis/openai/OpenaiClient.ts b/backend/src/apis/openai/OpenaiClient.ts index b2a859581..dd447c7ad 100644 --- a/backend/src/apis/openai/OpenaiClient.ts +++ b/backend/src/apis/openai/OpenaiClient.ts @@ -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_APIS_CATEGORY_NAME } from '@/apis' +import { getLogger } from 'log4js' + +const logger = getLogger(`${LOG4JS_APIS_CATEGORY_NAME}.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. diff --git a/backend/src/auth/jwt/JWT.ts b/backend/src/auth/jwt/JWT.ts index 6f6581773..07384c03b 100644 --- a/backend/src/auth/jwt/JWT.ts +++ b/backend/src/auth/jwt/JWT.ts @@ -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 => { if (!token) { throw new LogError('401 Unauthorized') diff --git a/backend/src/config/const.ts b/backend/src/config/const.ts new file mode 100644 index 000000000..68bd124a8 --- /dev/null +++ b/backend/src/config/const.ts @@ -0,0 +1 @@ +export const LOG4JS_BASE_CATEGORY_NAME = 'backend' diff --git a/backend/src/config/index.test.ts b/backend/src/config/index.test.ts deleted file mode 100644 index 24908513a..000000000 --- a/backend/src/config/index.test.ts +++ /dev/null @@ -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')) - }) - }) -}) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index f29f5ed4b..9d61c2da4 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -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, diff --git a/backend/src/config/schema.ts b/backend/src/config/schema.ts index f805d02a7..2fb576bdd 100644 --- a/backend/src/config/schema.ts +++ b/backend/src/config/schema.ts @@ -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, diff --git a/backend/src/emails/index.ts b/backend/src/emails/index.ts new file mode 100644 index 000000000..d529ac28d --- /dev/null +++ b/backend/src/emails/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' + +export const LOG4JS_EMAILS_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.emails` diff --git a/backend/src/emails/sendEmailTranslated.test.ts b/backend/src/emails/sendEmailTranslated.test.ts index 917d80ea6..b4da5fbfb 100644 --- a/backend/src/emails/sendEmailTranslated.test.ts +++ b/backend/src/emails/sendEmailTranslated.test.ts @@ -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_EMAILS_CATEGORY_NAME } from '.' import { sendEmailTranslated } from './sendEmailTranslated' +const logger = getLogger(`${LOG4JS_EMAILS_CATEGORY_NAME}.sendEmailTranslated`) + const testMailServerHost = 'localhost' const testMailServerPort = 1025 diff --git a/backend/src/emails/sendEmailTranslated.ts b/backend/src/emails/sendEmailTranslated.ts index ae52b3975..30050abf3 100644 --- a/backend/src/emails/sendEmailTranslated.ts +++ b/backend/src/emails/sendEmailTranslated.ts @@ -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_EMAILS_CATEGORY_NAME } from '@/emails' +import { getLogger } from 'log4js' + +const logger = getLogger(`${LOG4JS_EMAILS_CATEGORY_NAME}.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')}`, ) diff --git a/backend/src/emails/sendEmailVariants.test.ts b/backend/src/emails/sendEmailVariants.test.ts index 2c2885ebf..b264ddbf5 100644 --- a/backend/src/emails/sendEmailVariants.test.ts +++ b/backend/src/emails/sendEmailVariants.test.ts @@ -3,7 +3,11 @@ 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 { LOG4JS_EMAILS_CATEGORY_NAME } from '.' + +const logger = getLogger(`${LOG4JS_EMAILS_CATEGORY_NAME}.sendEmailTranslated`) import { CONFIG } from '@/config' diff --git a/backend/src/federation/authenticateCommunities.ts b/backend/src/federation/authenticateCommunities.ts index c4263445b..2a1c6f51d 100644 --- a/backend/src/federation/authenticateCommunities.ts +++ b/backend/src/federation/authenticateCommunities.ts @@ -4,12 +4,15 @@ 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 { LOG4JS_FEDERATION_CATEGORY_NAME } from '@/federation' +import { getLogger } from 'log4js' import { OpenConnectionArgs } from './client/1_0/model/OpenConnectionArgs' import { AuthenticationClientFactory } from './client/AuthenticationClientFactory' +const logger = getLogger(`${LOG4JS_FEDERATION_CATEGORY_NAME}.authenticateCommunities`) + export async function startCommunityAuthentication( foreignFedCom: DbFederatedCommunity, ): Promise { diff --git a/backend/src/federation/client/1_0/AuthenticationClient.ts b/backend/src/federation/client/1_0/AuthenticationClient.ts index 264afe3a0..38996433f 100644 --- a/backend/src/federation/client/1_0/AuthenticationClient.ts +++ b/backend/src/federation/client/1_0/AuthenticationClient.ts @@ -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_FEDERATION_CLIENT1_0_CATEGORY_NAME } from '@/federation/client/1_0' +import { getLogger } from 'log4js' import { OpenConnectionArgs } from './model/OpenConnectionArgs' import { openConnection } from './query/openConnection' +const logger = getLogger(`${LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME}.AuthenticationClient`) + export class AuthenticationClient { dbCom: DbFederatedCommunity endpoint: string @@ -25,25 +28,19 @@ export class AuthenticationClient { } async openConnection(args: OpenConnectionArgs): Promise { - 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) } } } diff --git a/backend/src/federation/client/1_0/FederationClient.ts b/backend/src/federation/client/1_0/FederationClient.ts index b83da8a8b..b548a97e1 100644 --- a/backend/src/federation/client/1_0/FederationClient.ts +++ b/backend/src/federation/client/1_0/FederationClient.ts @@ -1,15 +1,18 @@ import { FederatedCommunity as DbFederatedCommunity } from 'database' import { GraphQLClient } from 'graphql-request' +import { LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME } from '@/federation/client/1_0' 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_FEDERATION_CLIENT1_0_CATEGORY_NAME}.FederationClient`) + export class FederationClient { dbCom: DbFederatedCommunity endpoint: string @@ -32,25 +35,25 @@ export class FederationClient { } getPublicKey = async (): Promise => { - 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 => { - 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) + '...', }) diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index 91e7b827c..cc0c74846 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -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_FEDERATION_CLIENT1_0_CATEGORY_NAME } from '.' 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_FEDERATION_CLIENT1_0_CATEGORY_NAME}.SendCoinsClient`) + export class SendCoinsClient { dbCom: DbFederatedCommunity endpoint: string @@ -32,123 +35,87 @@ export class SendCoinsClient { } async voteForSendCoins(args: SendCoinsArgs): Promise { - 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 { - 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 { - 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 { - 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) } } } diff --git a/backend/src/federation/client/1_0/index.ts b/backend/src/federation/client/1_0/index.ts new file mode 100644 index 000000000..c21700126 --- /dev/null +++ b/backend/src/federation/client/1_0/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_FEDERATION_CATEGORY_NAME } from '@/federation' + +export const LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME = `${LOG4JS_FEDERATION_CATEGORY_NAME}.client.1_0` diff --git a/backend/src/federation/index.ts b/backend/src/federation/index.ts new file mode 100644 index 000000000..c1684852b --- /dev/null +++ b/backend/src/federation/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' + +export const LOG4JS_FEDERATION_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.federation` diff --git a/backend/src/federation/validateCommunities.test.ts b/backend/src/federation/validateCommunities.test.ts index 731ca72a1..bfb43fd35 100644 --- a/backend/src/federation/validateCommunities.test.ts +++ b/backend/src/federation/validateCommunities.test.ts @@ -4,11 +4,16 @@ import { GraphQLClient } from 'graphql-request' import { Response } from 'graphql-request/dist/types' import { DataSource } from 'typeorm' +import { LOG4JS_FEDERATION_CATEGORY_NAME } from '@/federation' +import { LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME } from '@/federation/client/1_0' import { cleanDB, testEnvironment } from '@test/helpers' -import { logger } from '@test/testSetup' +import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' import { validateCommunities } from './validateCommunities' +const logger = getLogger(`${LOG4JS_FEDERATION_CATEGORY_NAME}.validateCommunities`) +const federationClientLogger = getLogger(`${LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME}.FederationClient`) + let con: DataSource let testEnv: { mutate: ApolloServerTestClient['mutate'] @@ -49,7 +54,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,13 +84,12 @@ 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', - 'http//localhost:5001/api/1_0/', - ) + expect(federationClientLogger.warn).toBeCalledWith( + 'getPublicKey without response data from endpoint', 'http//localhost:5001/api/1_0/' + ) }) }) @@ -153,17 +157,14 @@ 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', - 'http//localhost:5001/api/1_0/', - ) + 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 +204,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,19 +270,16 @@ 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', - 'http//localhost:5001/api/1_1/', - ) + expect(federationClientLogger.debug).toBeCalledWith('getPublicKey from endpoint', 'http//localhost:5001/api/1_1/') }) }) describe('with three Communities of api 1_0, 1_1 and 2_0', () => { @@ -321,26 +319,19 @@ 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', - 'http//localhost:5001/api/1_1/', - ) + 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.endPoint, - '2_0', - ) + expect(logger.debug).toBeCalledWith('dbCom with unsupported apiVersion', dbCom.endPoint, '2_0') }) }) }) diff --git a/backend/src/federation/validateCommunities.ts b/backend/src/federation/validateCommunities.ts index 2a1bc630e..dc4747352 100644 --- a/backend/src/federation/validateCommunities.ts +++ b/backend/src/federation/validateCommunities.ts @@ -5,23 +5,23 @@ import { } from 'database' import { IsNull } from 'typeorm' +import { LOG4JS_FEDERATION_CATEGORY_NAME } from '@/federation' import { FederationClient as V1_0_FederationClient } from '@/federation/client/1_0/FederationClient' import { PublicCommunityInfo } from '@/federation/client/1_0/model/PublicCommunityInfo' import { FederationClientFactory } from '@/federation/client/FederationClientFactory' import { 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' +const logger = getLogger(`${LOG4JS_FEDERATION_CATEGORY_NAME}.validateCommunities`) + export async function startValidateCommunities(timerInterval: number): Promise { 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 { .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 { 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) { diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index 2ea34cc5a..f3c7d4709 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -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_RESOLVER_CATEGORY_NAME } from '.' 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 { const user = getUser(context) const now = new Date() + const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.BalanceResolver`) logger.addContext('user', user.id) - logger.info(`balance(userId=${user.id})...`) + logger.info(`balance...`) let balanceGDT if (!context.balanceGDT) { diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index f636bf53d..428915765 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -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,14 @@ import { } from '@/seeds/graphql/queries' import { peterLustig } from '@/seeds/users/peter-lustig' +import { getLogger } from 'config-schema/test/testSetup' +import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' import { getCommunityByUuid } from './util/communities' jest.mock('@/password/EncryptorUtils') +const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.CommunityResolver`) + // to do: We need a setup for the tests that closes the connection let mutate: ApolloServerTestClient['mutate'] let query: ApolloServerTestClient['query'] diff --git a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts index 9c9bdfa55..bf8c37126 100644 --- a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts @@ -5,7 +5,6 @@ import { GraphQLError } from 'graphql' import { DataSource } from 'typeorm' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' -import { logger } from '@test/testSetup' import { EventType } from '@/event/Events' import { userFactory } from '@/seeds/factory/user' @@ -18,9 +17,15 @@ import { import { listContributionLinks } from '@/seeds/graphql/queries' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' +import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' +import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' jest.mock('@/password/EncryptorUtils') +const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ContributionLinkResolver`) +const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) + let mutate: ApolloServerTestClient['mutate'] let query: ApolloServerTestClient['query'] let con: DataSource @@ -286,7 +291,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 +312,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 +336,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 +536,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 +618,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) }) }) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 00b7032dc..16ad4dbb2 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -5,7 +5,7 @@ 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 { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' import { EventType } from '@/event/Events' @@ -20,6 +20,14 @@ 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 { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' +import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' +import { LOG4JS_INTERACTION_CATEGORY_NAME } from '@/interactions' + +const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ContributionMessageResolver`) +const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) +const interactionLogger = getLogger(`${LOG4JS_INTERACTION_CATEGORY_NAME}.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,11 +156,7 @@ describe('ContributionMessageResolver', () => { message: 'Test', }, }) - expect(logger.debug).toBeCalledTimes(5) - expect(logger.debug).toHaveBeenNthCalledWith( - 5, - 'use UnconfirmedContributionUserAddMessageRole', - ) + expect(interactionLogger.debug).toBeCalledWith('use UnconfirmedContributionUserAddMessageRole') expect(mutationResult).toEqual( expect.objectContaining({ data: { @@ -325,7 +329,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'), ) @@ -346,11 +350,7 @@ describe('ContributionMessageResolver', () => { }, }) - expect(logger.debug).toBeCalledTimes(5) - expect(logger.debug).toHaveBeenNthCalledWith( - 5, - 'use UnconfirmedContributionAdminAddMessageRole', - ) + expect(interactionLogger.debug).toBeCalledWith('use UnconfirmedContributionAdminAddMessageRole') expect(mutationResult).toEqual( expect.objectContaining({ @@ -380,12 +380,7 @@ describe('ContributionMessageResolver', () => { message: 'Test', }, }) - - expect(logger.debug).toBeCalledTimes(5) - expect(logger.debug).toHaveBeenNthCalledWith( - 5, - 'use UnconfirmedContributionAdminAddMessageRole', - ) + expect(interactionLogger.debug).toBeCalledWith('use UnconfirmedContributionAdminAddMessageRole') expect(mutationResult).toEqual( expect.objectContaining({ @@ -399,13 +394,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'), diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index c1d25bd19..4444a0ef0 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -22,11 +22,12 @@ 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_RESOLVER_CATEGORY_NAME } from '.' import { findContributionMessages } from './util/findContributionMessages' const db = AppDatabase.getInstance() +const createLogger = () => getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ContributionMessageResolver`) @Resolver() export class ContributionMessageResolver { @@ -125,7 +126,9 @@ export class ContributionMessageResolver { @Args() contributionMessageArgs: ContributionMessageArgs, @Ctx() context: Context, ): Promise { + const logger = createLogger() const { contributionId, messageType } = contributionMessageArgs + logger.addContext('contribution', contributionMessageArgs.contributionId) const updateUnconfirmedContributionContext = new UpdateUnconfirmedContributionContext( contributionId, contributionMessageArgs, diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 02ec62748..77e879f8d 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -14,7 +14,7 @@ import { resetToken, testEnvironment, } from '@test/helpers' -import { i18n as localization, logger } from '@test/testSetup' +import { i18n as localization } from '@test/testSetup' import { sendContributionConfirmedEmail, @@ -50,10 +50,15 @@ 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, clearLogs, printLogs } from 'config-schema/test/testSetup' +import { getLogger as originalGetLogger } from 'log4js' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' 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 diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 32bb0ff82..328a70dc7 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -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_RESOLVER_CATEGORY_NAME } from '@/graphql/resolver' +import { ContributionMessageType } from '@enum/ContributionMessageType' import { AppDatabase } from 'database' -import { ContributionMessageType } from '../enum/ContributionMessageType' +import { getLogger } from 'log4js' import { loadAllContributions, loadUserContributions } from './util/contributions' import { getOpenCreations, getUserCreation, validateContribution } from './util/creations' import { extractGraphQLFields } from './util/extractGraphQLFields' @@ -58,6 +59,7 @@ import { getLastTransaction } from './util/getLastTransaction' import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' const db = AppDatabase.getInstance() +const createLogger = () => getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ContributionResolver`) @Resolver(() => Contribution) export class ContributionResolver { @@ -81,6 +83,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) @@ -211,6 +215,8 @@ export class ContributionResolver { @Args() { email, amount, memo, creationDate }: AdminCreateContributionArgs, @Ctx() context: Context, ): Promise { + const logger = createLogger() + logger.addContext('admin', context.user?.id) logger.info( `adminCreateContribution(email=${email}, amount=${amount.toString()}, memo=${memo}, creationDate=${creationDate})`, ) @@ -262,6 +268,8 @@ export class ContributionResolver { @Args() adminUpdateContributionArgs: AdminUpdateContributionArgs, @Ctx() context: Context, ): Promise { + const logger = createLogger() + logger.addContext('contribution', adminUpdateContributionArgs.id) const updateUnconfirmedContributionContext = new UpdateUnconfirmedContributionContext( adminUpdateContributionArgs.id, adminUpdateContributionArgs, @@ -273,7 +281,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(), @@ -284,7 +291,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, @@ -416,6 +422,9 @@ export class ContributionResolver { @Arg('id', () => Int) id: number, @Ctx() context: Context, ): Promise { + const logger = createLogger() + logger.addContext('contribution', id) + // acquire lock const releaseLock = await TRANSACTIONS_LOCK.acquire() try { diff --git a/backend/src/graphql/resolver/EmailOptinCodes.test.ts b/backend/src/graphql/resolver/EmailOptinCodes.test.ts index 37bf6cc8b..b916d23b6 100644 --- a/backend/src/graphql/resolver/EmailOptinCodes.test.ts +++ b/backend/src/graphql/resolver/EmailOptinCodes.test.ts @@ -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({ diff --git a/backend/src/graphql/resolver/GdtResolver.ts b/backend/src/graphql/resolver/GdtResolver.ts index 933fdb397..9d7451ed8 100644 --- a/backend/src/graphql/resolver/GdtResolver.ts +++ b/backend/src/graphql/resolver/GdtResolver.ts @@ -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_RESOLVER_CATEGORY_NAME } from '.' -import { backendLogger as logger } from '@/server/logger' +const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.GdtResolver`) @Resolver() export class GdtResolver { diff --git a/backend/src/graphql/resolver/KlicktippResolver.test.ts b/backend/src/graphql/resolver/KlicktippResolver.test.ts index 6e6cdefca..10ef6a760 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.test.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.test.ts @@ -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_RESOLVER_CATEGORY_NAME } from '.' jest.mock('@/password/EncryptorUtils') +const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.KlicktippResolver`) + let testEnv: any let mutate: any let con: any diff --git a/backend/src/graphql/resolver/ProjectBrandingResolver.ts b/backend/src/graphql/resolver/ProjectBrandingResolver.ts index 3742ac2c4..a7ef1c8b8 100644 --- a/backend/src/graphql/resolver/ProjectBrandingResolver.ts +++ b/backend/src/graphql/resolver/ProjectBrandingResolver.ts @@ -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_RESOLVER_CATEGORY_NAME } from '.' + +const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ProjectBrandingResolver`) @Resolver(() => ProjectBranding) export class ProjectBrandingResolver { diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index 92c147389..f0107dcdd 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -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,14 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' +import { getLogger } from 'config-schema/test/testSetup' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' +import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' import { transactionLinkCode } from './TransactionLinkResolver' +const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.TransactionLinkResolver`) +const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) + jest.mock('@/password/EncryptorUtils') // mock semaphore to allow use fake timers @@ -221,7 +226,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 +278,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 +322,8 @@ 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 +361,8 @@ 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 +400,8 @@ 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 +496,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 +571,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 +623,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 +657,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 +728,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 +932,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) }) }) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 01be0fef1..34c073e45 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -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_RESOLVER_CATEGORY_NAME } from '.' 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_RESOLVER_CATEGORY_NAME}.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 { - 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 { + 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 { + 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 { + 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 { + async queryRedeemJwtLink(code: string, logger: Logger): Promise { 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 { + const logger = createLogger() + logger.addContext('code', code.substring(0, 6)) logger.debug('TransactionLinkResolver.createDisburseJwt... args=', { senderCommunityUuid, senderGradidoId, diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index d164cd5a8..dd0983efc 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -8,11 +8,10 @@ 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 { EventType } from '@/event/Events' @@ -32,16 +31,20 @@ 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' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' 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({ diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index b842a2d34..511c7167d 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -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_RESOLVER_CATEGORY_NAME } from '.' 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_RESOLVER_CATEGORY_NAME}.TransactionResolver`) export const executeTransaction = async ( amount: Decimal, memo: string, sender: dbUser, recipient: dbUser, + logger: Logger, transactionLink?: dbTransactionLink | null, ): Promise => { // acquire lock const releaseLock = await TRANSACTIONS_LOCK.acquire() + try { logger.info('executeTransaction', amount, memo, sender, recipient) @@ -228,9 +232,9 @@ export class TransactionResolver { ): Promise { 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 = 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 { + 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 diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index e6c38c442..0edcf30cf 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -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,7 +67,10 @@ import { stephenHawking } from '@/seeds/users/stephen-hawking' import { printTimeDuration } from '@/util/time' import { objectValuesToArray } from '@/util/utilities' +import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' +import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' import { Location2Point } from './util/Location2Point' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' jest.mock('@/apis/humhub/HumHubClient') jest.mock('@/password/EncryptorUtils') @@ -93,6 +96,9 @@ jest.mock('@/apis/KlicktippController', () => { } }) +const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.UserResolver`) +const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) + CONFIG.EMAIL_CODE_REQUEST_TIME = 10 let admin: User @@ -275,7 +281,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 +649,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 +679,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 +700,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 +709,7 @@ 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 +790,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 +821,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 +850,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 +877,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 +1104,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 +1113,8 @@ 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 +1364,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 +1393,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 +1420,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 +1478,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 +1762,7 @@ 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 +1878,7 @@ 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 +1923,7 @@ 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 +1947,7 @@ 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 +1968,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 +2041,7 @@ 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 +2058,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 +2111,7 @@ 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 +2187,7 @@ 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 +2204,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 +2331,7 @@ 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 +2352,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 +2665,7 @@ describe('UserResolver', () => { errors: [new GraphQLError('401 Unauthorized')], }), ) - expect(logger.error).toBeCalledWith('401 Unauthorized') + expect(logErrorLogger.error).toBeCalledWith('401 Unauthorized') }) }) @@ -2720,7 +2703,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 +2725,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 +2748,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, diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index ee83937f0..861ee3622 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -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_RESOLVER_CATEGORY_NAME } from '@/graphql/resolver' +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_RESOLVER_CATEGORY_NAME}.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 => { +const newGradidoID = async (logger: Logger): Promise => { 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 { + 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,39 @@ export class UserResolver { @Args() { email, password, publisherId, project }: UnsecureLoginArgs, @Ctx() context: Context, ): Promise { - 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 pubKey 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 } - + // TODO: discuss need we logging all this cases? 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 +229,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 +250,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 +269,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 +278,6 @@ export class UserResolver { @Mutation(() => Boolean) async logout(@Ctx() context: Context): Promise { await EVENT_USER_LOGOUT(getUser(context)) - // remove user from logger context - logger.addContext('user', 'unknown') return true } @@ -288,10 +296,24 @@ export class UserResolver { project = null, }: CreateUserArgs, ): Promise { - 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 +327,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 +348,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 +358,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 +372,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 +395,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 +424,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 +451,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 +505,28 @@ export class UserResolver { @Authorized([RIGHTS.SEND_RESET_PASSWORD_EMAIL]) @Mutation(() => Boolean) async forgotPassword(@Arg('email') email: string): Promise { - 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 +537,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 +561,8 @@ export class UserResolver { @Arg('code') code: string, @Arg('password') password: string, ): Promise { - logger.info(`setPassword(${code}, ***)...`) + const logger = createLogger() + logger.info(`setPassword...`) // Validate Password if (!isValidPassword(password)) { throw new LogError( @@ -545,8 +574,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 +628,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 +641,21 @@ export class UserResolver { @Authorized([RIGHTS.QUERY_OPT_IN]) @Query(() => Boolean) async queryOptIn(@Arg('optIn') optIn: string): Promise { - 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 +692,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 +735,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 +745,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 +817,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 +839,22 @@ export class UserResolver { @Authorized([RIGHTS.HAS_ELOPAGE]) @Query(() => Boolean) async hasElopage(@Ctx() context: Context): Promise { - 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 { - 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 +873,11 @@ export class UserResolver { @Authorized([RIGHTS.GMS_USER_PLAYGROUND]) @Query(() => UserLocationResult) async userLocation(@Ctx() context: Context): Promise { - 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 +896,11 @@ export class UserResolver { @Ctx() context: Context, @Arg('project', () => String, { nullable: true }) project?: string | null, ): Promise { - 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 +1093,15 @@ export class UserResolver { @Arg('email') email: string, @Ctx() context: Context, ): Promise { + const logger = createLogger() email = email.trim().toLowerCase() - // const user = await dbUser.findOne({ id: emailContact.userId }) - const user = await findUserByEmail(email) + 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 +1110,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 +1156,25 @@ export class UserResolver { } export async function findUserByEmail(email: string): Promise { - 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) { + // TODO: discuss if it is ok to print email in log for this case + 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 { diff --git a/backend/src/graphql/resolver/index.ts b/backend/src/graphql/resolver/index.ts new file mode 100644 index 000000000..0ee59f7f6 --- /dev/null +++ b/backend/src/graphql/resolver/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' + +export const LOG4JS_RESOLVER_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver` diff --git a/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts b/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts index 5d7b9f723..01d423993 100644 --- a/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts +++ b/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts @@ -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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' import { ensureUrlEndsWithSlash } from '@/util/utilities' +import { getLogger } from 'log4js' + +const logger = getLogger( + `${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.authenticateGmsUserPlayground`, +) export async function authenticateGmsUserPlayground( _apiKey: string, diff --git a/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts index cc53ff5a1..80a1e608a 100644 --- a/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts +++ b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts @@ -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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '.' import { Point2Location } from './Location2Point' +const logger = getLogger( + `${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.compareGmsRelevantUserSettings`, +) + export function compareGmsRelevantUserSettings( orgUser: DbUser, updateUserInfosArgs: UpdateUserInfosArgs, diff --git a/backend/src/graphql/resolver/util/creations.ts b/backend/src/graphql/resolver/util/creations.ts index 9dadaa8e3..02439e10a 100644 --- a/backend/src/graphql/resolver/util/creations.ts +++ b/backend/src/graphql/resolver/util/creations.ts @@ -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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' 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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.creations`) interface CreationMap { id: number diff --git a/backend/src/graphql/resolver/util/getKlicktippState.ts b/backend/src/graphql/resolver/util/getKlicktippState.ts index e006ff20a..719286223 100644 --- a/backend/src/graphql/resolver/util/getKlicktippState.ts +++ b/backend/src/graphql/resolver/util/getKlicktippState.ts @@ -1,7 +1,10 @@ import { KlickTipp } from '@model/KlickTipp' import { getKlickTippUser } from '@/apis/KlicktippController' -import { klickTippLogger as logger } from '@/server/logger' +import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { getLogger } from 'log4js' + +const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.getKlicktippState`) export const getKlicktippState = async (email: string): Promise => { try { @@ -10,7 +13,7 @@ export const getKlicktippState = async (email: string): Promise => { 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) } diff --git a/backend/src/graphql/resolver/util/index.ts b/backend/src/graphql/resolver/util/index.ts new file mode 100644 index 000000000..bf0fba6d1 --- /dev/null +++ b/backend/src/graphql/resolver/util/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_RESOLVER_CATEGORY_NAME } from '@/graphql/resolver' + +export const LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME = `${LOG4JS_RESOLVER_CATEGORY_NAME}.util` diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 67b6d71bd..7115d3d1f 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -14,13 +14,16 @@ 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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' 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' +const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.processXComSendCoins`) + export async function processXComPendingSendCoins( receiverCom: DbCommunity, senderCom: DbCommunity, @@ -63,7 +66,7 @@ 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) + logger.debug(`calculated senderBalance = `, senderBalance) const receiverFCom = await DbFederatedCommunity.findOneOrFail({ where: { @@ -88,11 +91,11 @@ 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) + logger.debug(`ready for voteForSendCoins with args=`, args) voteResult = await client.voteForSendCoins(args) - logger.debug(`X-Com: returned from voteForSendCoins:`, voteResult) + logger.debug(`returned from voteForSendCoins:`, 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,20 +123,20 @@ 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) + logger.debug(`initialized sender pendingTX=`, 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) // 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) // 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( @@ -143,10 +146,7 @@ export async function processXComPendingSendCoins( } 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...`, voteResult) } return voteResult } @@ -168,7 +168,7 @@ export async function processXComCommittingSendCoins( const sendCoinsResult = new SendCoinsResult() try { logger.debug( - `XCom: processXComCommittingSendCoins...`, + `processXComCommittingSendCoins...`, receiverCom, senderCom, creationDate, @@ -191,7 +191,7 @@ export async function processXComCommittingSendCoins( memo, }) if (pendingTx) { - logger.debug(`X-Com: find pending Tx for settlement:`, pendingTx) + logger.debug('find pending Tx for settlement:', pendingTx) const receiverFCom = await DbFederatedCommunity.findOneOrFail({ where: { publicKey: receiverCom.publicKey, @@ -218,9 +218,9 @@ export async function processXComCommittingSendCoins( args.senderUserName = pendingTx.userName } args.senderAlias = sender.alias - logger.debug(`X-Com: ready for settleSendCoins with args=`, args) + logger.debug('ready for settleSendCoins with args=', 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,22 +244,22 @@ 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: ', err) // 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=`, + '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=`, + 'Error in reverting receiver pending transaction even after revertCount=', revertCount, ) } @@ -267,7 +267,7 @@ export async function processXComCommittingSendCoins( } } } catch (err) { - logger.error(`Error:`, err) + logger.error('Error: ', err) sendCoinsResult.vote = false } return sendCoinsResult diff --git a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts index 72a458642..4dd1672af 100644 --- a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts +++ b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts @@ -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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '.' import { sendTransactionsToDltConnector } from './sendTransactionsToDltConnector' jest.mock('@/password/EncryptorUtils') +const logger = getLogger( + `${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.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...') }) }) diff --git a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts index 010188dca..98e53456e 100644 --- a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts +++ b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts @@ -3,8 +3,13 @@ import { IsNull } from 'typeorm' import { DltConnectorClient } from '@dltConnector/DltConnectorClient' -import { backendLogger as logger } from '@/server/logger' +import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' import { Monitor, MonitorNames } from '@/util/Monitor' +import { getLogger } from 'log4js' + +const logger = getLogger( + `${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.sendTransactionsToDltConnector`, +) export async function sendTransactionsToDltConnector(): Promise { logger.info('sendTransactionsToDltConnector...') @@ -35,7 +40,7 @@ export async function sendTransactionsToDltConnector(): Promise { 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( diff --git a/backend/src/graphql/resolver/util/sendUserToGms.ts b/backend/src/graphql/resolver/util/sendUserToGms.ts index 26ac070d5..835d36343 100644 --- a/backend/src/graphql/resolver/util/sendUserToGms.ts +++ b/backend/src/graphql/resolver/util/sendUserToGms.ts @@ -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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' import { LogError } from '@/server/LogError' -import { backendLogger as logger } from '@/server/logger' +import { getLogger } from 'log4js' + +const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.sendUserToGms`) export async function sendUserToGms( user: DbUser, diff --git a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts index 82c95ba2e..81f1d5042 100644 --- a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts +++ b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts @@ -8,14 +8,17 @@ import { import { Decimal } from 'decimal.js-light' import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState' +import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' 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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.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({ diff --git a/backend/src/graphql/resolver/util/storeForeignUser.ts b/backend/src/graphql/resolver/util/storeForeignUser.ts index 929aa9cca..bd0eeb68f 100644 --- a/backend/src/graphql/resolver/util/storeForeignUser.ts +++ b/backend/src/graphql/resolver/util/storeForeignUser.ts @@ -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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { getLogger } from 'log4js' + +const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.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 } } diff --git a/backend/src/graphql/resolver/util/syncHumhub.test.ts b/backend/src/graphql/resolver/util/syncHumhub.test.ts index 594f5fdfd..2eba3be40 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.test.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.test.ts @@ -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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.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, diff --git a/backend/src/graphql/resolver/util/syncHumhub.ts b/backend/src/graphql/resolver/util/syncHumhub.ts index b483af1ce..c866daf5a 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.ts @@ -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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { getLogger } from 'log4js' + +const createLogger = () => getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.syncHumhub`) /** * Syncs the user with humhub @@ -21,6 +24,8 @@ export async function syncHumhub( oldHumhubUsername: string, spaceId?: number | null, ): Promise { + const logger = createLogger() + logger.addContext('user', user.id) // check for humhub relevant changes if ( updateUserInfosArg && diff --git a/backend/src/graphql/resolver/util/validateAlias.test.ts b/backend/src/graphql/resolver/util/validateAlias.test.ts index c57a6332f..f10f54886 100644 --- a/backend/src/graphql/resolver/util/validateAlias.test.ts +++ b/backend/src/graphql/resolver/util/validateAlias.test.ts @@ -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() }) diff --git a/backend/src/index.ts b/backend/src/index.ts index 01f7f47a3..6197f39df 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -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 diff --git a/backend/src/interactions/index.ts b/backend/src/interactions/index.ts new file mode 100644 index 000000000..3aad98ba5 --- /dev/null +++ b/backend/src/interactions/index.ts @@ -0,0 +1,3 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' + +export const LOG4JS_INTERACTION_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.interaction` diff --git a/backend/src/interactions/updateUnconfirmedContribution/AbstractUnconfirmedContribution.role.ts b/backend/src/interactions/updateUnconfirmedContribution/AbstractUnconfirmedContribution.role.ts index 36369987b..b713aeefd 100644 --- a/backend/src/interactions/updateUnconfirmedContribution/AbstractUnconfirmedContribution.role.ts +++ b/backend/src/interactions/updateUnconfirmedContribution/AbstractUnconfirmedContribution.role.ts @@ -5,13 +5,16 @@ import { Role } from '@/auth/Role' import { ContributionLogic } from '@/data/Contribution.logic' import { ContributionMessageBuilder } from '@/data/ContributionMessage.builder' import { ContributionStatus } from '@/graphql/enum/ContributionStatus' +import { LOG4JS_INTERACTION_CATEGORY_NAME } from '@/interactions' import { LogError } from '@/server/LogError' import { Context, getClientTimezoneOffset } from '@/server/context' +import { Logger, getLogger } from 'log4js' 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_INTERACTION_CATEGORY_NAME}.updateUnconfirmedContribution`) + this.logger.addContext('contribution', this.self.id) } public isChanged(): boolean { diff --git a/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionAdmin.role.ts b/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionAdmin.role.ts index a216a8919..f160966d3 100644 --- a/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionAdmin.role.ts +++ b/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionAdmin.role.ts @@ -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') } /** diff --git a/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionAdminAddMessage.role.ts b/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionAdminAddMessage.role.ts index 42bb75e00..dcc10091f 100644 --- a/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionAdminAddMessage.role.ts +++ b/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionAdminAddMessage.role.ts @@ -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 { diff --git a/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionUser.role.ts b/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionUser.role.ts index dedc7a1d1..6955cbfa7 100644 --- a/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionUser.role.ts +++ b/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionUser.role.ts @@ -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 { diff --git a/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionUserAddMessage.role.ts b/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionUserAddMessage.role.ts index 77d89ac2b..0440762f3 100644 --- a/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionUserAddMessage.role.ts +++ b/backend/src/interactions/updateUnconfirmedContribution/UnconfirmedContributionUserAddMessage.role.ts @@ -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 { diff --git a/backend/src/password/EncryptorUtils.ts b/backend/src/password/EncryptorUtils.ts index 7da8bc9c1..20ff202b7 100644 --- a/backend/src/password/EncryptorUtils.ts +++ b/backend/src/password/EncryptorUtils.ts @@ -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 => { try { - logger.trace('call worker for: SecretKeyCryptographyCreateKey') if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) { throw new LogError( 'ServerKey has an invalid size', diff --git a/backend/src/password/PasswordEncryptor.ts b/backend/src/password/PasswordEncryptor.ts index 1131b97ef..9efe4e60e 100644 --- a/backend/src/password/PasswordEncryptor.ts +++ b/backend/src/password/PasswordEncryptor.ts @@ -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 => { diff --git a/backend/src/password/__mocks__/EncryptorUtils.ts b/backend/src/password/__mocks__/EncryptorUtils.ts index 5575f72f7..1bac06239 100644 --- a/backend/src/password/__mocks__/EncryptorUtils.ts +++ b/backend/src/password/__mocks__/EncryptorUtils.ts @@ -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, diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts index 8036d49e5..d99e33872 100644 --- a/backend/src/seeds/index.ts +++ b/backend/src/seeds/index.ts @@ -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,7 @@ const resetEntity = async (entity: any) => { } const run = async () => { - const server = await createServer(context) + const server = await createServer(getLogger('apollo'), context) const seedClient = createTestClient(server.apollo) const { con } = server await cleanDB() @@ -97,6 +99,7 @@ const run = async () => { } run().catch((err) => { + initLogging() // biome-ignore lint/suspicious/noConsole: no logger present console.error('error on seeding', err) }) diff --git a/backend/src/server/LogError.test.ts b/backend/src/server/LogError.test.ts index 431b60e6e..3b98aea89 100644 --- a/backend/src/server/LogError.test.ts +++ b/backend/src/server/LogError.test.ts @@ -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') diff --git a/backend/src/server/LogError.ts b/backend/src/server/LogError.ts index 346923019..c455eaa15 100644 --- a/backend/src/server/LogError.ts +++ b/backend/src/server/LogError.ts @@ -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) diff --git a/backend/src/server/createServer.ts b/backend/src/server/createServer.ts index 40d0144fa..60f6fc31e 100644 --- a/backend/src/server/createServer.ts +++ b/backend/src/server/createServer.ts @@ -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 => { - 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( diff --git a/backend/src/server/localization.ts b/backend/src/server/localization.ts index 8e533576a..1e587104a 100644 --- a/backend/src/server/localization.ts +++ b/backend/src/server/localization.ts @@ -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 diff --git a/backend/src/server/logger.ts b/backend/src/server/logger.ts index 24ae79c67..be9cababe 100644 --- a/backend/src/server/logger.ts +++ b/backend/src/server/logger.ts @@ -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, + ) +} diff --git a/backend/src/util/Monitor.ts b/backend/src/util/Monitor.ts index f592a343d..31d6d8938 100644 --- a/backend/src/util/Monitor.ts +++ b/backend/src/util/Monitor.ts @@ -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() + 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) } diff --git a/backend/src/util/decay.ts b/backend/src/util/decay.ts index 1d1775cc9..96c7ddb4b 100644 --- a/backend/src/util/decay.ts +++ b/backend/src/util/decay.ts @@ -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() diff --git a/backend/src/util/time.ts b/backend/src/util/time.ts index 0acaf2501..ccbb91c07 100644 --- a/backend/src/util/time.ts +++ b/backend/src/util/time.ts @@ -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 } diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index b027633a6..64113062f 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -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 => { logger.info('Elopage Hook received') diff --git a/backend/src/webhook/gms.ts b/backend/src/webhook/gms.ts index 915552390..06f48024d 100644 --- a/backend/src/webhook/gms.ts +++ b/backend/src/webhook/gms.ts @@ -1,7 +1,9 @@ import { User as DbUser } from 'database' import { decode } from '@/auth/JWT' -import { gmsLogger as logger } from '@/server/logger' +import { getLogger } from 'log4js' + +const logger = getLogger('gms.GmsWebhook') export const gmsWebhook = async (req: any, res: any): Promise => { logger.info('GMS Hook received') diff --git a/backend/test/helpers.ts b/backend/test/helpers.ts index 6d567f029..a966eccf6 100644 --- a/backend/test/helpers.ts +++ b/backend/test/helpers.ts @@ -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 diff --git a/backend/test/testSetup.ts b/backend/test/testSetup.ts index 02c325794..c010ff705 100644 --- a/backend/test/testSetup.ts +++ b/backend/test/testSetup.ts @@ -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('@/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('@/server/localization') return { @@ -41,4 +24,4 @@ jest.mock('@/server/localization', () => { } }) -export { logger, i18n } +export { i18n, getLogger, printLogs, clearLogs as cleanLogs } diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 79bdd2cf3..2152f4b79 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -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" : ".", diff --git a/backend/turbo.json b/backend/turbo.json index e4dfea9a8..fa9af6ce2 100644 --- a/backend/turbo.json +++ b/backend/turbo.json @@ -24,6 +24,9 @@ }, "start": { "dependsOn": ["database#up", "build"] + }, + "start:bundle": { + "dependsOn": ["build:bundle"] } } } \ No newline at end of file From 6cf82ad87c4c5d39a95dee55c3d2dc57af2cde71 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 17 Jun 2025 15:39:37 +0200 Subject: [PATCH 11/46] add debug config, refine and test coloredContext --- .github/workflows/test_config.yml | 5 +- .gitignore | 1 + .vscode/launch.json | 58 +++++++- bun.lock | 9 ++ config-schema/package.json | 1 + config-schema/src/log4js-config/appenders.ts | 3 +- .../src/log4js-config/coloredContext.test.ts | 135 ++++++++++++++++++ .../src/log4js-config/coloredContext.ts | 22 ++- config-schema/src/log4js-config/index.test.ts | 28 ++++ config-schema/src/log4js-config/index.ts | 9 +- .../src/log4js-config/types/Category.ts | 8 ++ .../types/ColoredContextLayoutConfig.ts | 7 + .../log4js-config/types/CustomFileAppender.ts | 5 +- .../src/log4js-config/types/index.ts | 1 + config-schema/test/testSetup.ts | 106 +++++++++----- database/src/logging/AbstractLogging.view.ts | 6 +- dht-node/src/dht_node/index.ts | 5 +- dlt-connector/src/server/createServer.ts | 1 - federation/Dockerfile | 3 - federation/package.json | 1 + federation/src/index.ts | 3 +- federation/test/helpers.ts | 3 +- yarn.lock | 7 + 23 files changed, 353 insertions(+), 74 deletions(-) create mode 100644 config-schema/src/log4js-config/coloredContext.test.ts create mode 100644 config-schema/src/log4js-config/index.test.ts create mode 100644 config-schema/src/log4js-config/types/ColoredContextLayoutConfig.ts diff --git a/.github/workflows/test_config.yml b/.github/workflows/test_config.yml index 5ba0832c6..8643bf959 100644 --- a/.github/workflows/test_config.yml +++ b/.github/workflows/test_config.yml @@ -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 + diff --git a/.gitignore b/.gitignore index 3d7f34078..d96592bc8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.bak .turbo vite.config.mjs.timestamp-* +log4js-config*.json /node_modules/* messages.pot nbproject diff --git a/.vscode/launch.json b/.vscode/launch.json index 5ef63cc68..5fcb62e09 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,13 +4,6 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "type": "chrome", - "request": "launch", - "name": "Launch Chrome", - "url": "http://localhost:3000", - "webRoot": "${workspaceFolder}/frontend" - }, { "type": "node", "request": "launch", @@ -27,6 +20,23 @@ "internalConsoleOptions": "neverOpen", "cwd": "${workspaceFolder}/dht-node" }, + { + "type": "node", + "request": "launch", + "name": "DHT-Node Debug", + "stopOnEntry": true, + "runtimeExecutable": "yarn", + "runtimeArgs": [ + "run", + "dev" + ], + "skipFiles": [ + "/**" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "openOnSessionStart", + "cwd": "${workspaceFolder}/dht-node" + }, { "type": "node", "request": "launch", @@ -42,6 +52,40 @@ "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "cwd": "${workspaceFolder}/federation" + }, + { + "type": "node", + "request": "launch", + "name": "Federation Debug", + "stopOnEntry": true, + "runtimeExecutable": "yarn", + "runtimeArgs": [ + "run", + "dev" + ], + "skipFiles": [ + "/**" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "openOnSessionStart", + "cwd": "${workspaceFolder}/federation" + }, + { + "type": "node", + "request": "launch", + "name": "Backend Debug", + "stopOnEntry": true, + "runtimeExecutable": "yarn", + "runtimeArgs": [ + "run", + "dev" + ], + "skipFiles": [ + "/**" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "openOnSessionStart", + "cwd": "${workspaceFolder}/backend" } ] } \ No newline at end of file diff --git a/bun.lock b/bun.lock index 4711384ee..cf0f46bb5 100644 --- a/bun.lock +++ b/bun.lock @@ -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,6 +163,7 @@ "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", @@ -168,6 +171,7 @@ "devDependencies": { "@biomejs/biome": "1.9.4", "@types/node": "^17.0.21", + "jest": "27.2.4", "typescript": "^4.9.5", }, }, @@ -279,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", @@ -1010,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=="], @@ -3454,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=="], diff --git a/config-schema/package.json b/config-schema/package.json index 0b0487255..d5d0494f1 100644 --- a/config-schema/package.json +++ b/config-schema/package.json @@ -18,6 +18,7 @@ "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" }, diff --git a/config-schema/src/log4js-config/appenders.ts b/config-schema/src/log4js-config/appenders.ts index 6648d2924..19f74dccd 100644 --- a/config-schema/src/log4js-config/appenders.ts +++ b/config-schema/src/log4js-config/appenders.ts @@ -68,8 +68,7 @@ export function createAppenderConfig( } dateFile.layout = { type: 'coloredContext', - withStack: appender.withStack, - withFile: appender.withFile, + ...appender.layout, } customAppender[appender.name] = dateFile }) diff --git a/config-schema/src/log4js-config/coloredContext.test.ts b/config-schema/src/log4js-config/coloredContext.test.ts new file mode 100644 index 000000000..38bde4d96 --- /dev/null +++ b/config-schema/src/log4js-config/coloredContext.test.ts @@ -0,0 +1,135 @@ +import { createColoredContextLayout } from './coloredContext' +import { levels, LoggingEvent } from 'log4js' +import colors from 'yoctocolors-cjs' + +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`, + ) + }) + }) +}) diff --git a/config-schema/src/log4js-config/coloredContext.ts b/config-schema/src/log4js-config/coloredContext.ts index 21a2d7b28..0fc7476a8 100644 --- a/config-schema/src/log4js-config/coloredContext.ts +++ b/config-schema/src/log4js-config/coloredContext.ts @@ -1,12 +1,6 @@ import { Level, LoggingEvent } from 'log4js' import colors from 'yoctocolors-cjs' -import { LogLevel } from './types' - -export type coloredContextLayoutConfig = { - withStack?: LogLevel | boolean - withFile?: LogLevel | boolean - withLine?: LogLevel | boolean -} +import { LogLevel, ColoredContextLayoutConfig } from './types' function colorize(str: string, level: Level): string { switch (level.colour) { @@ -36,12 +30,14 @@ function colorize(str: string, level: Level): string { // 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((data) => { + .map((d) => { // if it is a object and his toString function return only garbage - if (typeof data === 'object' && data.toString() === '[object Object]') { - return JSON.stringify(data) + if (typeof d === 'object' && d.toString() === '[object Object]') { + return JSON.stringify(d) + } + if (d) { + return d.toString() } - return data.toString() }) .join(' ') } @@ -72,7 +68,7 @@ enum DetailKind { File = 'file', Line = 'line', } -function resolveDetailKind(logEvent: LoggingEvent, config: coloredContextLayoutConfig): DetailKind | undefined { +function resolveDetailKind(logEvent: LoggingEvent, config: ColoredContextLayoutConfig): DetailKind | undefined { if (logEvent.callStack && isEnabledByLogLevel(logEvent.level, config.withStack)) { return DetailKind.Callstack } @@ -85,7 +81,7 @@ function resolveDetailKind(logEvent: LoggingEvent, config: coloredContextLayoutC return undefined } -export function createColoredContextLayout(config: coloredContextLayoutConfig) { +export function createColoredContextLayout(config: ColoredContextLayoutConfig) { return (logEvent: LoggingEvent) => { const result: string[] = [] const detailKind = resolveDetailKind(logEvent, config) diff --git a/config-schema/src/log4js-config/index.test.ts b/config-schema/src/log4js-config/index.test.ts new file mode 100644 index 000000000..5f5ea6b5b --- /dev/null +++ b/config-schema/src/log4js-config/index.test.ts @@ -0,0 +1,28 @@ +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 + } + }) + }) +}) \ No newline at end of file diff --git a/config-schema/src/log4js-config/index.ts b/config-schema/src/log4js-config/index.ts index 18699639f..94721d384 100644 --- a/config-schema/src/log4js-config/index.ts +++ b/config-schema/src/log4js-config/index.ts @@ -2,9 +2,11 @@ import { readFileSync, writeFileSync } from 'node:fs' import { Configuration, LoggingEvent, addLayout, configure } from 'log4js' import { createAppenderConfig } from './appenders' import { createColoredContextLayout } from './coloredContext' -import { Category, CustomFileAppender, LogLevel, defaultCategory } from './types' +import type { Category, CustomFileAppender, LogLevel } from './types' +import { defaultCategory } from './types' -export { Category, LogLevel, defaultCategory } +export type { Category, LogLevel } +export { defaultCategory } /** * Creates the log4js configuration. @@ -33,8 +35,7 @@ export function createLog4jsConfig(categories: Category[], basePath?: string): C customFileAppenders.push({ name: category.name, filename: category.filename, - withLine: true, - withStack: 'error', + layout: category.layout, }) // needed by log4js, show all error message accidentally without (proper) Category result.categories.default = { diff --git a/config-schema/src/log4js-config/types/Category.ts b/config-schema/src/log4js-config/types/Category.ts index 889401b30..880fd141b 100644 --- a/config-schema/src/log4js-config/types/Category.ts +++ b/config-schema/src/log4js-config/types/Category.ts @@ -1,4 +1,5 @@ import { LogLevel } from './LogLevel' +import { ColoredContextLayoutConfig } from './ColoredContextLayoutConfig' /** * Configuration for a log4js category. @@ -8,6 +9,7 @@ import { LogLevel } from './LogLevel' * @property {boolean} [stdout] - Whether to log to stdout. * @property {boolean} [additionalErrorsFile] - Whether to log errors additional to the default error file. * @property {LogLevel} level - The logging level. + * @property {ColoredContextLayoutConfig} [layout] - The layout for the category. */ export type Category = { name: string @@ -15,6 +17,7 @@ export type Category = { stdout?: boolean additionalErrorsFile?: boolean level: LogLevel + layout?: ColoredContextLayoutConfig } export function defaultCategory(name: string, level: LogLevel): Category { @@ -23,5 +26,10 @@ export function defaultCategory(name: string, level: LogLevel): Category { level, stdout: true, additionalErrorsFile: true, + layout: { + withStack: 'error', + withFile: 'warn', + withLine: true, + }, } } diff --git a/config-schema/src/log4js-config/types/ColoredContextLayoutConfig.ts b/config-schema/src/log4js-config/types/ColoredContextLayoutConfig.ts new file mode 100644 index 000000000..7084adfa1 --- /dev/null +++ b/config-schema/src/log4js-config/types/ColoredContextLayoutConfig.ts @@ -0,0 +1,7 @@ +import { LogLevel } from './LogLevel' + +export type ColoredContextLayoutConfig = { + withStack?: LogLevel | boolean + withFile?: LogLevel | boolean + withLine?: LogLevel | boolean +} \ No newline at end of file diff --git a/config-schema/src/log4js-config/types/CustomFileAppender.ts b/config-schema/src/log4js-config/types/CustomFileAppender.ts index 46c292349..5bb67769c 100644 --- a/config-schema/src/log4js-config/types/CustomFileAppender.ts +++ b/config-schema/src/log4js-config/types/CustomFileAppender.ts @@ -1,4 +1,5 @@ import { LogLevel } from './LogLevel' +import { ColoredContextLayoutConfig } from './ColoredContextLayoutConfig' /** * use default dateFile Template for custom file appenders * @@ -29,7 +30,5 @@ import { LogLevel } from './LogLevel' export type CustomFileAppender = { name: string filename?: string - withStack?: LogLevel | boolean // with stack if boolean or from log level on or above - withFile?: LogLevel | boolean // with filename and line if boolean or from log level on or above - withLine?: LogLevel | boolean // with line if boolean or from log level on or above + layout?: ColoredContextLayoutConfig } diff --git a/config-schema/src/log4js-config/types/index.ts b/config-schema/src/log4js-config/types/index.ts index e79261487..67279ff57 100644 --- a/config-schema/src/log4js-config/types/index.ts +++ b/config-schema/src/log4js-config/types/index.ts @@ -1,3 +1,4 @@ export * from './Category' export * from './CustomFileAppender' export * from './LogLevel' +export * from './ColoredContextLayoutConfig' diff --git a/config-schema/test/testSetup.ts b/config-schema/test/testSetup.ts index 1d1b63f23..c1d679d8a 100644 --- a/config-schema/test/testSetup.ts +++ b/config-schema/test/testSetup.ts @@ -1,59 +1,99 @@ jest.setTimeout(1000000) -const loggers: { [key: string]: any } = {} -const logs: { level: string; message: string; logger: string; additional: string }[] = [] +type LogEntry = { + level: string; + message: string; + logger: string; + context: string; + additional: any[]; +} -function addLog(level: string, message: string, logger: string, additional: any[]) { - logs.push({ level, message, logger, additional: JSON.stringify(additional, null, 2) }) +const loggers: { [key: string]: any } = {} +const logs: LogEntry[] = [] + +function addLog(level: string, message: string, logger: string, context: Map, additional: any[]) { + logs.push({ + level, + context: [...context.entries()].map(([key, value]) => `${key}=${value}`).join(' ').trimEnd(), + message, + logger, + additional + }) } export function printLogs() { for (const log of logs) { - process.stdout.write(`${log.logger} [${log.level}] ${log.message} ${log.additional}\n`) + const messages = [] + messages.push(log.message) + messages.push(log.additional.map((d) => { + if (typeof d === 'object' && d.toString() === '[object Object]') { + return JSON.stringify(d) + } + if (d) { + return d.toString() + } + }).filter((d) => d)) + process.stdout.write(`${log.logger} [${log.level}] ${log.context} ${messages.join(' ')}\n`) } } -export function cleanLogs(): void { +export function clearLogs(): void { logs.length = 0 } +const getLoggerMocked = jest.fn().mockImplementation((param: any) => { + if (loggers[param]) { + // TODO: check if it is working when tests run in parallel + loggers[param].clearContext() + return loggers[param] + } + // console.log('getLogger called with: ', param) + const fakeLogger = { + context: new Map(), + addContext: jest.fn((key: string, value: string) => { + fakeLogger.context.set(key, value) + }), + trace: jest.fn((message: string, ...args: any[]) => { + addLog('trace', message, param, fakeLogger.context, args) + }), + debug: jest.fn((message: string, ...args: any[]) => { + addLog('debug', message, param, fakeLogger.context, args) + }), + warn: jest.fn((message: string, ...args: any[]) => { + addLog('warn', message, param, fakeLogger.context, args) + }), + info: jest.fn((message: string, ...args: any[]) => { + addLog('info', message, param, fakeLogger.context, args) + }), + error: jest.fn((message: string, ...args: any[]) => { + addLog('error', message, param, fakeLogger.context, args) + }), + fatal: jest.fn((message: string, ...args: any[]) => { + addLog('fatal', message, param, fakeLogger.context, args) + }), + removeContext: jest.fn((key: string) => { + fakeLogger.context.delete(key) + }), + clearContext: jest.fn(() => { + fakeLogger.context.clear() + }) + } + loggers[param] = fakeLogger + return fakeLogger +}) + jest.mock('log4js', () => { const originalModule = jest.requireActual('log4js') return { __esModule: true, ...originalModule, - getLogger: jest.fn().mockImplementation((param: any) => { - // console.log('getLogger called with: ', param) - const fakeLogger = { - addContext: jest.fn(), - trace: jest.fn((message: string, ...args: any[]) => { - addLog('trace', message, param, args) - }), - debug: jest.fn((message: string, ...args: any[]) => { - addLog('debug', message, param, args) - }), - warn: jest.fn((message: string, ...args: any[]) => { - addLog('warn', message, param, args) - }), - info: jest.fn((message: string, ...args: any[]) => { - addLog('info', message, param, args) - }), - error: jest.fn((message: string, ...args: any[]) => { - addLog('error', message, param, args) - }), - fatal: jest.fn((message: string, ...args: any[]) => { - addLog('fatal', message, param, args) - }), - } - loggers[param] = fakeLogger - return fakeLogger - }), + getLogger: getLoggerMocked } }) export function getLogger(name: string) { if (!loggers[name]) { - throw new Error(`No logger with name ${name} was requested from code`) + return getLoggerMocked(name) } return loggers[name] } diff --git a/database/src/logging/AbstractLogging.view.ts b/database/src/logging/AbstractLogging.view.ts index 4d8824cc3..00fdd4703 100644 --- a/database/src/logging/AbstractLogging.view.ts +++ b/database/src/logging/AbstractLogging.view.ts @@ -23,7 +23,11 @@ export abstract class AbstractLoggingView { public dateToString(date: Date | undefined | null): string | undefined { if (date) { - return date.toISOString() + if (date instanceof Date) { + return date.toISOString() + } else { + return new Date(date).toISOString() + } } return undefined } diff --git a/dht-node/src/dht_node/index.ts b/dht-node/src/dht_node/index.ts index 3c88bc1c8..394c5d86c 100644 --- a/dht-node/src/dht_node/index.ts +++ b/dht-node/src/dht_node/index.ts @@ -59,7 +59,6 @@ export const startDHT = async (topic: string): Promise => { const server = node.createServer() server.on('connection', function (socket: any) { - logger.addContext('pubkey', socket.remotePublicKey.toString('hex')) logger.info(`server on... with Remote public key: ${socket.remotePublicKey.toString('hex')}`) socket.on('data', async (data: Buffer) => { @@ -111,10 +110,10 @@ export const startDHT = async (topic: string): Promise => { const variables = { apiVersion: recApiVersion.api, endPoint: recApiVersion.url, - publicKey: socket.remotePublicKey, + publicKey: socket.remotePublicKey.toString('hex'), lastAnnouncedAt: new Date(), } - logger.debug(`upsert with variables=${JSON.stringify(variables)}`) + logger.debug(`upsert with variables=${JSON.stringify(variables, null, 2)}`) // this will NOT update the updatedAt column, to distingue between a normal update and the last announcement await DbFederatedCommunity.createQueryBuilder() .insert() diff --git a/dlt-connector/src/server/createServer.ts b/dlt-connector/src/server/createServer.ts index 25e0a12e2..53bf3ab97 100755 --- a/dlt-connector/src/server/createServer.ts +++ b/dlt-connector/src/server/createServer.ts @@ -26,7 +26,6 @@ const createServer = async ( logger: Logger = dltLogger, // localization: i18n.I18n = i18n, ): Promise => { - logger.addContext('user', 'unknown') logger.debug('createServer...') // connect to db and test db version diff --git a/federation/Dockerfile b/federation/Dockerfile index 070fe3e2a..a8fcfd769 100644 --- a/federation/Dockerfile +++ b/federation/Dockerfile @@ -115,8 +115,5 @@ COPY --chown=app:app --from=build ${DOCKER_WORKDIR}/federation/build/index.js ./ # 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}/federation/log4js-config.json ./log4js-config.json - # Run command CMD ["node", "index.js"] \ No newline at end of file diff --git a/federation/package.json b/federation/package.json index cf0ab77d4..95c1e5337 100644 --- a/federation/package.json +++ b/federation/package.json @@ -59,6 +59,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", diff --git a/federation/src/index.ts b/federation/src/index.ts index eb6404d5c..de2634465 100644 --- a/federation/src/index.ts +++ b/federation/src/index.ts @@ -1,3 +1,4 @@ +import 'source-map-support/register' import { createServer } from './server/createServer' import { defaultCategory, initLogger } from 'config-schema' @@ -7,7 +8,7 @@ import { CONFIG } from './config' import { LOG4JS_BASE_CATEGORY_NAME } from './config/const' async function main() { - // init logger + // init logger const log4jsConfigFileName = CONFIG.LOG4JS_CONFIG_PLACEHOLDER.replace('%v', CONFIG.FEDERATION_API) initLogger( [defaultCategory('federation', CONFIG.LOG_LEVEL), defaultCategory('apollo', CONFIG.LOG_LEVEL)], diff --git a/federation/test/helpers.ts b/federation/test/helpers.ts index 148ef6c54..c8523fe7e 100644 --- a/federation/test/helpers.ts +++ b/federation/test/helpers.ts @@ -4,7 +4,6 @@ import { createTestClient } from 'apollo-server-testing' import { createServer } from '@/server/createServer' import { getLogger } from 'config-schema/test/testSetup' -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' export const headerPushMock = jest.fn((t) => { context.token = t.value @@ -26,7 +25,7 @@ export const cleanDB = async () => { } } -export const testEnvironment = async (testLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`) /*, testI18n = i18n */) => { +export const testEnvironment = async (testLogger = getLogger('apollo') /*, testI18n = i18n */) => { const server = await createServer(/* context, */ testLogger /* , testI18n */) const con = server.con const testClient = createTestClient(server.apollo) diff --git a/yarn.lock b/yarn.lock index 9f39a5e23..015b98c5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2569,6 +2569,13 @@ dependencies: "@types/node" "*" +"@types/source-map-support@^0.5.10": + version "0.5.10" + resolved "https://registry.yarnpkg.com/@types/source-map-support/-/source-map-support-0.5.10.tgz#824dcef989496bae98e9d04c8dc1ac1d70e1bd39" + integrity sha512-tgVP2H469x9zq34Z0m/fgPewGhg/MLClalNOiPIzQlXrSS2YrKu/xCdSCKnEDwkFha51VKEKB6A9wW26/ZNwzA== + dependencies: + source-map "^0.6.0" + "@types/stack-utils@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" From 6258774530b7b51d6b41cd02fe6f385f8e9e8bf7 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 17 Jun 2025 15:43:06 +0200 Subject: [PATCH 12/46] lint --- .../federation/validateCommunities.test.ts | 30 +++- .../resolver/ContributionLinkResolver.test.ts | 6 +- .../ContributionMessageResolver.test.ts | 20 ++- .../resolver/ContributionResolver.test.ts | 6 +- .../resolver/TransactionLinkResolver.test.ts | 19 ++- .../resolver/TransactionResolver.test.ts | 2 +- .../src/graphql/resolver/UserResolver.test.ts | 48 +++++-- backend/src/graphql/resolver/UserResolver.ts | 17 ++- config-schema/biome.json | 131 ------------------ config-schema/src/commonSchema.ts | 4 +- config-schema/src/const.ts | 2 +- .../src/log4js-config/coloredContext.test.ts | 32 ++--- .../src/log4js-config/coloredContext.ts | 9 +- config-schema/src/log4js-config/index.test.ts | 10 +- .../src/log4js-config/types/Category.ts | 2 +- .../types/ColoredContextLayoutConfig.ts | 2 +- .../log4js-config/types/CustomFileAppender.ts | 2 +- .../PublicCommunityInfoResolver.test.ts | 2 +- .../1_0/resolver/PublicKeyResolver.test.ts | 2 +- .../1_0/resolver/SendCoinsResolver.test.ts | 2 +- .../1_1/resolver/PublicKeyResolver.test.ts | 2 +- federation/src/index.ts | 2 +- 22 files changed, 142 insertions(+), 210 deletions(-) delete mode 100644 config-schema/biome.json diff --git a/backend/src/federation/validateCommunities.test.ts b/backend/src/federation/validateCommunities.test.ts index bfb43fd35..160068140 100644 --- a/backend/src/federation/validateCommunities.test.ts +++ b/backend/src/federation/validateCommunities.test.ts @@ -12,7 +12,9 @@ import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' import { validateCommunities } from './validateCommunities' const logger = getLogger(`${LOG4JS_FEDERATION_CATEGORY_NAME}.validateCommunities`) -const federationClientLogger = getLogger(`${LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME}.FederationClient`) +const federationClientLogger = getLogger( + `${LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME}.FederationClient`, +) let con: DataSource let testEnv: { @@ -88,8 +90,9 @@ describe('validate Communities', () => { }) it('logs requestGetPublicKey missing response data ', () => { expect(federationClientLogger.warn).toBeCalledWith( - 'getPublicKey without response data from endpoint', 'http//localhost:5001/api/1_0/' - ) + 'getPublicKey without response data from endpoint', + 'http//localhost:5001/api/1_0/', + ) }) }) @@ -160,7 +163,10 @@ describe('validate Communities', () => { expect(logger.debug).toBeCalledWith(`found 1 dbCommunities`) }) it('logs requestGetPublicKey for community api 1_0 ', () => { - expect(federationClientLogger.debug).toBeCalledWith('getPublicKey from endpoint', 'http//localhost:5001/api/1_0/') + expect(federationClientLogger.debug).toBeCalledWith( + 'getPublicKey from endpoint', + 'http//localhost:5001/api/1_0/', + ) }) it('logs not matching publicKeys', () => { expect(logger.debug).toBeCalledWith( @@ -279,7 +285,10 @@ describe('validate Communities', () => { ) }) it('logs requestGetPublicKey for community api 1_1 ', () => { - expect(federationClientLogger.debug).toBeCalledWith('getPublicKey from endpoint', 'http//localhost:5001/api/1_1/') + expect(federationClientLogger.debug).toBeCalledWith( + 'getPublicKey from endpoint', + 'http//localhost:5001/api/1_1/', + ) }) }) describe('with three Communities of api 1_0, 1_1 and 2_0', () => { @@ -328,10 +337,17 @@ describe('validate Communities', () => { ) }) it('logs requestGetPublicKey for community api 1_1 ', () => { - expect(federationClientLogger.debug).toBeCalledWith('getPublicKey from endpoint', 'http//localhost:5001/api/1_1/') + 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('dbCom with unsupported apiVersion', dbCom.endPoint, '2_0') + expect(logger.debug).toBeCalledWith( + 'dbCom with unsupported apiVersion', + dbCom.endPoint, + '2_0', + ) }) }) }) diff --git a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts index bf8c37126..9db6a7d99 100644 --- a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts @@ -6,6 +6,7 @@ import { DataSource } from 'typeorm' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { EventType } from '@/event/Events' import { userFactory } from '@/seeds/factory/user' import { @@ -17,13 +18,10 @@ import { import { listContributionLinks } from '@/seeds/graphql/queries' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' -import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { getLogger } from 'config-schema/test/testSetup' jest.mock('@/password/EncryptorUtils') -const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ContributionLinkResolver`) const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) let mutate: ApolloServerTestClient['mutate'] diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 16ad4dbb2..74af7acc7 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -7,8 +7,10 @@ import { ContributionStatus } from '@enum/ContributionStatus' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' 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 { LOG4JS_INTERACTION_CATEGORY_NAME } from '@/interactions' import { userFactory } from '@/seeds/factory/user' import { adminCreateContributionMessage, @@ -22,12 +24,12 @@ import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { peterLustig } from '@/seeds/users/peter-lustig' import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -import { LOG4JS_INTERACTION_CATEGORY_NAME } from '@/interactions' const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ContributionMessageResolver`) const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) -const interactionLogger = getLogger(`${LOG4JS_INTERACTION_CATEGORY_NAME}.updateUnconfirmedContribution`) +const interactionLogger = getLogger( + `${LOG4JS_INTERACTION_CATEGORY_NAME}.updateUnconfirmedContribution`, +) jest.mock('@/password/EncryptorUtils') jest.mock('@/emails/sendEmailVariants', () => { @@ -156,7 +158,9 @@ describe('ContributionMessageResolver', () => { message: 'Test', }, }) - expect(interactionLogger.debug).toBeCalledWith('use UnconfirmedContributionUserAddMessageRole') + expect(interactionLogger.debug).toBeCalledWith( + 'use UnconfirmedContributionUserAddMessageRole', + ) expect(mutationResult).toEqual( expect.objectContaining({ data: { @@ -350,7 +354,9 @@ describe('ContributionMessageResolver', () => { }, }) - expect(interactionLogger.debug).toBeCalledWith('use UnconfirmedContributionAdminAddMessageRole') + expect(interactionLogger.debug).toBeCalledWith( + 'use UnconfirmedContributionAdminAddMessageRole', + ) expect(mutationResult).toEqual( expect.objectContaining({ @@ -380,7 +386,9 @@ describe('ContributionMessageResolver', () => { message: 'Test', }, }) - expect(interactionLogger.debug).toBeCalledWith('use UnconfirmedContributionAdminAddMessageRole') + expect(interactionLogger.debug).toBeCalledWith( + 'use UnconfirmedContributionAdminAddMessageRole', + ) expect(mutationResult).toEqual( expect.objectContaining({ diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 77e879f8d..90858cfa3 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -16,6 +16,7 @@ import { } from '@test/helpers' import { i18n as localization } from '@test/testSetup' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { sendContributionConfirmedEmail, sendContributionDeletedEmail, @@ -50,14 +51,13 @@ 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, clearLogs, printLogs } from 'config-schema/test/testSetup' +import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' import { getLogger as originalGetLogger } from 'log4js' -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' jest.mock('@/emails/sendEmailVariants') jest.mock('@/password/EncryptorUtils') -const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError` ) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) let mutate: ApolloServerTestClient['mutate'] let query: ApolloServerTestClient['query'] diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index f0107dcdd..7862f71f0 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -34,12 +34,10 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' -import { getLogger } from 'config-schema/test/testSetup' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { getLogger } from 'config-schema/test/testSetup' import { transactionLinkCode } from './TransactionLinkResolver' -const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.TransactionLinkResolver`) const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) jest.mock('@/password/EncryptorUtils') @@ -322,7 +320,10 @@ describe('TransactionLinkResolver', () => { }) it('logs the error "Contribution link is not valid yet"', () => { - expect(logErrorLogger.error).toBeCalledWith('Contribution link is not valid yet', validFrom) + 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'), @@ -361,7 +362,10 @@ describe('TransactionLinkResolver', () => { }) it('logs the error "Contribution link has unknown cycle"', () => { - expect(logErrorLogger.error).toBeCalledWith('Contribution link has unknown cycle', 'INVALID') + 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'), @@ -400,7 +404,10 @@ describe('TransactionLinkResolver', () => { }) it('logs the error "Contribution link is no longer valid"', () => { - expect(logErrorLogger.error).toBeCalledWith('Contribution link is no longer valid', validTo) + 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'), diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index dd0983efc..97b3084d9 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -14,6 +14,7 @@ import { v4 as uuidv4 } from 'uuid' import { cleanDB, testEnvironment } from '@test/helpers' 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,7 +33,6 @@ 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' -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' jest.mock('@/password/EncryptorUtils') diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 0edcf30cf..806ae6bda 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -67,10 +67,10 @@ 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 { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' import { Location2Point } from './util/Location2Point' -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' jest.mock('@/apis/humhub/HumHubClient') jest.mock('@/password/EncryptorUtils') @@ -709,7 +709,9 @@ describe('UserResolver', () => { }) it('logs the error found', () => { - expect(logger.warn).toBeCalledWith(`findUserByEmail failed, user with email=${variables.email} not found`) + expect(logger.warn).toBeCalledWith( + `findUserByEmail failed, user with email=${variables.email} not found`, + ) }) }) @@ -1114,7 +1116,9 @@ describe('UserResolver', () => { }) it('logs warning before throwing error', () => { - expect(logger.warn).toBeCalledWith('email already sent 0 minutes ago, min wait time: 10 minutes') + expect(logger.warn).toBeCalledWith( + 'email already sent 0 minutes ago, min wait time: 10 minutes', + ) }) }) }) @@ -1762,7 +1766,10 @@ describe('UserResolver', () => { }) it('logs the error thrown', () => { - expect(logErrorLogger.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, + ) }) }) @@ -1878,7 +1885,9 @@ describe('UserResolver', () => { ) }) it('logs the error thrown', () => { - expect(logErrorLogger.error).toBeCalledWith('Administrator can not change his own role') + expect(logErrorLogger.error).toBeCalledWith( + 'Administrator can not change his own role', + ) }) }) @@ -1923,7 +1932,10 @@ describe('UserResolver', () => { }) it('logs the error thrown', () => { - expect(logErrorLogger.error).toBeCalledWith('User already has role=', RoleNames.ADMIN) + expect(logErrorLogger.error).toBeCalledWith( + 'User already has role=', + RoleNames.ADMIN, + ) }) }) @@ -1947,7 +1959,10 @@ describe('UserResolver', () => { }) it('logs the error thrown', () => { - expect(logErrorLogger.error).toBeCalledWith('User already has role=', RoleNames.MODERATOR) + expect(logErrorLogger.error).toBeCalledWith( + 'User already has role=', + RoleNames.MODERATOR, + ) }) }) @@ -2041,7 +2056,10 @@ describe('UserResolver', () => { }) it('logs the error thrown', () => { - expect(logErrorLogger.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, + ) }) }) @@ -2111,7 +2129,10 @@ describe('UserResolver', () => { }) it('logs the error thrown', () => { - expect(logErrorLogger.error).toBeCalledWith('Could not find user with given ID', user.id) + expect(logErrorLogger.error).toBeCalledWith( + 'Could not find user with given ID', + user.id, + ) }) }) }) @@ -2187,7 +2208,9 @@ describe('UserResolver', () => { }) it('logs the error thrown', () => { - expect(logger.warn).toBeCalledWith('findUserByEmail failed, user with email=invalid not found') + expect(logger.warn).toBeCalledWith( + 'findUserByEmail failed, user with email=invalid not found', + ) }) }) @@ -2331,7 +2354,10 @@ describe('UserResolver', () => { }) it('logs the error thrown', () => { - expect(logErrorLogger.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, + ) }) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 861ee3622..2dc571901 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -525,8 +525,13 @@ export class UserResolver { 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)}`) + 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`, ) @@ -751,7 +756,7 @@ export class UserResolver { '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`) @@ -1095,13 +1100,13 @@ export class UserResolver { ): Promise { const logger = createLogger() email = email.trim().toLowerCase() - const user = await findUserByEmail(email) + const user = await findUserByEmail(email) logger.addContext('user', user.id) logger.info('sendActivationEmail...') if (user.deletedAt || user.emailContact.deletedAt) { 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() @@ -1165,7 +1170,7 @@ export async function findUserByEmail(email: string): Promise { relations: { userRoles: true, emailContact: true }, }) return dbUser - } catch(e) { + } catch (e) { const logger = createLogger() if (e instanceof EntityNotFoundError) { // TODO: discuss if it is ok to print email in log for this case diff --git a/config-schema/biome.json b/config-schema/biome.json deleted file mode 100644 index 5b6f222fc..000000000 --- a/config-schema/biome.json +++ /dev/null @@ -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", "testSetup.ts"], - "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": {} } } - ] -} diff --git a/config-schema/src/commonSchema.ts b/config-schema/src/commonSchema.ts index bee046c7a..12946fdae 100644 --- a/config-schema/src/commonSchema.ts +++ b/config-schema/src/commonSchema.ts @@ -127,7 +127,9 @@ export const LOG4JS_CONFIG = Joi.string() 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') + .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() diff --git a/config-schema/src/const.ts b/config-schema/src/const.ts index 87df0f804..1a593f101 100644 --- a/config-schema/src/const.ts +++ b/config-schema/src/const.ts @@ -1 +1 @@ -export const DECAY_START_TIME = new Date('2021-05-13T17:46:31Z') \ No newline at end of file +export const DECAY_START_TIME = new Date('2021-05-13T17:46:31Z') diff --git a/config-schema/src/log4js-config/coloredContext.test.ts b/config-schema/src/log4js-config/coloredContext.test.ts index 38bde4d96..56a7b7b64 100644 --- a/config-schema/src/log4js-config/coloredContext.test.ts +++ b/config-schema/src/log4js-config/coloredContext.test.ts @@ -1,6 +1,6 @@ -import { createColoredContextLayout } from './coloredContext' -import { levels, LoggingEvent } from 'log4js' +import { LoggingEvent, levels } from 'log4js' import colors from 'yoctocolors-cjs' +import { createColoredContextLayout } from './coloredContext' let defaultLogEvent: LoggingEvent let colorFn: (input: string) => string @@ -76,13 +76,13 @@ describe('createColoredContextLayout', () => { }) 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`, - ) + expect( + createColoredContextLayout({ + withStack: 'error', + withFile: 'warn', + withLine: true, + })(defaultLogEvent), + ).toBe(`${coloredString} user=1 message`) }) }) @@ -123,13 +123,13 @@ describe('createColoredContextLayout', () => { }) 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`, - ) + expect( + createColoredContextLayout({ + withStack: 'error', + withFile: 'warn', + withLine: true, + })(defaultLogEvent), + ).toBe(`${coloredString} user=1 message \nstack`) }) }) }) diff --git a/config-schema/src/log4js-config/coloredContext.ts b/config-schema/src/log4js-config/coloredContext.ts index 0fc7476a8..f8be6b003 100644 --- a/config-schema/src/log4js-config/coloredContext.ts +++ b/config-schema/src/log4js-config/coloredContext.ts @@ -1,6 +1,6 @@ import { Level, LoggingEvent } from 'log4js' import colors from 'yoctocolors-cjs' -import { LogLevel, ColoredContextLayoutConfig } from './types' +import { ColoredContextLayoutConfig, LogLevel } from './types' function colorize(str: string, level: Level): string { switch (level.colour) { @@ -68,7 +68,10 @@ enum DetailKind { File = 'file', Line = 'line', } -function resolveDetailKind(logEvent: LoggingEvent, config: ColoredContextLayoutConfig): DetailKind | undefined { +function resolveDetailKind( + logEvent: LoggingEvent, + config: ColoredContextLayoutConfig, +): DetailKind | undefined { if (logEvent.callStack && isEnabledByLogLevel(logEvent.level, config.withStack)) { return DetailKind.Callstack } @@ -99,7 +102,7 @@ export function createColoredContextLayout(config: ColoredContextLayoutConfig) { result.push(composeContextString(logEvent.context)) } result.push(composeDataString(logEvent.data)) - + if (detailKind === DetailKind.File) { result.push(`\n at ${logEvent.fileName}:${logEvent.lineNumber}`) } diff --git a/config-schema/src/log4js-config/index.test.ts b/config-schema/src/log4js-config/index.test.ts index 5f5ea6b5b..fbc722c0a 100644 --- a/config-schema/src/log4js-config/index.test.ts +++ b/config-schema/src/log4js-config/index.test.ts @@ -2,9 +2,7 @@ import { createLog4jsConfig, defaultCategory } from '.' describe('createLog4jsConfig', () => { it('should create a log4js config', () => { - const config = createLog4jsConfig([ - defaultCategory('test', 'debug') - ]) + const config = createLog4jsConfig([defaultCategory('test', 'debug')]) expect(config).toBeDefined() expect(config.appenders).toBeDefined() expect(config.categories).toBeDefined() @@ -21,8 +19,8 @@ describe('createLog4jsConfig', () => { layout: { type: 'coloredContext', withStack: 'error', - withLine: true - } + withLine: true, + }, }) }) -}) \ No newline at end of file +}) diff --git a/config-schema/src/log4js-config/types/Category.ts b/config-schema/src/log4js-config/types/Category.ts index 880fd141b..3be4e24b3 100644 --- a/config-schema/src/log4js-config/types/Category.ts +++ b/config-schema/src/log4js-config/types/Category.ts @@ -1,5 +1,5 @@ -import { LogLevel } from './LogLevel' import { ColoredContextLayoutConfig } from './ColoredContextLayoutConfig' +import { LogLevel } from './LogLevel' /** * Configuration for a log4js category. diff --git a/config-schema/src/log4js-config/types/ColoredContextLayoutConfig.ts b/config-schema/src/log4js-config/types/ColoredContextLayoutConfig.ts index 7084adfa1..1a01f666f 100644 --- a/config-schema/src/log4js-config/types/ColoredContextLayoutConfig.ts +++ b/config-schema/src/log4js-config/types/ColoredContextLayoutConfig.ts @@ -4,4 +4,4 @@ export type ColoredContextLayoutConfig = { withStack?: LogLevel | boolean withFile?: LogLevel | boolean withLine?: LogLevel | boolean -} \ No newline at end of file +} diff --git a/config-schema/src/log4js-config/types/CustomFileAppender.ts b/config-schema/src/log4js-config/types/CustomFileAppender.ts index 5bb67769c..eab3250a3 100644 --- a/config-schema/src/log4js-config/types/CustomFileAppender.ts +++ b/config-schema/src/log4js-config/types/CustomFileAppender.ts @@ -1,5 +1,5 @@ -import { LogLevel } from './LogLevel' import { ColoredContextLayoutConfig } from './ColoredContextLayoutConfig' +import { LogLevel } from './LogLevel' /** * use default dateFile Template for custom file appenders * diff --git a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts index 781da1a54..e7bf40bed 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts @@ -2,8 +2,8 @@ import { CONFIG } from '@/config' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { createServer } from '@/server/createServer' import { createTestClient } from 'apollo-server-testing' -import { getLogger } from 'log4js' import { Community as DbCommunity } from 'database' +import { getLogger } from 'log4js' import { DataSource } from 'typeorm' let query: any diff --git a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts index c6d516342..13ec75dfc 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts @@ -2,8 +2,8 @@ import { CONFIG } from '@/config' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { createServer } from '@/server/createServer' import { createTestClient } from 'apollo-server-testing' -import { getLogger } from 'log4js' import { FederatedCommunity as DbFederatedCommunity } from 'database' +import { getLogger } from 'log4js' let query: any diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts index 6ec69493e..d41ea1936 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -3,10 +3,10 @@ import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { fullName } from '@/graphql/util/fullName' import { cleanDB, testEnvironment } from '@test/helpers' import { ApolloServerTestClient } from 'apollo-server-testing' -import { getLogger } from 'log4js' import { Community as DbCommunity, User as DbUser, UserContact as DbUserContact } from 'database' import Decimal from 'decimal.js-light' import { GraphQLError } from 'graphql' +import { getLogger } from 'log4js' import { DataSource } from 'typeorm' import { SendCoinsArgs } from '../model/SendCoinsArgs' diff --git a/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.test.ts b/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.test.ts index 9d543eec5..40db6db76 100644 --- a/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.test.ts +++ b/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.test.ts @@ -2,8 +2,8 @@ import { CONFIG } from '@/config' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { createServer } from '@/server/createServer' import { createTestClient } from 'apollo-server-testing' -import { getLogger } from 'log4js' import { FederatedCommunity as DbFederatedCommunity } from 'database' +import { getLogger } from 'log4js' let query: any diff --git a/federation/src/index.ts b/federation/src/index.ts index de2634465..4492f24fb 100644 --- a/federation/src/index.ts +++ b/federation/src/index.ts @@ -8,7 +8,7 @@ import { CONFIG } from './config' import { LOG4JS_BASE_CATEGORY_NAME } from './config/const' async function main() { - // init logger + // init logger const log4jsConfigFileName = CONFIG.LOG4JS_CONFIG_PLACEHOLDER.replace('%v', CONFIG.FEDERATION_API) initLogger( [defaultCategory('federation', CONFIG.LOG_LEVEL), defaultCategory('apollo', CONFIG.LOG_LEVEL)], From b8807f845226d6c8a18437dee8113abda1a71895 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 17 Jun 2025 17:29:13 +0200 Subject: [PATCH 13/46] some fixes --- backend/src/graphql/resolver/UserResolver.test.ts | 4 ++++ dht-node/src/dht_node/index.test.ts | 4 +++- dht-node/src/dht_node/index.ts | 4 +--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 806ae6bda..9a8c223f1 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -698,6 +698,9 @@ describe('UserResolver', () => { }) describe('no users in database', () => { + beforeAll(() => { + clearLogs() + }) it('throws an error', async () => { jest.clearAllMocks() const result = await mutate({ mutation: login, variables }) @@ -709,6 +712,7 @@ describe('UserResolver', () => { }) it('logs the error found', () => { + printLogs() expect(logger.warn).toBeCalledWith( `findUserByEmail failed, user with email=${variables.email} not found`, ) diff --git a/dht-node/src/dht_node/index.test.ts b/dht-node/src/dht_node/index.test.ts index 9953bee9b..20cc52057 100644 --- a/dht-node/src/dht_node/index.test.ts +++ b/dht-node/src/dht_node/index.test.ts @@ -3,7 +3,7 @@ import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } import { validate as validateUUID, version as versionUUID } from 'uuid' import { cleanDB, testEnvironment } from '@test/helpers' -import { cleanLogs, getLogger, printLogs } from 'config-schema/test/testSetup' +import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' import { CONFIG } from '@/config' @@ -358,6 +358,7 @@ describe('federation', () => { let jsonArray: any[] let result: DbFederatedCommunity[] = [] beforeAll(async () => { + clearLogs() jest.clearAllMocks() jsonArray = [ { @@ -373,6 +374,7 @@ describe('federation', () => { ] await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) result = await DbFederatedCommunity.find({ where: { foreign: true } }) + printLogs() }) afterAll(async () => { diff --git a/dht-node/src/dht_node/index.ts b/dht-node/src/dht_node/index.ts index 394c5d86c..dc45b15ed 100644 --- a/dht-node/src/dht_node/index.ts +++ b/dht-node/src/dht_node/index.ts @@ -60,7 +60,6 @@ export const startDHT = async (topic: string): Promise => { server.on('connection', function (socket: any) { logger.info(`server on... with Remote public key: ${socket.remotePublicKey.toString('hex')}`) - socket.on('data', async (data: Buffer) => { try { if (data.length > 1141) { @@ -110,10 +109,9 @@ export const startDHT = async (topic: string): Promise => { const variables = { apiVersion: recApiVersion.api, endPoint: recApiVersion.url, - publicKey: socket.remotePublicKey.toString('hex'), + publicKey: socket.remotePublicKey, lastAnnouncedAt: new Date(), } - logger.debug(`upsert with variables=${JSON.stringify(variables, null, 2)}`) // this will NOT update the updatedAt column, to distingue between a normal update and the last announcement await DbFederatedCommunity.createQueryBuilder() .insert() From 7162d39b924fcf383f03bc6c1893ef9018dccd90 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 17 Jun 2025 17:44:55 +0200 Subject: [PATCH 14/46] update biome --- backend/package.json | 6 ++- biome.json | 48 +++++++++++---------- bun.lock | 30 ++++++------- config-schema/package.json | 2 +- database/biome.json | 40 ++++++++++------- database/package.json | 4 +- dht-node/package.json | 2 +- federation/package.json | 6 ++- package.json | 2 +- yarn.lock | 88 +++++++++++++++++++------------------- 10 files changed, 121 insertions(+), 107 deletions(-) diff --git a/backend/package.json b/backend/package.json index 8673ff42d..aeb6fcb5e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,7 +25,9 @@ "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", diff --git a/biome.json b/biome.json index 0f2cc7978..13ccfdbe9 100644 --- a/biome.json +++ b/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": {} } } ] } diff --git a/bun.lock b/bun.lock index cf0f46bb5..ba6e52ff1 100644 --- a/bun.lock +++ b/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", @@ -169,7 +169,7 @@ "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", @@ -197,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", @@ -213,7 +213,7 @@ "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", @@ -250,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", @@ -479,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=="], diff --git a/config-schema/package.json b/config-schema/package.json index d5d0494f1..c127bcd91 100644 --- a/config-schema/package.json +++ b/config-schema/package.json @@ -23,7 +23,7 @@ "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" diff --git a/database/biome.json b/database/biome.json index 786069e9b..d27c2426d 100644 --- a/database/biome.json +++ b/database/biome.json @@ -1,10 +1,18 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "root": false, + "$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": ["./src/**/*.ts", "./entity/**/*.ts", "./logging/**/*.ts", "./migrations/**/*.ts"] + "includes": [ + "src/**/*.ts", + "entity/**/*.ts", + "logging/**/*.ts", + "migrations/**/*.ts", + "!**/build", + "!**/node_modules", + "!**/coverage" + ] }, "formatter": { "enabled": true, @@ -17,14 +25,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", @@ -32,10 +39,11 @@ "noUselessTernary": "error", "noUselessUndefinedInitialization": "error", "noVoid": "error", - "noWith": "error", "useArrowFunction": "off", "useLiteralKeys": "error", - "useRegexLiterals": "error" + "useRegexLiterals": "error", + "noAdjacentSpacesInRegex": "error", + "noCommaOperator": "error" }, "correctness": { "noConstAssign": "error", @@ -46,7 +54,6 @@ "noInnerDeclarations": "error", "noInvalidConstructorSuper": "error", "noInvalidUseBeforeDeclaration": "error", - "noNewSymbol": "error", "noNodejsModules": "off", "noNonoctalDecimalEscape": "error", "noPrecisionLoss": "error", @@ -60,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", "useSingleVarDeclarator": "error", - "useThrowOnlyError": "error" + "useThrowOnlyError": "error", + "useArrayLiterals": "error" }, "suspicious": { "noAssignInExpressions": "error", @@ -106,10 +113,11 @@ "noUnsafeNegation": "error", "useDefaultSwitchClauseLast": "error", "useGetterReturn": "error", - "useValidTypeof": "error" + "noWith": "error", + "noVar": "warn" } }, - "ignore": ["**/node_modules", "**/*.min.js", "**/build"] + "includes": ["**", "!**/node_modules", "!**/*.min.js", "!**/build"] }, "javascript": { "formatter": { @@ -127,7 +135,7 @@ }, "overrides": [ { - "include": ["*.ts", "*.tsx"], + "includes": ["**/*.ts", "**/*.tsx"], "linter": { "rules": { "complexity": { "noVoid": "error" } } } } ] diff --git a/database/package.json b/database/package.json index 6aa4002c7..456b84201 100644 --- a/database/package.json +++ b/database/package.json @@ -28,14 +28,14 @@ "up:dht_test": "cross-env TZ=UTC DB_DATABASE=gradido_test_dht tsx migration/index.ts up" }, "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", "typescript": "^4.9.5" }, "dependencies": { - "@types/uuid": "^8.3.4", + "@types/uuid": "^8.3.4", "cross-env": "^7.0.3", "decimal.js-light": "^2.5.1", "dotenv": "^10.0.0", diff --git a/dht-node/package.json b/dht-node/package.json index 1f4f7c7df..a973f7087 100644 --- a/dht-node/package.json +++ b/dht-node/package.json @@ -25,7 +25,7 @@ "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", diff --git a/federation/package.json b/federation/package.json index 95c1e5337..3e15b8e14 100644 --- a/federation/package.json +++ b/federation/package.json @@ -26,7 +26,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", @@ -68,7 +68,9 @@ "uuid": "8.3.2" }, "nodemonConfig": { - "ignore": ["**/*.test.ts"] + "ignore": [ + "**/*.test.ts" + ] }, "engines": { "node": ">=18" diff --git a/package.json b/package.json index c5c713f79..dd4e8b802 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "uuid": "^8.3.2" }, "devDependencies": { - "@biomejs/biome": "1.9.4" + "@biomejs/biome": "2.0.0" }, "engines": { "node": ">=18" diff --git a/yarn.lock b/yarn.lock index 015b98c5d..b8a1c2f43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -386,59 +386,59 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@biomejs/biome@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" - integrity sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog== +"@biomejs/biome@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-2.0.0.tgz#dc770781565640b9f884ad3d6d6383f64ca257c2" + integrity sha512-BlUoXEOI/UQTDEj/pVfnkMo8SrZw3oOWBDrXYFT43V7HTkIUDkBRY53IC5Jx1QkZbaB+0ai1wJIfYwp9+qaJTQ== 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" + "@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" -"@biomejs/cli-darwin-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f" - integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== +"@biomejs/cli-darwin-arm64@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0.tgz#67135faa8bd52933fdaad09a160f9fc3a9defef3" + integrity sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw== -"@biomejs/cli-darwin-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" - integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== +"@biomejs/cli-darwin-x64@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.0.0.tgz#49a7e064bad53e095d8a152b072adffcdeb4fb8e" + integrity sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw== -"@biomejs/cli-linux-arm64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" - integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== +"@biomejs/cli-linux-arm64-musl@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0.tgz#bfde27de8262a20e57e153f3807f47a01ccaeab3" + integrity sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA== -"@biomejs/cli-linux-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" - integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== +"@biomejs/cli-linux-arm64@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.0.0.tgz#c2404b3869c03a6fa5a8b979755bc6dfd5a5ec47" + integrity sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw== -"@biomejs/cli-linux-x64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb" - integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== +"@biomejs/cli-linux-x64-musl@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0.tgz#8ab214ac7e21a2af29435439145888c50f2bdd2f" + integrity sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA== -"@biomejs/cli-linux-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8" - integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== +"@biomejs/cli-linux-x64@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-2.0.0.tgz#cbd172ead9e5bba8cd590d06e6e548445cf7ab2a" + integrity sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg== -"@biomejs/cli-win32-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" - integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== +"@biomejs/cli-win32-arm64@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.0.0.tgz#4677f4e034b3f4906e448b704f3314b38062a111" + integrity sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw== -"@biomejs/cli-win32-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" - integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== +"@biomejs/cli-win32-x64@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-2.0.0.tgz#f460a6950235c8f4bbd4cc405b210fec5cdb8ac9" + integrity sha512-2USVQ0hklNsph/KIR72ZdeptyXNnQ3JdzPn3NbjI4Sna34CnxeiYAaZcZzXPDl5PYNFBivV4xmvT3Z3rTmyDBg== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" From 57bace26981f7ba399a7ae2094e9f0c4e6c49b2c Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 17 Jun 2025 19:24:51 +0200 Subject: [PATCH 15/46] fix wrong position of initLogging call --- backend/package.json | 2 +- backend/src/apis/gms/ExportUsers.ts | 2 +- backend/src/apis/humhub/ExportUsers.ts | 3 +-- backend/src/seeds/index.ts | 2 +- backend/src/util/executeKlicktipp.ts | 2 ++ 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index aeb6fcb5e..d6740e4ae 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,7 +12,7 @@ "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", + "seed": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_backend 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", "humhubUserExport": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/humhub/ExportUsers.ts", diff --git a/backend/src/apis/gms/ExportUsers.ts b/backend/src/apis/gms/ExportUsers.ts index fb7cc81dc..aa210707c 100644 --- a/backend/src/apis/gms/ExportUsers.ts +++ b/backend/src/apis/gms/ExportUsers.ts @@ -19,6 +19,7 @@ CONFIG.EMAIL = false const forceMode = process.argv.includes('--force') async function main() { + initLogging() // open mysql connection const con = AppDatabase.getInstance() await con.init() @@ -74,7 +75,6 @@ async function main() { } main().catch((e) => { - initLogging() logger.error(e) process.exit(1) }) diff --git a/backend/src/apis/humhub/ExportUsers.ts b/backend/src/apis/humhub/ExportUsers.ts index c0746ceb4..5eb3f8d6c 100644 --- a/backend/src/apis/humhub/ExportUsers.ts +++ b/backend/src/apis/humhub/ExportUsers.ts @@ -62,7 +62,7 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise { - initLogging() logger.error(e) process.exit(1) }) diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts index d99e33872..506c93d41 100644 --- a/backend/src/seeds/index.ts +++ b/backend/src/seeds/index.ts @@ -50,6 +50,7 @@ const resetEntity = async (entity: any) => { } const run = async () => { + initLogging() const server = await createServer(getLogger('apollo'), context) const seedClient = createTestClient(server.apollo) const { con } = server @@ -99,7 +100,6 @@ const run = async () => { } run().catch((err) => { - initLogging() // biome-ignore lint/suspicious/noConsole: no logger present console.error('error on seeding', err) }) diff --git a/backend/src/util/executeKlicktipp.ts b/backend/src/util/executeKlicktipp.ts index eec525bac..47db2c735 100644 --- a/backend/src/util/executeKlicktipp.ts +++ b/backend/src/util/executeKlicktipp.ts @@ -1,8 +1,10 @@ import { AppDatabase } from 'database' import { exportEventDataToKlickTipp } from './klicktipp' +import { initLogging } from '@/server/logger' async function executeKlicktipp(): Promise { + initLogging() const connection = AppDatabase.getInstance() await connection.init() if (connection.isConnected()) { From 89a6ed33506006bfe63b755e021452af5247b5b1 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 17 Jun 2025 19:31:33 +0200 Subject: [PATCH 16/46] add some comment for test logger mock --- config-schema/test/testSetup.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/config-schema/test/testSetup.ts b/config-schema/test/testSetup.ts index c1d679d8a..ef471cc03 100644 --- a/config-schema/test/testSetup.ts +++ b/config-schema/test/testSetup.ts @@ -1,3 +1,10 @@ +/* + * This file is used to mock the log4js logger in the tests. + * It is used to collect all log entries in the logs array. + * If you want to debug your test, you can use `printLogs()` to print all log entries collected through the tests. + * To have only the relevant logs, call `clearLogs()` before your calling the methods you like to test and `printLogs()` after. + */ + jest.setTimeout(1000000) type LogEntry = { From ae1f9745b302c53af231b48007734f9eaab98db7 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 17 Jun 2025 19:37:53 +0200 Subject: [PATCH 17/46] ignore tests for tsc --- config-schema/tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config-schema/tsconfig.json b/config-schema/tsconfig.json index 04306edad..d65460927 100644 --- a/config-schema/tsconfig.json +++ b/config-schema/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ @@ -69,5 +68,6 @@ "skipLibCheck": true, /* Skip type checking of declaration files. */ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, - "references": [] /* Any project that is referenced must itself have a `references` array (which may be empty). */ + "references": [], /* Any project that is referenced must itself have a `references` array (which may be empty). */ + "exclude": ["**/*.test.ts", "**/*.spec.ts", "test/*"], } From 26c1c7f6d70a999cfc5066c66647b39fa04c4618 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 18 Jun 2025 08:03:25 +0200 Subject: [PATCH 18/46] remove using test db for seeding for debugging --- backend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index d6740e4ae..aeb6fcb5e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,7 +12,7 @@ "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 DB_DATABASE=gradido_test_backend ts-node -r tsconfig-paths/register src/seeds/index.ts", + "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", "humhubUserExport": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/apis/humhub/ExportUsers.ts", From bedafc45922b4360188022cb8742fad0b080d098 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 18 Jun 2025 08:06:29 +0200 Subject: [PATCH 19/46] rollback --- dlt-connector/src/server/createServer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/dlt-connector/src/server/createServer.ts b/dlt-connector/src/server/createServer.ts index 53bf3ab97..25e0a12e2 100755 --- a/dlt-connector/src/server/createServer.ts +++ b/dlt-connector/src/server/createServer.ts @@ -26,6 +26,7 @@ const createServer = async ( logger: Logger = dltLogger, // localization: i18n.I18n = i18n, ): Promise => { + logger.addContext('user', 'unknown') logger.debug('createServer...') // connect to db and test db version From d50c2fd2761efe12fbc34400817aa58fc4f97057 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 18 Jun 2025 09:40:46 +0200 Subject: [PATCH 20/46] use inspect instead of json.serialize for raw objects to prevent errors with circular objects --- config-schema/src/log4js-config/coloredContext.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config-schema/src/log4js-config/coloredContext.ts b/config-schema/src/log4js-config/coloredContext.ts index f8be6b003..1ebb6b219 100644 --- a/config-schema/src/log4js-config/coloredContext.ts +++ b/config-schema/src/log4js-config/coloredContext.ts @@ -1,6 +1,7 @@ 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) { @@ -33,7 +34,7 @@ function composeDataString(data: (string | Object)[]): string { .map((d) => { // if it is a object and his toString function return only garbage if (typeof d === 'object' && d.toString() === '[object Object]') { - return JSON.stringify(d) + return inspect(d, ) } if (d) { return d.toString() From 8a68f5756939cfa86a0f837942ee30eacf81acd4 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 18 Jun 2025 11:46:11 +0200 Subject: [PATCH 21/46] make things more stable --- backend/src/graphql/resolver/UserResolver.ts | 2 +- backend/src/graphql/resolver/util/processXComSendCoins.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 2dc571901..14be59307 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -1172,7 +1172,7 @@ export async function findUserByEmail(email: string): Promise { return dbUser } catch (e) { const logger = createLogger() - if (e instanceof EntityNotFoundError) { + if (e instanceof EntityNotFoundError || (e as Error).name === 'EntityNotFoundError') { // TODO: discuss if it is ok to print email in log for this case logger.warn(`findUserByEmail failed, user with email=${email} not found`) } else { diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 7115d3d1f..572a50f97 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -70,7 +70,7 @@ export async function processXComPendingSendCoins( const receiverFCom = await DbFederatedCommunity.findOneOrFail({ where: { - publicKey: receiverCom.publicKey, + publicKey: Buffer.from(receiverCom.publicKey), apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API, }, }) @@ -194,7 +194,7 @@ export async function processXComCommittingSendCoins( logger.debug('find pending Tx for settlement:', pendingTx) const receiverFCom = await DbFederatedCommunity.findOneOrFail({ where: { - publicKey: receiverCom.publicKey, + publicKey: Buffer.from(receiverCom.publicKey), apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API, }, From 91ab2914c209b9259743d218e5ffc4149659fe4d Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 20 Jun 2025 16:45:02 +0200 Subject: [PATCH 22/46] queries in database, shared initalized with validate user as example --- .github/file-filters.yml | 3 + .github/workflows/test_database.yml | 6 +- .github/workflows/test_shared.yml | 44 + .gitignore | 2 + .vscode/launch.json | 33 + backend/src/apis/gms/ExportUsers.ts | 6 +- .../src/graphql/resolver/CommunityResolver.ts | 3 +- .../resolver/TransactionLinkResolver.ts | 8 +- backend/src/graphql/resolver/UserResolver.ts | 24 +- .../src/graphql/resolver/util/communities.ts | 10 - bun.lock | 197 ++- config-schema/package.json | 3 +- config-schema/test/testSetup.bun.ts | 99 ++ database/package.json | 19 +- database/src/AppDatabase.ts | 3 +- database/src/config/index.ts | 6 +- database/src/index.ts | 1 + database/src/queries/communities.test.ts | 40 + database/src/queries/communities.ts | 11 + database/src/queries/index.ts | 2 + database/src/queries/user.test.ts | 42 + database/src/queries/user.ts | 9 + database/src/seeds/factory/user.ts | 37 + database/src/seeds/homeCommunity.ts | 26 + database/src/seeds/users/UserInterface.ts | 13 + database/src/seeds/users/bibi-bloxberg.ts | 12 + database/src/seeds/users/bob-baumeister.ts | 11 + .../src/seeds/users/garrick-ollivander.ts | 12 + database/src/seeds/users/index.ts | 15 + database/src/seeds/users/peter-lustig.ts | 11 + .../src/seeds/users/raeuber-hotzenplotz.ts | 10 + database/src/seeds/users/stephen-hawking.ts | 12 + database/src/seeds/utils.ts | 10 + database/tsconfig.json | 7 +- database/turbo.json | 6 + database/yarn.lock | 1060 ----------------- dht-node/src/dht_node/index.ts | 3 +- dht-node/tsconfig.json | 2 +- shared/bunfig.toml | 2 + shared/package.json | 7 +- shared/src/config/const.ts | 1 + shared/src/index.test.ts | 5 - shared/src/index.ts | 3 + shared/src/schema/index.ts | 4 + shared/src/schema/user.schema.test.ts | 181 +++ shared/src/schema/user.schema.ts | 54 + shared/src/server/LogError.ts | 8 - shared/src/server/logger.ts | 21 - shared/src/user/schema/alias.test.ts | 97 -- shared/src/user/schema/alias.ts | 66 - shared/tsconfig.json | 6 +- shared/turbo.json | 5 +- yarn.lock | 690 ++++++++++- 53 files changed, 1651 insertions(+), 1317 deletions(-) create mode 100644 .github/workflows/test_shared.yml create mode 100644 config-schema/test/testSetup.bun.ts create mode 100644 database/src/queries/communities.test.ts create mode 100644 database/src/queries/communities.ts create mode 100644 database/src/queries/index.ts create mode 100644 database/src/queries/user.test.ts create mode 100644 database/src/queries/user.ts create mode 100644 database/src/seeds/factory/user.ts create mode 100644 database/src/seeds/homeCommunity.ts create mode 100644 database/src/seeds/users/UserInterface.ts create mode 100644 database/src/seeds/users/bibi-bloxberg.ts create mode 100644 database/src/seeds/users/bob-baumeister.ts create mode 100644 database/src/seeds/users/garrick-ollivander.ts create mode 100644 database/src/seeds/users/index.ts create mode 100644 database/src/seeds/users/peter-lustig.ts create mode 100644 database/src/seeds/users/raeuber-hotzenplotz.ts create mode 100644 database/src/seeds/users/stephen-hawking.ts create mode 100644 database/src/seeds/utils.ts delete mode 100644 database/yarn.lock create mode 100644 shared/bunfig.toml create mode 100644 shared/src/config/const.ts delete mode 100644 shared/src/index.test.ts create mode 100644 shared/src/schema/index.ts create mode 100644 shared/src/schema/user.schema.test.ts create mode 100644 shared/src/schema/user.schema.ts delete mode 100644 shared/src/server/LogError.ts delete mode 100644 shared/src/server/logger.ts delete mode 100644 shared/src/user/schema/alias.test.ts delete mode 100644 shared/src/user/schema/alias.ts diff --git a/.github/file-filters.yml b/.github/file-filters.yml index 7dc1a3cba..15791d4b1 100644 --- a/.github/file-filters.yml +++ b/.github/file-filters.yml @@ -36,6 +36,9 @@ backend: &backend config: &config - 'config-schema/**/*' +shared: &shared + - 'shared/**/*' + database: &database - 'database/**/*' diff --git a/.github/workflows/test_database.yml b/.github/workflows/test_database.yml index a234c7eec..a4e6de787 100644 --- a/.github/workflows/test_database.yml +++ b/.github/workflows/test_database.yml @@ -35,7 +35,7 @@ jobs: database_migration_test: if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' - name: Database Migration Test - Up + Reset + name: Database Migration Test - Up, Test + Reset needs: files-changed runs-on: ubuntu-latest steps: @@ -58,8 +58,8 @@ jobs: bun install --filter database --frozen-lockfile bun install --global --no-save turbo@^2 - - name: Database | up - run: turbo up + - name: Database | up + test + run: turbo test - name: Database | reset run: turbo reset diff --git a/.github/workflows/test_shared.yml b/.github/workflows/test_shared.yml new file mode 100644 index 000000000..5fa1068da --- /dev/null +++ b/.github/workflows/test_shared.yml @@ -0,0 +1,44 @@ +name: Gradido Shared Test CI + +on: push + +jobs: + files-changed: + name: Detect File Changes - Shared + runs-on: ubuntu-latest + outputs: + shared: ${{ steps.changes.outputs.shared }} + docker-compose: ${{ steps.changes.outputs.docker-compose }} + database: ${{ steps.changes.outputs.database }} + steps: + - uses: actions/checkout@v3.3.0 + + - name: Check for shared file changes + uses: dorny/paths-filter@v2.11.1 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + list-files: shell + + build: + name: Unit Tests, typecheck - Shared + if: needs.files-changed.outputs.shared == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.database == 'true' + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: install bun + uses: oven-sh/setup-bun@v2 + + - name: install dependencies + run: bun install --filter shared database --frozen-lockfile + + - name: typecheck + run: cd shared && yarn typecheck + + - name: unit tests + run: cd shared && yarn test + diff --git a/.gitignore b/.gitignore index d96592bc8..d82288fbd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ vite.config.mjs.timestamp-* log4js-config*.json /node_modules/* +node_modules +build messages.pot nbproject .metadata diff --git a/.vscode/launch.json b/.vscode/launch.json index 5fcb62e09..30fa8f6ed 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,23 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Database Debug Tests", + "stopOnEntry": true, + "runtimeExecutable": "yarn", + "runtimeArgs": [ + "run", + "test" + ], + "skipFiles": [ + "/**" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "cwd": "${workspaceFolder}/database" + }, { "type": "node", "request": "launch", @@ -86,6 +103,22 @@ "console": "integratedTerminal", "internalConsoleOptions": "openOnSessionStart", "cwd": "${workspaceFolder}/backend" + }, + { + "type": "node", + "request": "launch", + "name": "Shared Debug Test", + "runtimeExecutable": "bun", + "runtimeArgs": [ + "run", + "test:debug" + ], + "skipFiles": [ + "/**" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "openOnSessionStart", + "cwd": "${workspaceFolder}/shared" } ] } \ No newline at end of file diff --git a/backend/src/apis/gms/ExportUsers.ts b/backend/src/apis/gms/ExportUsers.ts index aa210707c..36824bb71 100644 --- a/backend/src/apis/gms/ExportUsers.ts +++ b/backend/src/apis/gms/ExportUsers.ts @@ -5,11 +5,10 @@ import { LOG4JS_GMS_CATEGORY_NAME } from '@/apis/gms/index' // 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 { initLogging } from '@/server/logger' -import { AppDatabase } from 'database' +import { AppDatabase, getHomeCommunity } from 'database' import { getLogger } from 'log4js' const logger = getLogger(`${LOG4JS_GMS_CATEGORY_NAME}.ExportUsers`) @@ -25,6 +24,9 @@ async function main() { await con.init() const homeCom = await getHomeCommunity() + if (!homeCom) { + throw new LogError('HomeCommunity not found') + } if (homeCom.gmsApiKey === null) { throw new LogError('HomeCommunity needs GMS-ApiKey to publish user data to GMS.') } diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index ab1115a05..779e7db31 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -1,4 +1,4 @@ -import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } from 'database' +import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity, getHomeCommunity } from 'database' import { Arg, Args, Authorized, Mutation, Query, Resolver } from 'type-graphql' import { IsNull, Not } from 'typeorm' @@ -16,7 +16,6 @@ import { getAllCommunities, getCommunityByIdentifier, getCommunityByUuid, - getHomeCommunity, } from './util/communities' @Resolver() diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 34c073e45..8d442b447 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -21,6 +21,7 @@ import { Transaction as DbTransaction, TransactionLink as DbTransactionLink, User as DbUser, + getHomeCommunity, } from 'database' import { Decimal } from 'decimal.js-light' import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' @@ -49,7 +50,6 @@ import { executeTransaction } from './TransactionResolver' import { getAuthenticatedCommunities, getCommunityByUuid, - getHomeCommunity, } from './util/communities' import { getUserCreation, validateContribution } from './util/creations' import { getLastTransaction } from './util/getLastTransaction' @@ -442,6 +442,9 @@ export class TransactionLinkResolver { ) // TODO:encode/sign the jwt normally with the private key of the sender/home community, but interims with uuid const homeCom = await getHomeCommunity() + if (!homeCom) { + throw new LogError('Home community not found') + } if (!homeCom.communityUuid) { throw new LogError('Home community UUID is not set') } @@ -630,6 +633,9 @@ export class TransactionLinkResolver { ) } const homeCommunity = await getHomeCommunity() + if (!homeCommunity) { + throw new LogError('Home community not found') + } const recipientCommunity = new Community(homeCommunity) const senderCommunity = new Community(senderCom) const senderUser = new User(null) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 14be59307..197ff3e6c 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -93,7 +93,7 @@ import { Logger, getLogger } from 'log4js' import { FULL_CREATION_AVAILABLE } from './const/const' import { Location2Point, Point2Location } from './util/Location2Point' import { authenticateGmsUserPlayground } from './util/authenticateGmsUserPlayground' -import { getHomeCommunity } from './util/communities' +import { getHomeCommunity } from 'database' import { compareGmsRelevantUserSettings } from './util/compareGmsRelevantUserSettings' import { getUserCreations } from './util/creations' import { extractGraphQLFieldsForSelect } from './util/extractGraphQLFields' @@ -381,6 +381,10 @@ export class UserResolver { ) let dbUser = new DbUser() const homeCom = await getHomeCommunity() + if (!homeCom) { + logger.error('no home community found, please start the dht-node first') + throw new Error(`Error creating user, please write the support team: ${CONFIG.COMMUNITY_SUPPORT_MAIL}`) + } if (homeCom.communityUuid) { dbUser.communityUuid = homeCom.communityUuid } @@ -821,6 +825,12 @@ export class UserResolver { if (CONFIG.GMS_ACTIVE && updateUserInGMS) { logger.debug(`changed user-settings relevant for gms-user update...`) const homeCom = await getHomeCommunity() + if (!homeCom) { + logger.error('no home community found, please start the dht-node first') + throw new Error( + `Error updating user, please write the support team: ${CONFIG.COMMUNITY_SUPPORT_MAIL}` + ) + } if (homeCom.gmsApiKey !== null) { logger.debug(`send User to Gms...`) await sendUserToGms(user, homeCom) @@ -863,6 +873,12 @@ export class UserResolver { let result = new GmsUserAuthenticationResult() if (context.token) { const homeCom = await getHomeCommunity() + if (!homeCom) { + logger.error("couldn't authenticate for gms, no home community found, please start the dht-node first") + throw new Error( + `Error authenticating for gms, please write the support team: ${CONFIG.COMMUNITY_SUPPORT_MAIL}` + ) + } if (!homeCom.gmsApiKey) { throw new LogError('authenticateGmsUserSearch missing HomeCommunity GmsApiKey') } @@ -886,6 +902,12 @@ export class UserResolver { const result = new UserLocationResult() if (context.token) { const homeCom = await getHomeCommunity() + if (!homeCom) { + logger.error("couldn't load home community location, no home community found, please start the dht-node first") + throw new Error( + `Error loading user location, please write the support team: ${CONFIG.COMMUNITY_SUPPORT_MAIL}` + ) + } result.communityLocation = Point2Location(homeCom.location as Point) result.userLocation = Point2Location(dbUser.location as Point) logger.info('userLocation=', result) diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index 028116c99..ee7a0f96d 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -36,16 +36,6 @@ export async function isHomeCommunity(communityIdentifier: string): Promise { - return await DbCommunity.findOneOrFail({ - where: [{ foreign: false }], - }) -} - /** * TODO: Check if it is needed, because currently it isn't used at all * Retrieves the URL of the community with the given identifier. diff --git a/bun.lock b/bun.lock index e5b37a676..005ccc1ae 100644 --- a/bun.lock +++ b/bun.lock @@ -172,7 +172,7 @@ "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", "jest": "27.2.4", - "typescript": "^4.9.5", + "typescript": "^5.8.3", }, }, "database": { @@ -191,17 +191,27 @@ "reflect-metadata": "^0.1.13", "source-map-support": "^0.5.21", "ts-mysql-migrate": "^1.0.2", - "tsx": "^4.19.4", + "tsx": "^4.20.3", "typeorm": "^0.3.22", "uuid": "^8.3.2", "wkx": "^0.5.0", }, "devDependencies": { "@biomejs/biome": "2.0.0", + "@swc-node/register": "^1.10.10", + "@swc/cli": "^0.7.3", + "@swc/core": "^1.11.24", + "@swc/helpers": "^0.5.17", "@types/faker": "^5.5.9", "@types/geojson": "^7946.0.13", - "@types/node": "^17.0.21", + "@types/jest": "27.0.2", + "@types/node": "^18.7.14", + "jest": "27.2.4", + "ts-jest": "27.0.5", + "ts-node": "^10.9.2", "typescript": "^4.9.5", + "uuid": "^8.3.2", + "vitest": "^3.2.4", }, }, "dht-node": { @@ -393,12 +403,13 @@ "dependencies": { "database": "*", "esbuild": "^0.25.2", - "zod": "^3.25.20", + "log4js": "^6.9.1", + "zod": "^3.25.61", }, "devDependencies": { "@biomejs/biome": "1.9.4", "@types/node": "^17.0.21", - "typescript": "^4.9.5", + "typescript": "^5.8.3", }, }, }, @@ -529,6 +540,12 @@ "@dual-bundle/import-meta-resolve": ["@dual-bundle/import-meta-resolve@4.1.0", "", {}, "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg=="], + "@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" } }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="], @@ -745,6 +762,8 @@ "@napi-rs/nice-win32-x64-msvc": ["@napi-rs/nice-win32-x64-msvc@1.0.1", "", { "os": "win32", "cpu": "x64" }, "sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" } }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], @@ -755,6 +774,32 @@ "@one-ini/wasm": ["@one-ini/wasm@0.1.1", "", {}, "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw=="], + "@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@5.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hXem5ZAguS7IlSiHg/LK0tEfLj4eUo+9U6DaFwwBEGd0L0VIF9LmuiHydRyOrdnnmi9iAAFMAn/wl2cUoiuruA=="], + + "@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@5.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-wgSwfsZkRbuYCIBLxeg1bYrtKnirAy+IJF0lwfz4z08clgdNBDbfGECJe/cd0csIZPpRcvPFe8317yf31sWhtA=="], + + "@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@5.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kzeE2WHgcRMmWjB071RdwEV5Pwke4o0WWslCKoh8if1puvxIxfzu3o7g6P2+v77BP5qop4cri+uvLABSO0WZjg=="], + + "@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@5.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-I8np34yZP/XfIkZNDbw3rweqVgfjmHYpNX3xnJZWg+f4mgO9/UNWBwetSaqXeDZqvIch/aHak+q4HVrQhQKCqg=="], + + "@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@5.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-u2ndfeEUrW898eXM+qPxIN8TvTPjI90NDQBRgaxxkOfNw3xaotloeiZGz5+Yzlfxgvxr9DY9FdYkqhUhSnGhOw=="], + + "@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@5.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-TzbjmFkcnESGuVItQ2diKacX8vu5G0bH3BHmIlmY4OSRLyoAlrJFwGKAHmh6C9+Amfcjo2rx8vdm7swzmsGC6Q=="], + + "@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@5.3.0", "", { "os": "linux", "cpu": "none" }, "sha512-NH3pjAqh8nuN29iRuRfTY42Vn03ctoR9VE8llfoUKUfhHUjFHYOXK5VSkhjj1usG8AeuesvqrQnLptCRQVTi/Q=="], + + "@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@5.3.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-tuZtkK9sJYh2MC2uhol1M/8IMTB6ZQ5jmqP2+k5XNXnOb/im94Y5uV/u2lXwVyIuKHZZHtr+0d1HrOiNahoKpw=="], + + "@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@5.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-VzhPYmZCtoES/ThcPdGSmMop7JlwgqtSvlgtKCW15ByV2JKyl8kHAHnPSBfpIooXb0ehFnRdxFtL9qtAEWy01g=="], + + "@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@5.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Hi39cWzul24rGljN4Vf1lxjXzQdCrdxO5oCT7KJP4ndSlqIUODJnfnMAP1YhcnIRvNvk+5E6sZtnEmFUd/4d8Q=="], + + "@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@5.3.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.9" }, "cpu": "none" }, "sha512-ddujvHhP3chmHnSXRlkPVUeYj4/B7eLZwL4yUid+df3WCbVh6DgoT9RmllZn21AhxgKtMdekDdyVJYKFd8tl4A=="], + + "@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@5.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-j1YYPLvUkMVNKmIFQZZJ7q6Do4cI3htUnyxNLwDSBVhSohvPIK2VG+IdtOAlWZGa7v+phEZsHfNbXVwB0oPYFQ=="], + + "@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@5.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-LT9eOPPUqfZscQRd5mc08RBeDWOQf+dnOrKnanMallTGPe6g7+rcAlFTA8SWoJbcD45PV8yArFtCmSQSpzHZmg=="], + "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], "@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.1", "", { "os": "android", "cpu": "arm64" }, "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA=="], @@ -873,6 +918,12 @@ "@sqltools/formatter": ["@sqltools/formatter@1.2.5", "", {}, "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw=="], + "@swc-node/core": ["@swc-node/core@1.13.3", "", { "peerDependencies": { "@swc/core": ">= 1.4.13", "@swc/types": ">= 0.1" } }, "sha512-OGsvXIid2Go21kiNqeTIn79jcaX4l0G93X2rAnas4LFoDyA9wAwVK7xZdm+QsKoMn5Mus2yFLCc4OtX2dD/PWA=="], + + "@swc-node/register": ["@swc-node/register@1.10.10", "", { "dependencies": { "@swc-node/core": "^1.13.3", "@swc-node/sourcemap-support": "^0.5.1", "colorette": "^2.0.20", "debug": "^4.3.5", "oxc-resolver": "^5.0.0", "pirates": "^4.0.6", "tslib": "^2.6.3" }, "peerDependencies": { "@swc/core": ">= 1.4.13", "typescript": ">= 4.3" } }, "sha512-jYWaI2WNEKz8KZL3sExd2KVL1JMma1/J7z+9iTpv0+fRN7LGMF8VTGGuHI2bug/ztpdZU1G44FG/Kk6ElXL9CQ=="], + + "@swc-node/sourcemap-support": ["@swc-node/sourcemap-support@0.5.1", "", { "dependencies": { "source-map-support": "^0.5.21", "tslib": "^2.6.3" } }, "sha512-JxIvIo/Hrpv0JCHSyRpetAdQ6lB27oFYhv0PKCNf1g2gUXOjpeR1exrXccRxLMuAV5WAmGFBwRnNOJqN38+qtg=="], + "@swc/cli": ["@swc/cli@0.7.3", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-rnVXNnlURjdOuPaBIwZ3TmBA44BF/eP0j154LanlgPEYfau74ige7cpKlKkZr1IBqMOG99lAnYNxQipDWA3hdg=="], "@swc/core": ["@swc/core@1.11.24", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.21" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.11.24", "@swc/core-darwin-x64": "1.11.24", "@swc/core-linux-arm-gnueabihf": "1.11.24", "@swc/core-linux-arm64-gnu": "1.11.24", "@swc/core-linux-arm64-musl": "1.11.24", "@swc/core-linux-x64-gnu": "1.11.24", "@swc/core-linux-x64-musl": "1.11.24", "@swc/core-win32-arm64-msvc": "1.11.24", "@swc/core-win32-ia32-msvc": "1.11.24", "@swc/core-win32-x64-msvc": "1.11.24" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg=="], @@ -919,6 +970,8 @@ "@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="], + "@types/accepts": ["@types/accepts@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ=="], "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], @@ -931,6 +984,8 @@ "@types/body-parser": ["@types/body-parser@1.19.5", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg=="], + "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], "@types/content-disposition": ["@types/content-disposition@0.5.8", "", {}, "sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg=="], @@ -939,6 +994,8 @@ "@types/cors": ["@types/cors@2.8.10", "", {}, "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + "@types/dotenv": ["@types/dotenv@8.2.3", "", { "dependencies": { "dotenv": "*" } }, "sha512-g2FXjlDX/cYuc5CiQvyU/6kkbP1JtmGzh0obW50zD7OKeILVL0NSpPWLXVfqoAGQjom2/SLLx9zHq0KXvD6mbw=="], "@types/email-templates": ["@types/email-templates@10.0.4", "", { "dependencies": { "@types/html-to-text": "*", "@types/nodemailer": "*", "juice": "^8.0.0" } }, "sha512-8O2bdGPO6RYgH2DrnFAcuV++s+8KNA5e2Erjl6UxgKRVsBH9zXu2YLrLyOBRMn2VyEYmzgF+6QQUslpVhj0y/g=="], @@ -2551,6 +2608,8 @@ "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + "oxc-resolver": ["oxc-resolver@5.3.0", "", { "optionalDependencies": { "@oxc-resolver/binding-darwin-arm64": "5.3.0", "@oxc-resolver/binding-darwin-x64": "5.3.0", "@oxc-resolver/binding-freebsd-x64": "5.3.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "5.3.0", "@oxc-resolver/binding-linux-arm64-gnu": "5.3.0", "@oxc-resolver/binding-linux-arm64-musl": "5.3.0", "@oxc-resolver/binding-linux-riscv64-gnu": "5.3.0", "@oxc-resolver/binding-linux-s390x-gnu": "5.3.0", "@oxc-resolver/binding-linux-x64-gnu": "5.3.0", "@oxc-resolver/binding-linux-x64-musl": "5.3.0", "@oxc-resolver/binding-wasm32-wasi": "5.3.0", "@oxc-resolver/binding-win32-arm64-msvc": "5.3.0", "@oxc-resolver/binding-win32-x64-msvc": "5.3.0" } }, "sha512-FHqtZx0idP5QRPSNcI5g2ItmADg7fhR3XIeWg5eRMGfp44xqRpfkdvo+EX4ZceqV9bxvl0Z8vaqMqY0gYaNYNA=="], + "p-cancelable": ["p-cancelable@3.0.0", "", {}, "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="], "p-event": ["p-event@4.2.0", "", { "dependencies": { "p-timeout": "^3.1.0" } }, "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ=="], @@ -2617,7 +2676,7 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="], @@ -3025,7 +3084,7 @@ "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], - "tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="], + "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], "tinypool": ["tinypool@1.0.2", "", {}, "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA=="], @@ -3077,7 +3136,7 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "tsx": ["tsx@4.19.4", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q=="], + "tsx": ["tsx@4.20.3", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ=="], "tua-body-scroll-lock": ["tua-body-scroll-lock@1.5.3", "", {}, "sha512-44W12iqek41kZuTdpEUt3JTUsMx0IxfTajXWfQyMLgzsPaMYUPZLcJkwa4P0x24h5DQ3lYvDuYvphBo4+L0t4w=="], @@ -3439,9 +3498,9 @@ "@nuxt/kit/pkg-types": ["pkg-types@2.1.0", "", { "dependencies": { "confbox": "^0.2.1", "exsolve": "^1.0.1", "pathe": "^2.0.3" } }, "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="], - "@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], + "@nuxt/kit/tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="], - "@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + "@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], "@selderee/plugin-htmlparser2/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="], @@ -3509,6 +3568,8 @@ "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "apollo-boost/ts-invariant": ["ts-invariant@0.4.4", "", { "dependencies": { "tslib": "^1.9.3" } }, "sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA=="], "apollo-boost/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], @@ -3591,12 +3652,22 @@ "concurrently/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "config-schema/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "css-select/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="], "css-select/domutils": ["domutils@2.8.0", "", { "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.2.0" } }, "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="], "cssstyle/rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], + "database/@swc/cli": ["@swc/cli@0.7.7", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-j4yYm9bx3pxWofaJKX1BFwj/3ngUDynN4UIQ2Xd2h0h/7Gt7zkReBTpDN7g5S13mgAYxacaTHTOUsz18097E8w=="], + + "database/@swc/core": ["@swc/core@1.12.4", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.4", "@swc/core-darwin-x64": "1.12.4", "@swc/core-linux-arm-gnueabihf": "1.12.4", "@swc/core-linux-arm64-gnu": "1.12.4", "@swc/core-linux-arm64-musl": "1.12.4", "@swc/core-linux-x64-gnu": "1.12.4", "@swc/core-linux-x64-musl": "1.12.4", "@swc/core-win32-arm64-msvc": "1.12.4", "@swc/core-win32-ia32-msvc": "1.12.4", "@swc/core-win32-x64-msvc": "1.12.4" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-hn30ebV4njAn0NAUM+3a0qCF+MJgqTNSrfA/hUAbC6TVjOQy2OYGQwkUvCu/V7S2+rZxrUsTpKOnZ7qqECZV9Q=="], + + "database/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="], + + "database/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + "decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], "dht-node/@types/jest": ["@types/jest@27.5.1", "", { "dependencies": { "jest-matcher-utils": "^27.0.0", "pretty-format": "^27.0.0" } }, "sha512-fUy7YRpT+rHXto1YlL+J9rs0uLGyiqVt3ZOTQR+4ROc47yNl8WLdVLgUloBRhOxP1PZvguHl44T3H0wAWxahYQ=="], @@ -3607,6 +3678,8 @@ "dht-node/ts-jest": ["ts-jest@27.1.4", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash.memoize": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ=="], + "dht-node/tsx": ["tsx@4.19.4", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q=="], + "dht-rpc/sodium-universal": ["sodium-universal@5.0.1", "", { "dependencies": { "sodium-native": "^5.0.1" }, "peerDependencies": { "sodium-javascript": "~0.8.0" }, "optionalPeers": ["sodium-javascript"] }, "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q=="], "domexception/webidl-conversions": ["webidl-conversions@5.0.0", "", {}, "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA=="], @@ -3719,6 +3792,8 @@ "jest-util/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="], + "jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "jest-watcher/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="], "jest-worker/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="], @@ -3741,6 +3816,8 @@ "mailparser/tlds": ["tlds@1.255.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw=="], + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "multimatch/@types/minimatch": ["@types/minimatch@3.0.5", "", {}, "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="], @@ -3803,6 +3880,10 @@ "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], + "shared/@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=="], + + "shared/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "simple-update-notifier/semver": ["semver@7.0.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A=="], "sodium-secretstream/sodium-universal": ["sodium-universal@5.0.1", "", { "dependencies": { "sodium-native": "^5.0.1" }, "peerDependencies": { "sodium-javascript": "~0.8.0" }, "optionalPeers": ["sodium-javascript"] }, "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q=="], @@ -3855,8 +3936,6 @@ "test-exclude/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - "tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], - "typed-rest-client/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], "typeorm/dotenv": ["dotenv@16.5.0", "", {}, "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg=="], @@ -3879,16 +3958,14 @@ "unimport/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "unimport/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], - "unimport/pkg-types": ["pkg-types@2.1.0", "", { "dependencies": { "confbox": "^0.2.1", "exsolve": "^1.0.1", "pathe": "^2.0.3" } }, "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="], + "unimport/tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="], + "unimport/unplugin": ["unplugin@2.3.2", "", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-3n7YA46rROb3zSj8fFxtxC/PqoyvYQ0llwz9wtUPUutr9ig09C8gGo5CWCwHrUzlqC1LLR43kxp5vEIyH1ac1w=="], "unplugin-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "unplugin-utils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], - "unplugin-vue-components/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "unplugin-vue-components/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -4011,6 +4088,56 @@ "css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], + "database/@swc/cli/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], + + "database/@swc/cli/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "database/@swc/core/@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HihKfeitjZU2ab94Zf893sxzFryLKX0TweGsNXXOLNtkSMLw50auuYfpRM0BOL9/uXXtuCWgRIF6P030SAX5xQ=="], + + "database/@swc/core/@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-meYCXHyYb6RDdu2N5PNAf0EelyxPBFhRcVo4kBFLuvuNb0m6EUg///VWy8MUMXq9/s9uzGS9kJVXXdRdr/d6FA=="], + + "database/@swc/core/@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.4", "", { "os": "linux", "cpu": "arm" }, "sha512-szfDbf7mE8V64of0q/LSqbk+em+T+TD3uqnH40Z7Qu/aL8vi5CHgyLjWG2SLkLLpyjgkAUF6AKrupgnBYcC2NA=="], + + "database/@swc/core/@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-n0IY76w+Scx8m3HIVRvLkoResuwsQgjDfAk9bxn99dq4leQO+mE0fkPl0Yw/1BIsPh+kxGfopIJH9zsZ1Z2YrA=="], + + "database/@swc/core/@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-wE5jmFi5cEQyLy8WmCWmNwfKETrnzy2D8YNi/xpYWpLPWqPhcelpa6tswkfYlbsMmmOh7hQNoTba1QdGu0jvHQ=="], + + "database/@swc/core/@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6S50Xd/7ePjEwrXyHMxpKTZ+KBrgUwMA8hQPbArUOwH4S5vHBr51heL0iXbUkppn1bkSr0J0IbOove5hzn+iqQ=="], + + "database/@swc/core/@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-hbYRyaHhC13vYKuGG5BrAG5fjjWEQFfQetuFp/4QKEoXDzdnabJoixxWTQACDL3m0JW32nJ+gUzsYIPtFYkwXg=="], + + "database/@swc/core/@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-e6EbfjPL8GA/bb1lc9Omtxjlz+1ThTsAuBsy4Q3Kpbuh6B3jclg8KzxU/6t91v23wG593mieTyR5f3Pr7X3AWw=="], + + "database/@swc/core/@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-RG2FzmllBTUf4EksANlIvLckcBrLZEA0t13LIa6L213UZKQfEuDNHezqESgoVhJMg2S/tWauitATOCFgZNSmjg=="], + + "database/@swc/core/@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.4", "", { "os": "win32", "cpu": "x64" }, "sha512-oRHKnZlR83zaMeVUCmHENa4j5uNRAWbmEpjYbzRcfC45LPFNWKGWGAGERLx0u87XMUtTGqnVYxnBTHN/rzDHOw=="], + + "database/@swc/core/@swc/types": ["@swc/types@0.1.23", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw=="], + + "database/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + + "database/vitest/@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], + + "database/vitest/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + + "database/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], + + "database/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], + + "database/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + + "database/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + + "database/vitest/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "database/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "database/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], + + "database/vitest/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "database/vitest/vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + "dht-rpc/sodium-universal/sodium-native": ["sodium-native@5.0.1", "", { "dependencies": { "require-addon": "^1.1.0", "which-runtime": "^1.2.1" } }, "sha512-Q305aUXc0OzK7VVRvWkeEQJQIHs6slhFwWpyqLB5iJqhpyt2lYIVu96Y6PQ7TABIlWXVF3YiWDU3xS2Snkus+g=="], "editorconfig/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], @@ -4075,6 +4202,8 @@ "jest-worker/jest-util/@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-worker/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "js-beautify/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "jsdom/parse5/entities": ["entities@6.0.0", "", {}, "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw=="], @@ -4111,6 +4240,22 @@ "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "shared/@biomejs/biome/@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], + + "shared/@biomejs/biome/@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], + + "shared/@biomejs/biome/@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], + + "shared/@biomejs/biome/@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], + + "shared/@biomejs/biome/@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], + + "shared/@biomejs/biome/@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], + + "shared/@biomejs/biome/@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], + + "shared/@biomejs/biome/@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], + "sodium-secretstream/sodium-universal/sodium-native": ["sodium-native@5.0.1", "", { "dependencies": { "require-addon": "^1.1.0", "which-runtime": "^1.2.1" } }, "sha512-Q305aUXc0OzK7VVRvWkeEQJQIHs6slhFwWpyqLB5iJqhpyt2lYIVu96Y6PQ7TABIlWXVF3YiWDU3xS2Snkus+g=="], "streamroller/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], @@ -4131,8 +4276,6 @@ "typeorm/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - "unctx/unplugin/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], - "unimport/pkg-types/confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], "unplugin-vue-components/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -4141,6 +4284,8 @@ "unplugin-vue-components/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "vite-plugin-html/@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], @@ -4217,6 +4362,8 @@ "cheerio-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], + "chokidar-cli/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "chokidar-cli/yargs/cliui/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="], "chokidar-cli/yargs/cliui/wrap-ansi": ["wrap-ansi@5.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", "strip-ansi": "^5.0.0" } }, "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q=="], @@ -4233,6 +4380,14 @@ "css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], + "database/@swc/cli/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "database/vitest/@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "database/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], + + "database/vitest/@vitest/utils/loupe": ["loupe@3.1.4", "", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="], + "editorconfig/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "filelist/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], @@ -4257,6 +4412,8 @@ "mailparser/html-to-text/selderee/parseley": ["parseley@0.12.1", "", { "dependencies": { "leac": "^0.6.0", "peberminta": "^0.9.0" } }, "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw=="], + "nodemon/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "run-applescript/execa/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], @@ -4273,6 +4430,8 @@ "typeorm/glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "unplugin-vue-components/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "unplugin-vue-components/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "vue-apollo/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], @@ -4295,6 +4454,8 @@ "chokidar-cli/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], + "database/@swc/cli/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "js-beautify/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], diff --git a/config-schema/package.json b/config-schema/package.json index c127bcd91..17afc7977 100644 --- a/config-schema/package.json +++ b/config-schema/package.json @@ -19,6 +19,7 @@ "build:bun": "bun build src/index.ts --outdir=build --target=bun --packages=external", "typecheck": "tsc --noEmit", "test": "bun test", + "test:debug": "bun test --inspect-brk", "lint": "biome check --error-on-warnings .", "lint:fix": "biome check --error-on-warnings . --write" }, @@ -26,7 +27,7 @@ "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", "jest": "27.2.4", - "typescript": "^4.9.5" + "typescript": "^5.8.3" }, "dependencies": { "esbuild": "^0.25.2", diff --git a/config-schema/test/testSetup.bun.ts b/config-schema/test/testSetup.bun.ts new file mode 100644 index 000000000..d2a4002bb --- /dev/null +++ b/config-schema/test/testSetup.bun.ts @@ -0,0 +1,99 @@ +import { mock, jest } from 'bun:test' +import { inspect } from 'node:util' +/* + * This file is used to mock the log4js logger in the tests. + * It is used to collect all log entries in the logs array. + * If you want to debug your test, you can use `printLogs()` to print all log entries collected through the tests. + * To have only the relevant logs, call `clearLogs()` before your calling the methods you like to test and `printLogs()` after. + * + * This is the bun version + */ + +jest.setTimeout(1000000) + +type LogEntry = { + level: string; + message: string; + logger: string; + context: string; + additional: any[]; +} + +const loggers: { [key: string]: any } = {} +const logs: LogEntry[] = [] + +function addLog(level: string, message: string, logger: string, context: Map, additional: any[]) { + logs.push({ + level, + context: [...context.entries()].map(([key, value]) => `${key}=${value}`).join(' ').trimEnd(), + message, + logger, + additional + }) +} + +export function printLogs() { + for (const log of logs) { + const messages = [] + messages.push(log.message) + // console.log('additionals: ', JSON.stringify(log.additional, null, 2)) + messages.push(log.additional.map((d) => inspect(d)).filter((d) => d)) + process.stdout.write(`${log.logger} [${log.level}] ${log.context} ${messages.join(' ')}\n`) + } +} + +export function clearLogs(): void { + logs.length = 0 +} + +const getLoggerMocked = mock().mockImplementation((param: any) => { + if (loggers[param]) { + // TODO: check if it is working when tests run in parallel + loggers[param].clearContext() + return loggers[param] + } + // console.log('getLogger called with: ', param) + const fakeLogger = { + context: new Map(), + addContext: jest.fn((key: string, value: string) => { + fakeLogger.context.set(key, value) + }), + trace: jest.fn((message: string, ...args: any[]) => { + addLog('trace', message, param, fakeLogger.context, args) + }), + debug: jest.fn((message: string, ...args: any[]) => { + addLog('debug', message, param, fakeLogger.context, args) + }), + warn: jest.fn((message: string, ...args: any[]) => { + addLog('warn', message, param, fakeLogger.context, args) + }), + info: jest.fn((message: string, ...args: any[]) => { + addLog('info', message, param, fakeLogger.context, args) + }), + error: jest.fn((message: string, ...args: any[]) => { + addLog('error', message, param, fakeLogger.context, args) + }), + fatal: jest.fn((message: string, ...args: any[]) => { + addLog('fatal', message, param, fakeLogger.context, args) + }), + removeContext: jest.fn((key: string) => { + fakeLogger.context.delete(key) + }), + clearContext: jest.fn(() => { + fakeLogger.context.clear() + }) + } + loggers[param] = fakeLogger + return fakeLogger +}) + +mock.module('log4js', () => ({ + getLogger: getLoggerMocked +})) + +export function getLogger(name: string) { + if (!loggers[name]) { + return getLoggerMocked(name) + } + return loggers[name] +} diff --git a/database/package.json b/database/package.json index 456b84201..e62875f4a 100644 --- a/database/package.json +++ b/database/package.json @@ -20,19 +20,32 @@ "lint": "biome check --error-on-warnings .", "lint:fix": "biome check --error-on-warnings . --write", "clear": "cross-env TZ=UTC tsx migration/index.ts clear", + "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test vitest run", + "test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles", "up": "cross-env TZ=UTC tsx migration/index.ts up", "down": "cross-env TZ=UTC tsx migration/index.ts down", "reset": "cross-env TZ=UTC tsx migration/index.ts reset", + "up:test": "cross-env TZ=UTC DB_DATABASE=gradido_test tsx migration/index.ts up", "up:backend_test": "cross-env TZ=UTC DB_DATABASE=gradido_test_backend tsx migration/index.ts up", "up:federation_test": "cross-env TZ=UTC DB_DATABASE=gradido_test_federation tsx migration/index.ts up", "up:dht_test": "cross-env TZ=UTC DB_DATABASE=gradido_test_dht tsx migration/index.ts up" }, "devDependencies": { "@biomejs/biome": "2.0.0", + "@swc-node/register": "^1.10.10", + "@swc/cli": "^0.7.3", + "@swc/core": "^1.11.24", + "@swc/helpers": "^0.5.17", "@types/faker": "^5.5.9", "@types/geojson": "^7946.0.13", - "@types/node": "^17.0.21", - "typescript": "^4.9.5" + "@types/jest": "27.0.2", + "@types/node": "^18.7.14", + "jest": "27.2.4", + "ts-jest": "27.0.5", + "ts-node": "^10.9.2", + "typescript": "^4.9.5", + "uuid": "^8.3.2", + "vitest": "^3.2.4" }, "dependencies": { "@types/uuid": "^8.3.4", @@ -47,7 +60,7 @@ "reflect-metadata": "^0.1.13", "source-map-support": "^0.5.21", "ts-mysql-migrate": "^1.0.2", - "tsx": "^4.19.4", + "tsx": "^4.20.3", "typeorm": "^0.3.22", "uuid": "^8.3.2", "wkx": "^0.5.0" diff --git a/database/src/AppDatabase.ts b/database/src/AppDatabase.ts index 2a6181a26..f10c095bb 100644 --- a/database/src/AppDatabase.ts +++ b/database/src/AppDatabase.ts @@ -4,7 +4,7 @@ import { Migration, entities } from './entity' import { getLogger } from 'log4js' import { latestDbVersion } from '.' import { CONFIG } from './config' -import { LOG4JS_BASE_CATEGORY_NAME } from './config/const' +import { LOG4JS_BASE_CATEGORY_NAME, MIGRATIONS_TABLE } from './config/const' const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.AppDatabase`) @@ -93,6 +93,7 @@ export class AppDatabase { public async destroy(): Promise { await this.dataSource?.destroy() } + // ###################################### // private methods // ###################################### diff --git a/database/src/config/index.ts b/database/src/config/index.ts index 3eb93ad66..fc6a4bb04 100644 --- a/database/src/config/index.ts +++ b/database/src/config/index.ts @@ -2,6 +2,10 @@ import dotenv from 'dotenv' dotenv.config() +const defaults = { + DEFAULT_LANGUAGE: process.env.DEFAULT_LANGUAGE ?? 'en', +} + const database = { DB_CONNECT_RETRY_COUNT: process.env.DB_CONNECT_RETRY_COUNT ? Number.parseInt(process.env.DB_CONNECT_RETRY_COUNT) @@ -21,4 +25,4 @@ const database = { const PRODUCTION = process.env.NODE_ENV === 'production' || false const nodeEnv = process.env.NODE_ENV || 'development' -export const CONFIG = { ...database, NODE_ENV: nodeEnv, PRODUCTION } +export const CONFIG = { ...database, NODE_ENV: nodeEnv, PRODUCTION, ...defaults } diff --git a/database/src/index.ts b/database/src/index.ts index 1dd0a84d6..3ff4efa17 100644 --- a/database/src/index.ts +++ b/database/src/index.ts @@ -59,4 +59,5 @@ export const entities = [ export { latestDbVersion } export * from './logging' +export * from './queries' export { AppDatabase } from './AppDatabase' diff --git a/database/src/queries/communities.test.ts b/database/src/queries/communities.test.ts new file mode 100644 index 000000000..ca8ab0b12 --- /dev/null +++ b/database/src/queries/communities.test.ts @@ -0,0 +1,40 @@ +import { Community } from '..' +import { AppDatabase } from '../AppDatabase' +import { getHomeCommunity } from './communities' +import { describe, expect, it, beforeAll, afterAll } from 'vitest' +import { createCommunity } from '../seeds/homeCommunity' + +const db = AppDatabase.getInstance() + +beforeAll(async () => { + await db.init() +}) +afterAll(async () => { + await db.destroy() +}) + +describe('community.queries', () => { + beforeAll(async () => { + await Community.clear() + }) + describe('getHomeCommunity', () => { + it('should return null if no home community exists', async () => { + await createCommunity(true) + expect(await getHomeCommunity()).toBeNull() + }) + it('should return the home community', async () => { + const homeCom = await createCommunity(false) + const community = await getHomeCommunity() + expect(community).toBeDefined() + expect(community?.name).toBe(homeCom.name) + expect(community?.description).toBe(homeCom.description) + expect(community?.url).toBe(homeCom.url) + expect(community?.creationDate).toStrictEqual(homeCom.creationDate) + expect(community?.communityUuid).toBe(homeCom.communityUuid) + expect(community?.authenticatedAt).toStrictEqual(homeCom.authenticatedAt) + expect(community?.foreign).toBe(homeCom.foreign) + expect(community?.publicKey).toStrictEqual(homeCom.publicKey) + expect(community?.privateKey).toStrictEqual(homeCom.privateKey) + }) + }) +}) \ No newline at end of file diff --git a/database/src/queries/communities.ts b/database/src/queries/communities.ts new file mode 100644 index 000000000..868db42e8 --- /dev/null +++ b/database/src/queries/communities.ts @@ -0,0 +1,11 @@ +import { Community as DbCommunity } from '../entity/Community' + +/** + * Retrieves the home community, i.e., a community that is not foreign. + * @returns A promise that resolves to the home community, or null if no home community was found + */ +export async function getHomeCommunity(): Promise { + return await DbCommunity.findOne({ + where: { foreign: false }, + }) +} \ No newline at end of file diff --git a/database/src/queries/index.ts b/database/src/queries/index.ts new file mode 100644 index 000000000..bb6255b2e --- /dev/null +++ b/database/src/queries/index.ts @@ -0,0 +1,2 @@ +export * from './user' +export * from './communities' diff --git a/database/src/queries/user.test.ts b/database/src/queries/user.test.ts new file mode 100644 index 000000000..4ab567a24 --- /dev/null +++ b/database/src/queries/user.test.ts @@ -0,0 +1,42 @@ +import { User, UserContact } from '..' +import { AppDatabase } from '../AppDatabase' +import { aliasExists } from './user' +import { userFactory } from '../seeds/factory/user' +import { bibiBloxberg } from '../seeds/users/bibi-bloxberg' +import { describe, expect, it, beforeAll, afterAll } from 'vitest' + +const db = AppDatabase.getInstance() + +beforeAll(async () => { + await db.init() +}) +afterAll(async () => { + await db.destroy() +}) + +describe('integration test mysql queries', () => { + describe('user.queries', () => { + describe('aliasExists', () => { + beforeAll(async () => { + await User.clear() + await UserContact.clear() + + const bibi = bibiBloxberg + bibi.alias = 'b-b' + await userFactory(bibi) + }) + + it('should return true if alias exists', async () => { + expect(await aliasExists('b-b')).toBe(true) + }) + + it('should return true if alias exists even with deviating casing', async () => { + expect(await aliasExists('b-B')).toBe(true) + }) + + it('should return false if alias does not exist', async () => { + expect(await aliasExists('bibi')).toBe(false) + }) + }) + }) +}) \ No newline at end of file diff --git a/database/src/queries/user.ts b/database/src/queries/user.ts new file mode 100644 index 000000000..49394ad8f --- /dev/null +++ b/database/src/queries/user.ts @@ -0,0 +1,9 @@ +import { Raw } from 'typeorm' +import { User as DbUser } from '../entity' + +export async function aliasExists(alias: string): Promise { + const user = await DbUser.findOne({ + where: { alias: Raw((a) => `LOWER(${a}) = LOWER(:alias)`, { alias }) }, + }) + return user !== null +} \ No newline at end of file diff --git a/database/src/seeds/factory/user.ts b/database/src/seeds/factory/user.ts new file mode 100644 index 000000000..fbca53490 --- /dev/null +++ b/database/src/seeds/factory/user.ts @@ -0,0 +1,37 @@ +import { UserInterface } from '../users/UserInterface' +import { User, UserContact } from '../../entity' +import { generateRandomNumber, generateRandomNumericString } from '../utils' +import { v4 } from 'uuid' + +export const userFactory = async (user: UserInterface): Promise => { + let dbUserContact = new UserContact() + + dbUserContact.email = user.email ?? '' + dbUserContact.type = 'email' //UserContactType.USER_CONTACT_EMAIL + + let dbUser = new User() + dbUser.firstName = user.firstName ?? '' + dbUser.lastName = user.lastName ?? '' + dbUser.alias = user.alias ?? '' + dbUser.language = user.language ?? 'en' + dbUser.createdAt = user.createdAt ?? new Date() + dbUser.deletedAt = user.deletedAt ?? null + dbUser.publisherId = user.publisherId ?? 0 + dbUser.gradidoID = v4() + + if (user.emailChecked) { + dbUserContact.emailVerificationCode = generateRandomNumericString(64) + dbUserContact.emailOptInTypeId = 1 //OptInType.EMAIL_OPT_IN_REGISTER + dbUserContact.emailChecked = true + dbUser.password = generateRandomNumber() + // TODO: think where to put enums + dbUser.passwordEncryptionType = 2 //PasswordEncryptionType.GRADIDO_ID + } + // TODO: improve with cascade + dbUser = await dbUser.save() + dbUserContact.userId = dbUser.id + dbUserContact = await dbUserContact.save() + dbUser.emailId = dbUserContact.id + dbUser.emailContact = dbUserContact + return dbUser.save() +} \ No newline at end of file diff --git a/database/src/seeds/homeCommunity.ts b/database/src/seeds/homeCommunity.ts new file mode 100644 index 000000000..d76ee0b98 --- /dev/null +++ b/database/src/seeds/homeCommunity.ts @@ -0,0 +1,26 @@ +import { Community } from '../entity' +import { randomBytes } from 'node:crypto' +import { v4 as uuidv4 } from 'uuid' + +export async function createCommunity(foreign: boolean): Promise { + const homeCom = new Community() + homeCom.publicKey = randomBytes(32) + homeCom.communityUuid = uuidv4() + homeCom.authenticatedAt = new Date() + homeCom.name = 'HomeCommunity-name' + homeCom.creationDate = new Date() + + if(foreign) { + homeCom.foreign = true + homeCom.name = 'ForeignCommunity-name' + homeCom.description = 'ForeignCommunity-description' + homeCom.url = 'http://foreign/api' + } else { + homeCom.foreign = false + homeCom.privateKey = randomBytes(64) + homeCom.name = 'HomeCommunity-name' + homeCom.description = 'HomeCommunity-description' + homeCom.url = 'http://localhost/api' + } + return await homeCom.save() +} diff --git a/database/src/seeds/users/UserInterface.ts b/database/src/seeds/users/UserInterface.ts new file mode 100644 index 000000000..1fb1b128a --- /dev/null +++ b/database/src/seeds/users/UserInterface.ts @@ -0,0 +1,13 @@ +export interface UserInterface { + alias?: string + email?: string + firstName?: string + lastName?: string + // description?: string + createdAt?: Date + emailChecked?: boolean + language?: string + deletedAt?: Date + publisherId?: number + role?: string +} diff --git a/database/src/seeds/users/bibi-bloxberg.ts b/database/src/seeds/users/bibi-bloxberg.ts new file mode 100644 index 000000000..9a40e922b --- /dev/null +++ b/database/src/seeds/users/bibi-bloxberg.ts @@ -0,0 +1,12 @@ +import { UserInterface } from './UserInterface' + +export const bibiBloxberg: UserInterface = { + email: 'bibi@bloxberg.de', + firstName: 'Bibi', + lastName: 'Bloxberg', + alias: 'BBB', + // description: 'Hex Hex', + emailChecked: true, + language: 'de', + publisherId: 1234, +} diff --git a/database/src/seeds/users/bob-baumeister.ts b/database/src/seeds/users/bob-baumeister.ts new file mode 100644 index 000000000..b7902f7bc --- /dev/null +++ b/database/src/seeds/users/bob-baumeister.ts @@ -0,0 +1,11 @@ +import { UserInterface } from './UserInterface' + +export const bobBaumeister: UserInterface = { + alias: 'MeisterBob', + email: 'bob@baumeister.de', + firstName: 'Bob', + lastName: 'der Baumeister', + // description: 'Können wir das schaffen? Ja, wir schaffen das!', + emailChecked: true, + language: 'de', +} diff --git a/database/src/seeds/users/garrick-ollivander.ts b/database/src/seeds/users/garrick-ollivander.ts new file mode 100644 index 000000000..264a866bd --- /dev/null +++ b/database/src/seeds/users/garrick-ollivander.ts @@ -0,0 +1,12 @@ +import { UserInterface } from './UserInterface' + +export const garrickOllivander: UserInterface = { + email: 'garrick@ollivander.com', + firstName: 'Garrick', + lastName: 'Ollivander', + // description: `Curious ... curious ... + // Renowned wandmaker Mr Ollivander owns the wand shop Ollivanders: Makers of Fine Wands Since 382 BC in Diagon Alley. His shop is widely considered the best place to purchase a wand.`, + createdAt: new Date('2022-01-10T10:23:17'), + emailChecked: false, + language: 'en', +} diff --git a/database/src/seeds/users/index.ts b/database/src/seeds/users/index.ts new file mode 100644 index 000000000..beb6c6f25 --- /dev/null +++ b/database/src/seeds/users/index.ts @@ -0,0 +1,15 @@ +import { bibiBloxberg } from './bibi-bloxberg' +import { bobBaumeister } from './bob-baumeister' +import { garrickOllivander } from './garrick-ollivander' +import { peterLustig } from './peter-lustig' +import { raeuberHotzenplotz } from './raeuber-hotzenplotz' +import { stephenHawking } from './stephen-hawking' + +export const users = [ + peterLustig, + bibiBloxberg, + bobBaumeister, + raeuberHotzenplotz, + stephenHawking, + garrickOllivander, +] diff --git a/database/src/seeds/users/peter-lustig.ts b/database/src/seeds/users/peter-lustig.ts new file mode 100644 index 000000000..58b07fe99 --- /dev/null +++ b/database/src/seeds/users/peter-lustig.ts @@ -0,0 +1,11 @@ +import { UserInterface } from './UserInterface' + +export const peterLustig: UserInterface = { + email: 'peter@lustig.de', + firstName: 'Peter', + lastName: 'Lustig', + // description: 'Latzhose und Nickelbrille', + createdAt: new Date('2020-11-25T10:48:43'), + emailChecked: true, + language: 'de' +} diff --git a/database/src/seeds/users/raeuber-hotzenplotz.ts b/database/src/seeds/users/raeuber-hotzenplotz.ts new file mode 100644 index 000000000..62601efff --- /dev/null +++ b/database/src/seeds/users/raeuber-hotzenplotz.ts @@ -0,0 +1,10 @@ +import { UserInterface } from './UserInterface' + +export const raeuberHotzenplotz: UserInterface = { + email: 'raeuber@hotzenplotz.de', + firstName: 'Räuber', + lastName: 'Hotzenplotz', + // description: 'Pfefferpistole', + emailChecked: true, + language: 'de', +} diff --git a/database/src/seeds/users/stephen-hawking.ts b/database/src/seeds/users/stephen-hawking.ts new file mode 100644 index 000000000..a683b7579 --- /dev/null +++ b/database/src/seeds/users/stephen-hawking.ts @@ -0,0 +1,12 @@ +import { UserInterface } from './UserInterface' + +export const stephenHawking: UserInterface = { + email: 'stephen@hawking.uk', + firstName: 'Stephen', + lastName: 'Hawking', + // description: 'A Brief History of Time', + emailChecked: true, + createdAt: new Date('1942-01-08T09:17:52'), + deletedAt: new Date('2018-03-14T09:17:52'), + language: 'en', +} diff --git a/database/src/seeds/utils.ts b/database/src/seeds/utils.ts new file mode 100644 index 000000000..0aa6c2d0f --- /dev/null +++ b/database/src/seeds/utils.ts @@ -0,0 +1,10 @@ +import { randomBytes } from 'node:crypto' + +export function generateRandomNumber(): BigInt { + return BigInt(randomBytes(8).readBigUInt64LE()) +} +export function generateRandomNumericString(length: number = 64): string { + const digits = '0123456789' + const bytes = randomBytes(length / 8) + return Array.from(bytes, (b) => digits[b % 10]).join('').slice(0, length) +} \ No newline at end of file diff --git a/database/tsconfig.json b/database/tsconfig.json index fc32bbbab..ca22317ab 100644 --- a/database/tsconfig.json +++ b/database/tsconfig.json @@ -50,7 +50,7 @@ //"@/*": ["src/*"], /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ //}, // "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ + "typeRoots": ["node_modules/@types"], /* List of folders to include type definitions from. */ // "types": ["node"], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ @@ -71,5 +71,8 @@ "skipLibCheck": true, /* Skip type checking of declaration files. */ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, - "references": [] /* Any project that is referenced must itself have a `references` array (which may be empty). */ + "references": [], /* Any project that is referenced must itself have a `references` array (which may be empty). */ + "ts-node": { + "swc": true + } } diff --git a/database/turbo.json b/database/turbo.json index 8339cc11f..f0d5ac9a5 100644 --- a/database/turbo.json +++ b/database/turbo.json @@ -13,10 +13,16 @@ "up:dht_test": { "cache": false }, + "up:test": { + "cache": false + }, "up": { "cache": false, "dependsOn": ["build"] }, + "test": { + "dependsOn": ["up:test"] + }, "down": { "cache": false, "dependsOn": ["build"] diff --git a/database/yarn.lock b/database/yarn.lock deleted file mode 100644 index 03a87b110..000000000 --- a/database/yarn.lock +++ /dev/null @@ -1,1060 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/runtime@^7.21.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" - integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== - dependencies: - regenerator-runtime "^0.13.11" - -"@biomejs/biome@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" - integrity sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog== - 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" - -"@biomejs/cli-darwin-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f" - integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== - -"@biomejs/cli-darwin-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" - integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== - -"@biomejs/cli-linux-arm64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" - integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== - -"@biomejs/cli-linux-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" - integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== - -"@biomejs/cli-linux-x64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb" - integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== - -"@biomejs/cli-linux-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8" - integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== - -"@biomejs/cli-win32-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" - integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== - -"@biomejs/cli-win32-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" - integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== - -"@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== - dependencies: - "@jridgewell/trace-mapping" "0.3.9" - -"@esbuild/aix-ppc64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz#014180d9a149cffd95aaeead37179433f5ea5437" - integrity sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ== - -"@esbuild/android-arm64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz#649e47e04ddb24a27dc05c395724bc5f4c55cbfe" - integrity sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ== - -"@esbuild/android-arm@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.3.tgz#8a0f719c8dc28a4a6567ef7328c36ea85f568ff4" - integrity sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A== - -"@esbuild/android-x64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.3.tgz#e2ab182d1fd06da9bef0784a13c28a7602d78009" - integrity sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ== - -"@esbuild/darwin-arm64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz#c7f3166fcece4d158a73dcfe71b2672ca0b1668b" - integrity sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w== - -"@esbuild/darwin-x64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz#d8c5342ec1a4bf4b1915643dfe031ba4b173a87a" - integrity sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A== - -"@esbuild/freebsd-arm64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz#9f7d789e2eb7747d4868817417cc968ffa84f35b" - integrity sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw== - -"@esbuild/freebsd-x64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz#8ad35c51d084184a8e9e76bb4356e95350a64709" - integrity sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q== - -"@esbuild/linux-arm64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz#3af0da3d9186092a9edd4e28fa342f57d9e3cd30" - integrity sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A== - -"@esbuild/linux-arm@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz#e91cafa95e4474b3ae3d54da12e006b782e57225" - integrity sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ== - -"@esbuild/linux-ia32@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz#81025732d85b68ee510161b94acdf7e3007ea177" - integrity sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw== - -"@esbuild/linux-loong64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz#3c744e4c8d5e1148cbe60a71a11b58ed8ee5deb8" - integrity sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g== - -"@esbuild/linux-mips64el@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz#1dfe2a5d63702db9034cc6b10b3087cc0424ec26" - integrity sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag== - -"@esbuild/linux-ppc64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz#2e85d9764c04a1ebb346dc0813ea05952c9a5c56" - integrity sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg== - -"@esbuild/linux-riscv64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz#a9ea3334556b09f85ccbfead58c803d305092415" - integrity sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA== - -"@esbuild/linux-s390x@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz#f6a7cb67969222b200974de58f105dfe8e99448d" - integrity sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ== - -"@esbuild/linux-x64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz#a237d3578ecdd184a3066b1f425e314ade0f8033" - integrity sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA== - -"@esbuild/netbsd-arm64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz#4c15c68d8149614ddb6a56f9c85ae62ccca08259" - integrity sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA== - -"@esbuild/netbsd-x64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz#12f6856f8c54c2d7d0a8a64a9711c01a743878d5" - integrity sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g== - -"@esbuild/openbsd-arm64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz#ca078dad4a34df192c60233b058db2ca3d94bc5c" - integrity sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ== - -"@esbuild/openbsd-x64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz#c9178adb60e140e03a881d0791248489c79f95b2" - integrity sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w== - -"@esbuild/sunos-x64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz#03765eb6d4214ff27e5230af779e80790d1ee09f" - integrity sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA== - -"@esbuild/win32-arm64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz#f1c867bd1730a9b8dfc461785ec6462e349411ea" - integrity sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ== - -"@esbuild/win32-ia32@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz#77491f59ef6c9ddf41df70670d5678beb3acc322" - integrity sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew== - -"@esbuild/win32-x64@0.25.3": - version "0.25.3" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz#b17a2171f9074df9e91bfb07ef99a892ac06412a" - integrity sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg== - -"@jridgewell/resolve-uri@^3.0.3": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== - -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@sqltools/formatter@^1.2.5": - version "1.2.5" - resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.5.tgz#3abc203c79b8c3e90fd6c156a0c62d5403520e12" - integrity sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw== - -"@tsconfig/node10@^1.0.7": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" - integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== - -"@tsconfig/node12@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" - integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== - -"@tsconfig/node14@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" - integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== - -"@tsconfig/node16@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" - integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== - -"@types/faker@^5.5.9": - version "5.5.9" - resolved "https://registry.yarnpkg.com/@types/faker/-/faker-5.5.9.tgz#588ede92186dc557bff8341d294335d50d255f0c" - integrity sha512-uCx6mP3UY5SIO14XlspxsGjgaemrxpssJI0Ol+GfhxtcKpv9pgRZYsS4eeKeHVLje6Qtc8lGszuBI461+gVZBA== - -"@types/geojson@^7946.0.13": - version "7946.0.13" - resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.13.tgz#e6e77ea9ecf36564980a861e24e62a095988775e" - integrity sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ== - -"@types/mysql@^2.15.8": - version "2.15.19" - resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.19.tgz#d158927bb7c1a78f77e56de861a3b15cae0e7aed" - integrity sha512-wSRg2QZv14CWcZXkgdvHbbV2ACufNy5EgI8mBBxnJIptchv7DBy/h53VMa2jDhyo0C9MO4iowE6z9vF8Ja1DkQ== - dependencies: - "@types/node" "*" - -"@types/node@*": - version "16.7.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.1.tgz#c6b9198178da504dfca1fd0be9b2e1002f1586f0" - integrity sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A== - -"@types/node@^17.0.21": - version "17.0.45" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" - integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== - -"@types/uuid@^8.3.4": - version "8.3.4" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" - integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== - -acorn-walk@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.1.1.tgz#3ddab7f84e4a7e2313f6c414c5b7dac85f4e3ebc" - integrity sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w== - -acorn@^8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" - integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -any-promise@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= - -app-root-path@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.1.0.tgz#5971a2fc12ba170369a7a1ef018c71e6e47c2e86" - integrity sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA== - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -bignumber.js@9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075" - integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A== - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -chalk@^4.0.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -cli-highlight@^2.1.11: - version "2.1.11" - resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf" - integrity sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg== - dependencies: - chalk "^4.0.0" - highlight.js "^10.7.1" - mz "^2.4.0" - parse5 "^5.1.1" - parse5-htmlparser2-tree-adapter "^6.0.0" - yargs "^16.0.0" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - -cross-env@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" - integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== - dependencies: - cross-spawn "^7.0.1" - -cross-spawn@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -crypto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" - integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== - -date-fns@^2.29.3: - version "2.30.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" - integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== - dependencies: - "@babel/runtime" "^7.21.0" - -debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -decimal.js-light@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" - integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== - -denque@^1.4.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" - integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -dotenv@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - -dotenv@^16.0.3: - version "16.3.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" - integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -esbuild@~0.25.0: - version "0.25.3" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.3.tgz#371f7cb41283e5b2191a96047a7a89562965a285" - integrity sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q== - optionalDependencies: - "@esbuild/aix-ppc64" "0.25.3" - "@esbuild/android-arm" "0.25.3" - "@esbuild/android-arm64" "0.25.3" - "@esbuild/android-x64" "0.25.3" - "@esbuild/darwin-arm64" "0.25.3" - "@esbuild/darwin-x64" "0.25.3" - "@esbuild/freebsd-arm64" "0.25.3" - "@esbuild/freebsd-x64" "0.25.3" - "@esbuild/linux-arm" "0.25.3" - "@esbuild/linux-arm64" "0.25.3" - "@esbuild/linux-ia32" "0.25.3" - "@esbuild/linux-loong64" "0.25.3" - "@esbuild/linux-mips64el" "0.25.3" - "@esbuild/linux-ppc64" "0.25.3" - "@esbuild/linux-riscv64" "0.25.3" - "@esbuild/linux-s390x" "0.25.3" - "@esbuild/linux-x64" "0.25.3" - "@esbuild/netbsd-arm64" "0.25.3" - "@esbuild/netbsd-x64" "0.25.3" - "@esbuild/openbsd-arm64" "0.25.3" - "@esbuild/openbsd-x64" "0.25.3" - "@esbuild/sunos-x64" "0.25.3" - "@esbuild/win32-arm64" "0.25.3" - "@esbuild/win32-ia32" "0.25.3" - "@esbuild/win32-x64" "0.25.3" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -generate-function@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" - integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== - dependencies: - is-property "^1.0.2" - -geojson@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/geojson/-/geojson-0.5.0.tgz#3cd6c96399be65b56ee55596116fe9191ce701c0" - integrity sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-tsconfig@^4.7.5: - version "4.10.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.10.0.tgz#403a682b373a823612475a4c2928c7326fc0f6bb" - integrity sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A== - dependencies: - resolve-pkg-maps "^1.0.0" - -glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -highlight.js@^10.7.1: - version "10.7.3" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" - integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== - -iconv-lite@^0.6.2: - version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-property@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -long@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - -lru-cache@^4.1.3: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -mkdirp@^2.1.3: - version "2.1.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" - integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== - -mkdirp@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" - integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -mysql2@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-2.3.0.tgz#600f5cc27e397dfb77b59eac93666434f88e8079" - integrity sha512-0t5Ivps5Tdy5YHk5NdKwQhe/4Qyn2pload+S+UooDBvsqngtzujG1BaTWBihQLfeKO3t3122/GtusBtmHEHqww== - dependencies: - denque "^1.4.1" - generate-function "^2.3.1" - iconv-lite "^0.6.2" - long "^4.0.0" - lru-cache "^6.0.0" - named-placeholders "^1.1.2" - seq-queue "^0.0.5" - sqlstring "^2.3.2" - -mysql@^2.18.1: - version "2.18.1" - resolved "https://registry.yarnpkg.com/mysql/-/mysql-2.18.1.tgz#2254143855c5a8c73825e4522baf2ea021766717" - integrity sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig== - dependencies: - bignumber.js "9.0.0" - readable-stream "2.3.7" - safe-buffer "5.1.2" - sqlstring "2.3.1" - -mz@^2.4.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - -named-placeholders@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.2.tgz#ceb1fbff50b6b33492b5cf214ccf5e39cef3d0e8" - integrity sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA== - dependencies: - lru-cache "^4.1.3" - -ncp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" - integrity sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA== - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -parse5-htmlparser2-tree-adapter@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" - integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== - dependencies: - parse5 "^6.0.1" - -parse5@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - -parse5@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -prettier@^2.8.7: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -readable-stream@2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -reflect-metadata@^0.1.13: - version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== - -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -resolve-pkg-maps@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" - integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@^5.0.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -"safer-buffer@>= 2.1.2 < 3.0.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -seq-queue@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" - integrity sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4= - -sha.js@^2.4.11: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -sqlstring@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.1.tgz#475393ff9e91479aea62dcaf0ca3d14983a7fb40" - integrity sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A= - -sqlstring@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.2.tgz#cdae7169389a1375b18e885f2e60b3e460809514" - integrity sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg== - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= - dependencies: - thenify ">= 3.1.0 < 4" - -"thenify@>= 3.1.0 < 4": - version "3.3.1" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" - integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== - dependencies: - any-promise "^1.0.0" - -ts-mysql-migrate@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ts-mysql-migrate/-/ts-mysql-migrate-1.0.2.tgz#736d37c3aa3fef92f226b869098e939950d0e18c" - integrity sha512-zDW6iQsfPCJfQ3JMhfUGjhy8aK+VNTvPrXmJH66PB2EGEvyn4m7x2nBdhDNhKuwYU9LMxW1p+l39Ei+btXNpxA== - dependencies: - "@types/mysql" "^2.15.8" - mysql "^2.18.1" - -ts-node@^10.9.2: - version "10.9.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" - integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== - dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" - -tslib@^2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== - -tsx@^4.19.3: - version "4.19.3" - resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.19.3.tgz#2bdbcb87089374d933596f8645615142ed727666" - integrity sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ== - dependencies: - esbuild "~0.25.0" - get-tsconfig "^4.7.5" - optionalDependencies: - fsevents "~2.3.3" - -typeorm@^0.3.16: - version "0.3.17" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.17.tgz#a73c121a52e4fbe419b596b244777be4e4b57949" - integrity sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig== - dependencies: - "@sqltools/formatter" "^1.2.5" - app-root-path "^3.1.0" - buffer "^6.0.3" - chalk "^4.1.2" - cli-highlight "^2.1.11" - date-fns "^2.29.3" - debug "^4.3.4" - dotenv "^16.0.3" - glob "^8.1.0" - mkdirp "^2.1.3" - reflect-metadata "^0.1.13" - sha.js "^2.4.11" - tslib "^2.5.0" - uuid "^9.0.0" - yargs "^17.6.2" - -typescript@^4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -uuid@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" - integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== - -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wkx@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c" - integrity sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg== - dependencies: - "@types/node" "*" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs@^16.0.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^17.6.2: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/dht-node/src/dht_node/index.ts b/dht-node/src/dht_node/index.ts index dc45b15ed..4884e2a7f 100644 --- a/dht-node/src/dht_node/index.ts +++ b/dht-node/src/dht_node/index.ts @@ -3,6 +3,7 @@ import { CommunityLoggingView, Community as DbCommunity, FederatedCommunity as DbFederatedCommunity, + getHomeCommunity, } from 'database' import { v4 as uuidv4 } from 'uuid' @@ -234,7 +235,7 @@ async function writeFederatedHomeCommunityEntries(pubKey: string): Promise { try { // check for existing homeCommunity entry - let homeCom = await DbCommunity.findOne({ where: { foreign: false } }) + let homeCom = await getHomeCommunity() if (homeCom) { // simply update the existing entry, but it MUST keep the ID and UUID because of possible relations homeCom.publicKey = keyPair.publicKey diff --git a/dht-node/tsconfig.json b/dht-node/tsconfig.json index 9628b7c41..d87ceed66 100644 --- a/dht-node/tsconfig.json +++ b/dht-node/tsconfig.json @@ -54,7 +54,7 @@ "typeRoots": [ /* List of folders to include type definitions from. */ "src/dht_node/@types", "node_modules/@types", - "../node_modules/@types" + // "../node_modules/@types" ], // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ diff --git a/shared/bunfig.toml b/shared/bunfig.toml new file mode 100644 index 000000000..6e74bf7ca --- /dev/null +++ b/shared/bunfig.toml @@ -0,0 +1,2 @@ +[test] +preload = ["../config-schema/test/testSetup.bun.ts"] diff --git a/shared/package.json b/shared/package.json index f0434567a..e16907502 100644 --- a/shared/package.json +++ b/shared/package.json @@ -17,6 +17,8 @@ "scripts": { "build": "esbuild src/index.ts --outdir=build --platform=node --target=node18.20.7 --bundle --packages=external", "build:bun": "bun build src/index.ts --outdir=build --target=bun --packages=external", + "test": "bun test", + "test:debug": "bun test --inspect-brk", "typecheck": "tsc --noEmit", "lint": "biome check --error-on-warnings .", "lint:fix": "biome check --error-on-warnings . --write" @@ -24,12 +26,13 @@ "devDependencies": { "@biomejs/biome": "1.9.4", "@types/node": "^17.0.21", - "typescript": "^4.9.5" + "typescript": "^5.8.3" }, "dependencies": { "database": "*", "esbuild": "^0.25.2", - "zod": "^3.25.20" + "log4js": "^6.9.1", + "zod": "^3.25.61" }, "engines": { "node": ">=18" diff --git a/shared/src/config/const.ts b/shared/src/config/const.ts new file mode 100644 index 000000000..9e9fdf08d --- /dev/null +++ b/shared/src/config/const.ts @@ -0,0 +1 @@ +export const LOG4JS_BASE_CATEGORY_NAME = 'shared' \ No newline at end of file diff --git a/shared/src/index.test.ts b/shared/src/index.test.ts deleted file mode 100644 index b942d6baf..000000000 --- a/shared/src/index.test.ts +++ /dev/null @@ -1,5 +0,0 @@ -describe('shared', () => { - it('should be true', () => { - expect(true).toBe(true) - }) -}) \ No newline at end of file diff --git a/shared/src/index.ts b/shared/src/index.ts index e69de29bb..30612341b 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -0,0 +1,3 @@ +import * as schema from './schema' + +export { schema } \ No newline at end of file diff --git a/shared/src/schema/index.ts b/shared/src/schema/index.ts new file mode 100644 index 000000000..6c1cda831 --- /dev/null +++ b/shared/src/schema/index.ts @@ -0,0 +1,4 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' + +export const LOG_CATEGORY_SCHEMA_ALIAS = `${LOG4JS_BASE_CATEGORY_NAME}.schema` +export * from './user.schema' \ No newline at end of file diff --git a/shared/src/schema/user.schema.test.ts b/shared/src/schema/user.schema.test.ts new file mode 100644 index 000000000..1b4ae1d9d --- /dev/null +++ b/shared/src/schema/user.schema.test.ts @@ -0,0 +1,181 @@ +import { validateAlias } from './user.schema' +import { getLogger } from '../../../config-schema/test/testSetup.bun' +import { LOG_CATEGORY_SCHEMA_ALIAS } from '.' +import { describe, it, expect, beforeEach, mock, jest } from 'bun:test' +import { aliasExists } from 'database' + +const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`) + +mock.module('database', () => ({ + aliasExists: jest.fn(), +})) + +describe('validate alias', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + describe('alias too short', () => { + it('throws and logs an error', () => { + expect(validateAlias('Bi')).rejects.toThrowError(new Error('Given alias is too short')) + expect(logger.warn.mock.calls[0]).toEqual([ + 'invalid alias', + 'Bi', + expect.arrayContaining([ + expect.objectContaining({ + code: 'too_small', + minimum: 3, + type: 'string', + inclusive: true, + exact: false, + message: 'Given alias is too short', + path: [], + }), + ]), + ]) + }) + }) + + describe('alias too long', () => { + it('throws and logs an error', () => { + expect(validateAlias('BibiBloxbergHexHexHex')).rejects.toThrowError( + new Error('Given alias is too long'), + ) + expect(logger.warn.mock.calls[0]).toEqual([ + 'invalid alias', + 'BibiBloxbergHexHexHex', + expect.arrayContaining([ + expect.objectContaining({ + code: 'too_big', + maximum: 20, + type: 'string', + inclusive: true, + exact: false, + message: 'Given alias is too long', + path: [], + }), + ]), + ]) + }) + }) + + describe('alias contains invalid characters', () => { + it('throws and logs an error', () => { + expect(validateAlias('Bibi.Bloxberg')).rejects.toEqual( + new Error('Invalid characters in alias'), + ) + expect(logger.warn.mock.calls[0]).toEqual([ + 'invalid alias', + 'Bibi.Bloxberg', + expect.arrayContaining([ + expect.objectContaining({ + validation: 'regex', + code: 'invalid_string', + message: 'Invalid characters in alias', + path: [], + }), + ]), + ]) + }) + }) + + describe('alias is a reserved word', () => { + it('throws and logs an error', () => { + expect(validateAlias('admin')).rejects.toEqual(new Error('Given alias is not allowed')) + expect(logger.warn.mock.calls[0]).toEqual([ + 'invalid alias', + 'admin', + expect.arrayContaining([ + expect.objectContaining({ + code: 'custom', + message: 'Given alias is not allowed', + path: [], + }), + ]), + ]) + }) + }) + + describe('alias is a reserved word with uppercase characters', () => { + it('throws and logs an error', () => { + expect(validateAlias('Admin')).rejects.toEqual(new Error('Given alias is not allowed')) + expect(logger.warn.mock.calls[0]).toEqual([ + 'invalid alias', + 'Admin', + expect.arrayContaining([ + expect.objectContaining({ + code: 'custom', + message: 'Given alias is not allowed', + path: [], + }), + ]), + ]) + }) + }) + + describe('hyphens and underscore', () => { + describe('alias starts with underscore', () => { + it('throws and logs an error', () => { + expect(validateAlias('_bibi')).rejects.toEqual( + new Error('Invalid characters in alias'), + ) + expect(logger.warn.mock.calls[0]).toEqual([ + 'invalid alias', + '_bibi', + expect.arrayContaining([ + expect.objectContaining({ + validation: 'regex', + code: 'invalid_string', + message: 'Invalid characters in alias', + path: [], + }), + ]), + ]) + }) + }) + + describe('alias contains two following hyphens', () => { + it('throws and logs an error', () => { + expect(validateAlias('bi--bi')).rejects.toEqual( + new Error('Invalid characters in alias'), + ) + expect(logger.warn.mock.calls[0]).toEqual([ + 'invalid alias', + 'bi--bi', + expect.arrayContaining([ + expect.objectContaining({ + validation: 'regex', + code: 'invalid_string', + message: 'Invalid characters in alias', + path: [], + }), + ]), + ]) + }) + }) + }) +// TODO: add integration test with real database to test the query, maybe move query + describe('test against existing alias in database', () => { + describe('alias exists in database', () => { + it('throws and logs an error', () => { + (aliasExists as jest.Mock).mockResolvedValue(true) + expect(validateAlias('b-b')).rejects.toEqual(new Error('Given alias is already in use')) + expect(logger.warn.mock.calls[0]).toEqual(['alias already in use', 'b-b']) + }) + }) + +/* describe('alias exists in database with in lower-case', () => { + it('throws and logs an error', () => { + expect(validateAlias('b-B')).rejects.toEqual(new Error('Given alias is already in use')) + expect(logger.warn.mock.calls[0]).toEqual(['alias already in use', 'b-B']) + }) + }) +*/ + describe('valid alias', () => { + it('resolves to true', async () => { + (aliasExists as jest.Mock).mockResolvedValue(false) + expect(validateAlias('bibi')).resolves.toEqual(true) + }) + }) + }) +}) diff --git a/shared/src/schema/user.schema.ts b/shared/src/schema/user.schema.ts new file mode 100644 index 000000000..23dbe3b85 --- /dev/null +++ b/shared/src/schema/user.schema.ts @@ -0,0 +1,54 @@ +import { z } from 'zod' +import { getLogger } from 'log4js' +import { LOG_CATEGORY_SCHEMA_ALIAS } from '.' +import { aliasExists } from 'database' + +export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/ +const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`) + +const RESERVED_ALIAS = [ + 'admin', + 'email', + 'gast', + 'gdd', + 'gradido', + 'guest', + 'home', + 'root', + 'support', + 'temp', + 'tmp', + 'tmp', + 'user', + 'usr', + 'var', +] + +export const aliasSchema = z + .string() + .min(3, 'Given alias is too short') + .max(20, 'Given alias is too long') + .regex(VALID_ALIAS_REGEX, 'Invalid characters in alias') + .refine((val) => !RESERVED_ALIAS.includes(val.toLowerCase()), { + message: 'Given alias is not allowed', + }) + +export const validateAlias = async (alias: string): Promise => { + try { + aliasSchema.parse(alias) + } catch (err) { + if (err instanceof z.ZodError) { + // throw only first error, but log all errors + logger.warn('invalid alias', alias, err.errors) + throw new Error(err.errors[0].message) + } + throw err + } + + if (await aliasExists(alias)) { + logger.warn('alias already in use', alias) + throw new Error('Given alias is already in use') + } + + return true +} diff --git a/shared/src/server/LogError.ts b/shared/src/server/LogError.ts deleted file mode 100644 index 346923019..000000000 --- a/shared/src/server/LogError.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { backendLogger as logger } from './logger' - -export class LogError extends Error { - constructor(msg: string, ...details: any[]) { - super(msg) - logger.error(msg, ...details) - } -} diff --git a/shared/src/server/logger.ts b/shared/src/server/logger.ts deleted file mode 100644 index 24ae79c67..000000000 --- a/shared/src/server/logger.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { readFileSync } from 'fs' - -import { configure, getLogger } from 'log4js' - -import { CONFIG } from '@/config' - -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 } diff --git a/shared/src/user/schema/alias.test.ts b/shared/src/user/schema/alias.test.ts deleted file mode 100644 index 0bdfb59a6..000000000 --- a/shared/src/user/schema/alias.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { validateAlias } from './alias' - -describe('validate alias', () => { - beforeAll(() => { - jest.clearAllMocks() - }) - - describe('alias too short', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('Bi')).rejects.toEqual(new Error('Given alias is too short')) - expect(logger.error).toBeCalledWith('Given alias is too short', 'Bi') - }) - }) - - describe('alias too long', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('BibiBloxbergHexHexHex')).rejects.toEqual( - new Error('Given alias is too long'), - ) - expect(logger.error).toBeCalledWith('Given alias is too long', 'BibiBloxbergHexHexHex') - }) - }) - - describe('alias contains invalid characters', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('Bibi.Bloxberg')).rejects.toEqual( - new Error('Invalid characters in alias'), - ) - expect(logger.error).toBeCalledWith('Invalid characters in alias', 'Bibi.Bloxberg') - }) - }) - - describe('alias is a reserved word', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('admin')).rejects.toEqual(new Error('Alias is not allowed')) - expect(logger.error).toBeCalledWith('Alias is not allowed', 'admin') - }) - }) - - describe('alias is a reserved word with uppercase characters', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('Admin')).rejects.toEqual(new Error('Alias is not allowed')) - expect(logger.error).toBeCalledWith('Alias is not allowed', 'Admin') - }) - }) - - describe('hyphens and underscore', () => { - describe('alias starts with underscore', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('_bibi')).rejects.toEqual( - new Error('Invalid characters in alias'), - ) - expect(logger.error).toBeCalledWith('Invalid characters in alias', '_bibi') - }) - }) - - describe('alias contains two following hyphens', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('bi--bi')).rejects.toEqual( - new Error('Invalid characters in alias'), - ) - expect(logger.error).toBeCalledWith('Invalid characters in alias', 'bi--bi') - }) - }) - }) - - describe('test against existing alias in database', () => { - beforeAll(async () => { - const bibi = await userFactory(testEnv, bibiBloxberg) - const user = await User.findOne({ where: { id: bibi.id } }) - if (user) { - user.alias = 'b-b' - await user.save() - } - }) - - describe('alias exists in database', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('b-b')).rejects.toEqual(new Error('Alias already in use')) - expect(logger.error).toBeCalledWith('Alias already in use', 'b-b') - }) - }) - - describe('alias exists in database with in lower-case', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('b-B')).rejects.toEqual(new Error('Alias already in use')) - expect(logger.error).toBeCalledWith('Alias already in use', 'b-B') - }) - }) - - describe('valid alias', () => { - it('resolves to true', async () => { - await expect(validateAlias('bibi')).resolves.toEqual(true) - }) - }) - }) -}) diff --git a/shared/src/user/schema/alias.ts b/shared/src/user/schema/alias.ts deleted file mode 100644 index c273f12ba..000000000 --- a/shared/src/user/schema/alias.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { z } from 'zod' -import { User as DbUser } from 'database' -import { Raw } from 'typeorm' -// import { LogError } from '@/server/LogError' - -class LogError extends Error { - details: any[] - constructor(msg: string, ...details: any[]) { - super(msg) - this.name = 'LogError' - this.message = msg - this.stack = new Error().stack - this.details = details - } -} - -export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/ - -const RESERVED_ALIAS = [ - 'admin', - 'email', - 'gast', - 'gdd', - 'gradido', - 'guest', - 'home', - 'root', - 'support', - 'temp', - 'tmp', - 'tmp', - 'user', - 'usr', - 'var', -] - -export const aliasSchema = z - .string() - .min(3, 'Alias is too short') - .max(20, 'Alias is too long') - .regex(VALID_ALIAS_REGEX, 'Invalid characters in alias') - .refine((val) => !RESERVED_ALIAS.includes(val.toLowerCase()), { - message: 'Alias is not allowed', - }) - -export const validateAlias = async (alias: string): Promise => { - try { - aliasSchema.parse(alias) - } catch (err) { - if (err instanceof z.ZodError) { - console.log(err) - throw new LogError(err.errors[0].message, alias) - } - throw err - } - - const aliasInUse = await DbUser.find({ - where: { alias: Raw((a) => `LOWER(${a}) = "${alias.toLowerCase()}"`) }, - }) - - if (aliasInUse.length !== 0) { - throw new LogError('Alias already in use', alias) - } - - return true -} diff --git a/shared/tsconfig.json b/shared/tsconfig.json index 04306edad..75b686340 100644 --- a/shared/tsconfig.json +++ b/shared/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ @@ -49,7 +48,7 @@ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ + // "types": ["bun-types"], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ @@ -69,5 +68,6 @@ "skipLibCheck": true, /* Skip type checking of declaration files. */ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, - "references": [] /* Any project that is referenced must itself have a `references` array (which may be empty). */ + "references": [], /* Any project that is referenced must itself have a `references` array (which may be empty). */ + "exclude": ["**/*.test.ts", "**/*.spec.ts", "test/*", "**/bun.d.ts"], } diff --git a/shared/turbo.json b/shared/turbo.json index 2d56da524..9f85ac803 100644 --- a/shared/turbo.json +++ b/shared/turbo.json @@ -1,16 +1,17 @@ { - "$schema": "https://turbo.build/schema.json", + "extends": ["//"], "tasks": { "lint": { }, "lint:fix": { }, "test": { + "dependsOn": ["config-schema#build"] }, "typecheck": { }, "dev": { - "dependsOn": ["database#build"], + "dependsOn": ["database#build", "config-schema#build"], "persistent": true, "cache": false }, diff --git a/yarn.lock b/yarn.lock index b8a1c2f43..29e6db28e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -386,6 +386,20 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@biomejs/biome@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" + integrity sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog== + 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" + "@biomejs/biome@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-2.0.0.tgz#dc770781565640b9f884ad3d6d6383f64ca257c2" @@ -400,41 +414,81 @@ "@biomejs/cli-win32-arm64" "2.0.0" "@biomejs/cli-win32-x64" "2.0.0" +"@biomejs/cli-darwin-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f" + integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== + "@biomejs/cli-darwin-arm64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0.tgz#67135faa8bd52933fdaad09a160f9fc3a9defef3" integrity sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw== +"@biomejs/cli-darwin-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" + integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== + "@biomejs/cli-darwin-x64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.0.0.tgz#49a7e064bad53e095d8a152b072adffcdeb4fb8e" integrity sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw== +"@biomejs/cli-linux-arm64-musl@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" + integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== + "@biomejs/cli-linux-arm64-musl@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0.tgz#bfde27de8262a20e57e153f3807f47a01ccaeab3" integrity sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA== +"@biomejs/cli-linux-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" + integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== + "@biomejs/cli-linux-arm64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.0.0.tgz#c2404b3869c03a6fa5a8b979755bc6dfd5a5ec47" integrity sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw== +"@biomejs/cli-linux-x64-musl@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb" + integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== + "@biomejs/cli-linux-x64-musl@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0.tgz#8ab214ac7e21a2af29435439145888c50f2bdd2f" integrity sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA== +"@biomejs/cli-linux-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8" + integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== + "@biomejs/cli-linux-x64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-2.0.0.tgz#cbd172ead9e5bba8cd590d06e6e548445cf7ab2a" integrity sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg== +"@biomejs/cli-win32-arm64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" + integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== + "@biomejs/cli-win32-arm64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.0.0.tgz#4677f4e034b3f4906e448b704f3314b38062a111" integrity sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw== +"@biomejs/cli-win32-x64@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" + integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== + "@biomejs/cli-win32-x64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-2.0.0.tgz#f460a6950235c8f4bbd4cc405b210fec5cdb8ac9" @@ -490,6 +544,28 @@ resolved "https://registry.yarnpkg.com/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#519c1549b0e147759e7825701ecffd25e5819f7b" integrity sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg== +"@emnapi/core@^1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.4.3.tgz#9ac52d2d5aea958f67e52c40a065f51de59b77d6" + integrity sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g== + dependencies: + "@emnapi/wasi-threads" "1.0.2" + tslib "^2.4.0" + +"@emnapi/runtime@^1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.4.3.tgz#c0564665c80dc81c448adac23f9dfbed6c838f7d" + integrity sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ== + dependencies: + tslib "^2.4.0" + +"@emnapi/wasi-threads@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz#977f44f844eac7d6c138a415a123818c655f874c" + integrity sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA== + dependencies: + tslib "^2.4.0" + "@esbuild/aix-ppc64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" @@ -500,6 +576,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz#014180d9a149cffd95aaeead37179433f5ea5437" integrity sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ== +"@esbuild/aix-ppc64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz#4e0f91776c2b340e75558f60552195f6fad09f18" + integrity sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA== + "@esbuild/android-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" @@ -510,6 +591,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz#649e47e04ddb24a27dc05c395724bc5f4c55cbfe" integrity sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ== +"@esbuild/android-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz#bc766407f1718923f6b8079c8c61bf86ac3a6a4f" + integrity sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg== + "@esbuild/android-arm@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" @@ -520,6 +606,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.3.tgz#8a0f719c8dc28a4a6567ef7328c36ea85f568ff4" integrity sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A== +"@esbuild/android-arm@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.5.tgz#4290d6d3407bae3883ad2cded1081a234473ce26" + integrity sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA== + "@esbuild/android-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" @@ -530,6 +621,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.3.tgz#e2ab182d1fd06da9bef0784a13c28a7602d78009" integrity sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ== +"@esbuild/android-x64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.5.tgz#40c11d9cbca4f2406548c8a9895d321bc3b35eff" + integrity sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw== + "@esbuild/darwin-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" @@ -540,6 +636,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz#c7f3166fcece4d158a73dcfe71b2672ca0b1668b" integrity sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w== +"@esbuild/darwin-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz#49d8bf8b1df95f759ac81eb1d0736018006d7e34" + integrity sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ== + "@esbuild/darwin-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" @@ -550,6 +651,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz#d8c5342ec1a4bf4b1915643dfe031ba4b173a87a" integrity sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A== +"@esbuild/darwin-x64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz#e27a5d92a14886ef1d492fd50fc61a2d4d87e418" + integrity sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ== + "@esbuild/freebsd-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" @@ -560,6 +666,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz#9f7d789e2eb7747d4868817417cc968ffa84f35b" integrity sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw== +"@esbuild/freebsd-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz#97cede59d638840ca104e605cdb9f1b118ba0b1c" + integrity sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw== + "@esbuild/freebsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" @@ -570,6 +681,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz#8ad35c51d084184a8e9e76bb4356e95350a64709" integrity sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q== +"@esbuild/freebsd-x64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz#71c77812042a1a8190c3d581e140d15b876b9c6f" + integrity sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw== + "@esbuild/linux-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" @@ -580,6 +696,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz#3af0da3d9186092a9edd4e28fa342f57d9e3cd30" integrity sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A== +"@esbuild/linux-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz#f7b7c8f97eff8ffd2e47f6c67eb5c9765f2181b8" + integrity sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg== + "@esbuild/linux-arm@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" @@ -590,6 +711,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz#e91cafa95e4474b3ae3d54da12e006b782e57225" integrity sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ== +"@esbuild/linux-arm@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz#2a0be71b6cd8201fa559aea45598dffabc05d911" + integrity sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw== + "@esbuild/linux-ia32@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" @@ -600,6 +726,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz#81025732d85b68ee510161b94acdf7e3007ea177" integrity sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw== +"@esbuild/linux-ia32@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz#763414463cd9ea6fa1f96555d2762f9f84c61783" + integrity sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA== + "@esbuild/linux-loong64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" @@ -610,6 +741,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz#3c744e4c8d5e1148cbe60a71a11b58ed8ee5deb8" integrity sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g== +"@esbuild/linux-loong64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz#428cf2213ff786a502a52c96cf29d1fcf1eb8506" + integrity sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg== + "@esbuild/linux-mips64el@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" @@ -620,6 +756,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz#1dfe2a5d63702db9034cc6b10b3087cc0424ec26" integrity sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag== +"@esbuild/linux-mips64el@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz#5cbcc7fd841b4cd53358afd33527cd394e325d96" + integrity sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg== + "@esbuild/linux-ppc64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" @@ -630,6 +771,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz#2e85d9764c04a1ebb346dc0813ea05952c9a5c56" integrity sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg== +"@esbuild/linux-ppc64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz#0d954ab39ce4f5e50f00c4f8c4fd38f976c13ad9" + integrity sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ== + "@esbuild/linux-riscv64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" @@ -640,6 +786,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz#a9ea3334556b09f85ccbfead58c803d305092415" integrity sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA== +"@esbuild/linux-riscv64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz#0e7dd30730505abd8088321e8497e94b547bfb1e" + integrity sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA== + "@esbuild/linux-s390x@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" @@ -650,6 +801,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz#f6a7cb67969222b200974de58f105dfe8e99448d" integrity sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ== +"@esbuild/linux-s390x@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz#5669af81327a398a336d7e40e320b5bbd6e6e72d" + integrity sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ== + "@esbuild/linux-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" @@ -660,11 +816,21 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz#a237d3578ecdd184a3066b1f425e314ade0f8033" integrity sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA== +"@esbuild/linux-x64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz#b2357dd153aa49038967ddc1ffd90c68a9d2a0d4" + integrity sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw== + "@esbuild/netbsd-arm64@0.25.3": version "0.25.3" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz#4c15c68d8149614ddb6a56f9c85ae62ccca08259" integrity sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA== +"@esbuild/netbsd-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz#53b4dfb8fe1cee93777c9e366893bd3daa6ba63d" + integrity sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw== + "@esbuild/netbsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" @@ -675,11 +841,21 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz#12f6856f8c54c2d7d0a8a64a9711c01a743878d5" integrity sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g== +"@esbuild/netbsd-x64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz#a0206f6314ce7dc8713b7732703d0f58de1d1e79" + integrity sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ== + "@esbuild/openbsd-arm64@0.25.3": version "0.25.3" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz#ca078dad4a34df192c60233b058db2ca3d94bc5c" integrity sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ== +"@esbuild/openbsd-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz#2a796c87c44e8de78001d808c77d948a21ec22fd" + integrity sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw== + "@esbuild/openbsd-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" @@ -690,6 +866,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz#c9178adb60e140e03a881d0791248489c79f95b2" integrity sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w== +"@esbuild/openbsd-x64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz#28d0cd8909b7fa3953af998f2b2ed34f576728f0" + integrity sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg== + "@esbuild/sunos-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" @@ -700,6 +881,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz#03765eb6d4214ff27e5230af779e80790d1ee09f" integrity sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA== +"@esbuild/sunos-x64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz#a28164f5b997e8247d407e36c90d3fd5ddbe0dc5" + integrity sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA== + "@esbuild/win32-arm64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" @@ -710,6 +896,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz#f1c867bd1730a9b8dfc461785ec6462e349411ea" integrity sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ== +"@esbuild/win32-arm64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz#6eadbead38e8bd12f633a5190e45eff80e24007e" + integrity sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw== + "@esbuild/win32-ia32@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" @@ -720,6 +911,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz#77491f59ef6c9ddf41df70670d5678beb3acc322" integrity sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew== +"@esbuild/win32-ia32@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz#bab6288005482f9ed2adb9ded7e88eba9a62cc0d" + integrity sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ== + "@esbuild/win32-x64@0.21.5": version "0.21.5" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" @@ -730,6 +926,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz#b17a2171f9074df9e91bfb07ef99a892ac06412a" integrity sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg== +"@esbuild/win32-x64@0.25.5": + version "0.25.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz#7fc114af5f6563f19f73324b5d5ff36ece0803d1" + integrity sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g== + "@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.6.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz#e4c58fdcf0696e7a5f19c30201ed43123ab15abc" @@ -1580,6 +1781,15 @@ "@napi-rs/nice-win32-ia32-msvc" "1.0.1" "@napi-rs/nice-win32-x64-msvc" "1.0.1" +"@napi-rs/wasm-runtime@^0.2.9": + version "0.2.11" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz#192c1610e1625048089ab4e35bc0649ce478500e" + integrity sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA== + dependencies: + "@emnapi/core" "^1.4.3" + "@emnapi/runtime" "^1.4.3" + "@tybys/wasm-util" "^0.9.0" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1634,6 +1844,73 @@ resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323" integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw== +"@oxc-resolver/binding-darwin-arm64@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-5.3.0.tgz#943360f8765ab7c1e45d122528c0cb6b6cfa9e18" + integrity sha512-hXem5ZAguS7IlSiHg/LK0tEfLj4eUo+9U6DaFwwBEGd0L0VIF9LmuiHydRyOrdnnmi9iAAFMAn/wl2cUoiuruA== + +"@oxc-resolver/binding-darwin-x64@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-5.3.0.tgz#ef9a4f86e530f7d3b88a07a6992aeffd8cab9027" + integrity sha512-wgSwfsZkRbuYCIBLxeg1bYrtKnirAy+IJF0lwfz4z08clgdNBDbfGECJe/cd0csIZPpRcvPFe8317yf31sWhtA== + +"@oxc-resolver/binding-freebsd-x64@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-5.3.0.tgz#024f1d5a0d09f15015ebe25940d8c64451050cde" + integrity sha512-kzeE2WHgcRMmWjB071RdwEV5Pwke4o0WWslCKoh8if1puvxIxfzu3o7g6P2+v77BP5qop4cri+uvLABSO0WZjg== + +"@oxc-resolver/binding-linux-arm-gnueabihf@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-5.3.0.tgz#be05c639320ee6b066e38057d381a1e97f109176" + integrity sha512-I8np34yZP/XfIkZNDbw3rweqVgfjmHYpNX3xnJZWg+f4mgO9/UNWBwetSaqXeDZqvIch/aHak+q4HVrQhQKCqg== + +"@oxc-resolver/binding-linux-arm64-gnu@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-5.3.0.tgz#118330cf96f43425463f95f7a673d2dc88edafa3" + integrity sha512-u2ndfeEUrW898eXM+qPxIN8TvTPjI90NDQBRgaxxkOfNw3xaotloeiZGz5+Yzlfxgvxr9DY9FdYkqhUhSnGhOw== + +"@oxc-resolver/binding-linux-arm64-musl@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-5.3.0.tgz#2675359d1116495a33ef51f6faf1996bf1c6e0b4" + integrity sha512-TzbjmFkcnESGuVItQ2diKacX8vu5G0bH3BHmIlmY4OSRLyoAlrJFwGKAHmh6C9+Amfcjo2rx8vdm7swzmsGC6Q== + +"@oxc-resolver/binding-linux-riscv64-gnu@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-5.3.0.tgz#964ca6b730cc751b84374b6bfec744e4c9081118" + integrity sha512-NH3pjAqh8nuN29iRuRfTY42Vn03ctoR9VE8llfoUKUfhHUjFHYOXK5VSkhjj1usG8AeuesvqrQnLptCRQVTi/Q== + +"@oxc-resolver/binding-linux-s390x-gnu@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-5.3.0.tgz#09e98f69220e40dce1d1d32c18fe2dcc037f3dc4" + integrity sha512-tuZtkK9sJYh2MC2uhol1M/8IMTB6ZQ5jmqP2+k5XNXnOb/im94Y5uV/u2lXwVyIuKHZZHtr+0d1HrOiNahoKpw== + +"@oxc-resolver/binding-linux-x64-gnu@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-5.3.0.tgz#450d5cad81c23266f2b8647b39d5aec5214ae712" + integrity sha512-VzhPYmZCtoES/ThcPdGSmMop7JlwgqtSvlgtKCW15ByV2JKyl8kHAHnPSBfpIooXb0ehFnRdxFtL9qtAEWy01g== + +"@oxc-resolver/binding-linux-x64-musl@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-5.3.0.tgz#524a2b3c7ad34244c20fcc6b7f98752799231f6a" + integrity sha512-Hi39cWzul24rGljN4Vf1lxjXzQdCrdxO5oCT7KJP4ndSlqIUODJnfnMAP1YhcnIRvNvk+5E6sZtnEmFUd/4d8Q== + +"@oxc-resolver/binding-wasm32-wasi@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-5.3.0.tgz#5b0435e453bbfb7767b1c11ce5e4e17156bd492d" + integrity sha512-ddujvHhP3chmHnSXRlkPVUeYj4/B7eLZwL4yUid+df3WCbVh6DgoT9RmllZn21AhxgKtMdekDdyVJYKFd8tl4A== + dependencies: + "@napi-rs/wasm-runtime" "^0.2.9" + +"@oxc-resolver/binding-win32-arm64-msvc@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-5.3.0.tgz#ef83aa140f7ff71bb52f1ead20b474015b99ec35" + integrity sha512-j1YYPLvUkMVNKmIFQZZJ7q6Do4cI3htUnyxNLwDSBVhSohvPIK2VG+IdtOAlWZGa7v+phEZsHfNbXVwB0oPYFQ== + +"@oxc-resolver/binding-win32-x64-msvc@5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-5.3.0.tgz#9c2c51c4bc664cec75946b8d4f971f6029cac890" + integrity sha512-LT9eOPPUqfZscQRd5mc08RBeDWOQf+dnOrKnanMallTGPe6g7+rcAlFTA8SWoJbcD45PV8yArFtCmSQSpzHZmg== + "@parcel/watcher-android-arm64@2.5.1": version "2.5.1" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1" @@ -1813,101 +2090,201 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz#e1562d360bca73c7bef6feef86098de3a2f1d442" integrity sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw== +"@rollup/rollup-android-arm-eabi@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz#a3e4e4b2baf0bade6918cf5135c3ef7eee653196" + integrity sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA== + "@rollup/rollup-android-arm64@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz#37ba63940211673e15dcc5f469a78e34276dbca7" integrity sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw== +"@rollup/rollup-android-arm64@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz#63566b0e76c62d4f96d44448f38a290562280200" + integrity sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw== + "@rollup/rollup-darwin-arm64@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz#58b1eb86d997d71dabc5b78903233a3c27438ca0" integrity sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA== +"@rollup/rollup-darwin-arm64@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz#60a51a61b22b1f4fdf97b4adf5f0f447f492759d" + integrity sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA== + "@rollup/rollup-darwin-x64@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz#5e22dab3232b1e575d930ce891abb18fe19c58c9" integrity sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw== +"@rollup/rollup-darwin-x64@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz#bfe3059440f7032de11e749ece868cd7f232e609" + integrity sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ== + "@rollup/rollup-freebsd-arm64@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz#04c892d9ff864d66e31419634726ab0bebb33707" integrity sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw== +"@rollup/rollup-freebsd-arm64@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz#d5d4c6cd3b8acb7493b76227d8b2b4a2d732a37b" + integrity sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ== + "@rollup/rollup-freebsd-x64@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz#f4b1e091f7cf5afc9e3a029d70128ad56409ecfb" integrity sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q== +"@rollup/rollup-freebsd-x64@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz#cb4e1547b572cd0144c5fbd6c4a0edfed5fe6024" + integrity sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g== + "@rollup/rollup-linux-arm-gnueabihf@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz#c8814bb5ce047a81b1fe4a33628dfd4ac52bd864" integrity sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg== +"@rollup/rollup-linux-arm-gnueabihf@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz#feb81bd086f6a469777f75bec07e1bdf93352e69" + integrity sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ== + "@rollup/rollup-linux-arm-musleabihf@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz#5b4e7bd83cbebbf5ffe958802dcfd4ee34bf73a3" integrity sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg== +"@rollup/rollup-linux-arm-musleabihf@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz#68bff1c6620c155c9d8f5ee6a83c46eb50486f18" + integrity sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg== + "@rollup/rollup-linux-arm64-gnu@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz#141c848e53cee011e82a11777b8a51f1b3e8d77c" integrity sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg== +"@rollup/rollup-linux-arm64-gnu@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz#dbc5036a85e3ca3349887c8bdbebcfd011e460b0" + integrity sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ== + "@rollup/rollup-linux-arm64-musl@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz#22ebeaf2fa301aa4aa6c84b760e6cd1d1ac7eb1e" integrity sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ== +"@rollup/rollup-linux-arm64-musl@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz#72efc633aa0b93531bdfc69d70bcafa88e6152fc" + integrity sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q== + "@rollup/rollup-linux-loongarch64-gnu@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz#20b77dc78e622f5814ff8e90c14c938ceb8043bc" integrity sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ== +"@rollup/rollup-linux-loongarch64-gnu@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz#9b6a49afde86c8f57ca11efdf8fd8d7c52048817" + integrity sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg== + "@rollup/rollup-linux-powerpc64le-gnu@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz#2c90f99c987ef1198d4f8d15d754c286e1f07b13" integrity sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg== +"@rollup/rollup-linux-powerpc64le-gnu@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz#93cb96073efab0cdbf419c8dfc44b5e2bd815139" + integrity sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ== + "@rollup/rollup-linux-riscv64-gnu@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz#9336fd5e47d7f4760d02aa85f76976176eef53ca" integrity sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ== +"@rollup/rollup-linux-riscv64-gnu@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz#028708f73c8130ae924e5c3755de50fe93687249" + integrity sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA== + "@rollup/rollup-linux-riscv64-musl@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz#d75b4d54d46439bb5c6c13762788f57e798f5670" integrity sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA== +"@rollup/rollup-linux-riscv64-musl@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz#878bfb158b2cf6671b7611fd58e5c80d9144ac6c" + integrity sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q== + "@rollup/rollup-linux-s390x-gnu@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz#e9f09b802f1291839247399028beaef9ce034c81" integrity sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg== +"@rollup/rollup-linux-s390x-gnu@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz#59b4ebb2129d34b7807ed8c462ff0baaefca9ad4" + integrity sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA== + "@rollup/rollup-linux-x64-gnu@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz#0413169dc00470667dea8575c1129d4e7a73eb29" integrity sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ== +"@rollup/rollup-linux-x64-gnu@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz#597d40f60d4b15bedbbacf2491a69c5b67a58e93" + integrity sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw== + "@rollup/rollup-linux-x64-musl@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz#c76fd593323c60ea219439a00da6c6d33ffd0ea6" integrity sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ== +"@rollup/rollup-linux-x64-musl@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz#0a062d6fee35ec4fbb607b2a9d933a9372ccf63a" + integrity sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA== + "@rollup/rollup-win32-arm64-msvc@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz#c7724c386eed0bda5ae7143e4081c1910cab349b" integrity sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg== +"@rollup/rollup-win32-arm64-msvc@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz#41ffab489857987c75385b0fc8cccf97f7e69d0a" + integrity sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w== + "@rollup/rollup-win32-ia32-msvc@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz#7749e1b65cb64fe6d41ad1ad9e970a0ccc8ac350" integrity sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA== +"@rollup/rollup-win32-ia32-msvc@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz#d9fb61d98eedfa52720b6ed9f31442b3ef4b839f" + integrity sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA== + "@rollup/rollup-win32-x64-msvc@4.40.1": version "4.40.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz#8078b71fe0d5825dcbf83d52a7dc858b39da165c" integrity sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA== +"@rollup/rollup-win32-x64-msvc@4.44.0": + version "4.44.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz#a36e79b6ccece1533f777a1bca1f89c13f0c5f62" + integrity sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ== + "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" @@ -1980,6 +2357,32 @@ resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.5.tgz#3abc203c79b8c3e90fd6c156a0c62d5403520e12" integrity sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw== +"@swc-node/core@^1.13.3": + version "1.13.3" + resolved "https://registry.yarnpkg.com/@swc-node/core/-/core-1.13.3.tgz#0821d01263f48314392d38d80ef1a03fef5f11b3" + integrity sha512-OGsvXIid2Go21kiNqeTIn79jcaX4l0G93X2rAnas4LFoDyA9wAwVK7xZdm+QsKoMn5Mus2yFLCc4OtX2dD/PWA== + +"@swc-node/register@^1.10.10": + version "1.10.10" + resolved "https://registry.yarnpkg.com/@swc-node/register/-/register-1.10.10.tgz#04652ced75f5f2f0ca40b8db587ffa913696fa52" + integrity sha512-jYWaI2WNEKz8KZL3sExd2KVL1JMma1/J7z+9iTpv0+fRN7LGMF8VTGGuHI2bug/ztpdZU1G44FG/Kk6ElXL9CQ== + dependencies: + "@swc-node/core" "^1.13.3" + "@swc-node/sourcemap-support" "^0.5.1" + colorette "^2.0.20" + debug "^4.3.5" + oxc-resolver "^5.0.0" + pirates "^4.0.6" + tslib "^2.6.3" + +"@swc-node/sourcemap-support@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@swc-node/sourcemap-support/-/sourcemap-support-0.5.1.tgz#0355540d62874891770ce1ba06838de186f098ff" + integrity sha512-JxIvIo/Hrpv0JCHSyRpetAdQ6lB27oFYhv0PKCNf1g2gUXOjpeR1exrXccRxLMuAV5WAmGFBwRnNOJqN38+qtg== + dependencies: + source-map-support "^0.5.21" + tslib "^2.6.3" + "@swc/cli@^0.7.3": version "0.7.3" resolved "https://registry.yarnpkg.com/@swc/cli/-/cli-0.7.3.tgz#853d9b997bdfb9f58ba5dcb8e23e82ac5dfd8d53" @@ -2129,6 +2532,13 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== +"@tybys/wasm-util@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.9.0.tgz#3e75eb00604c8d6db470bf18c37b7d984a0e3355" + integrity sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw== + dependencies: + tslib "^2.4.0" + "@types/accepts@*", "@types/accepts@^1.3.5": version "1.3.7" resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.7.tgz#3b98b1889d2b2386604c2bbbe62e4fb51e95b265" @@ -2185,6 +2595,13 @@ "@types/connect" "*" "@types/node" "*" +"@types/chai@^5.2.2": + version "5.2.2" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.2.tgz#6f14cea18180ffc4416bc0fd12be05fdd73bdd6b" + integrity sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg== + dependencies: + "@types/deep-eql" "*" + "@types/connect@*": version "3.4.38" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" @@ -2212,6 +2629,11 @@ resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.10.tgz#61cc8469849e5bcdd0c7044122265c39cec10cf4" integrity sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ== +"@types/deep-eql@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" + integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== + "@types/dotenv@^8.2.3": version "8.2.3" resolved "https://registry.yarnpkg.com/@types/dotenv/-/dotenv-8.2.3.tgz#c97b3c5b2e97ff3873793a000999e86cd66ff354" @@ -2249,6 +2671,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.7.tgz#4158d3105276773d5b7695cd4834b1722e4f37a8" integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== +"@types/estree@1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + "@types/express-serve-static-core@^4.17.21", "@types/express-serve-static-core@^4.17.33": version "4.19.6" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz#e01324c2a024ff367d92c66f48553ced0ab50267" @@ -2518,6 +2945,13 @@ dependencies: undici-types "~5.26.4" +"@types/node@^18.7.14": + version "18.19.112" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.112.tgz#cd2aee9c075402e0e1942a44101428881dbeb110" + integrity sha512-i+Vukt9POdS/MBI7YrrkkI5fMfwFtOjphSmt4WXYLfwqsfr6z/HdCx7LqT9M7JktGob8WNgj8nFB4TbGNE4Cog== + dependencies: + undici-types "~5.26.4" + "@types/nodemailer@*", "@types/nodemailer@^6.4.4": version "6.4.17" resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.17.tgz#5c82a42aee16a3dd6ea31446a1bd6a447f1ac1a4" @@ -2755,6 +3189,17 @@ chai "^5.1.2" tinyrainbow "^1.2.0" +"@vitest/expect@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.4.tgz#8362124cd811a5ee11c5768207b9df53d34f2433" + integrity sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig== + dependencies: + "@types/chai" "^5.2.2" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + tinyrainbow "^2.0.0" + "@vitest/mocker@2.1.9": version "2.1.9" resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.9.tgz#36243b27351ca8f4d0bbc4ef91594ffd2dc25ef5" @@ -2764,6 +3209,15 @@ estree-walker "^3.0.3" magic-string "^0.30.12" +"@vitest/mocker@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.2.4.tgz#4471c4efbd62db0d4fa203e65cc6b058a85cabd3" + integrity sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ== + dependencies: + "@vitest/spy" "3.2.4" + estree-walker "^3.0.3" + magic-string "^0.30.17" + "@vitest/pretty-format@2.1.9", "@vitest/pretty-format@^2.1.9": version "2.1.9" resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.9.tgz#434ff2f7611689f9ce70cd7d567eceb883653fdf" @@ -2771,6 +3225,13 @@ dependencies: tinyrainbow "^1.2.0" +"@vitest/pretty-format@3.2.4", "@vitest/pretty-format@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz#3c102f79e82b204a26c7a5921bf47d534919d3b4" + integrity sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA== + dependencies: + tinyrainbow "^2.0.0" + "@vitest/runner@2.1.9": version "2.1.9" resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.9.tgz#cc18148d2d797fd1fd5908d1f1851d01459be2f6" @@ -2779,6 +3240,15 @@ "@vitest/utils" "2.1.9" pathe "^1.1.2" +"@vitest/runner@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.2.4.tgz#5ce0274f24a971f6500f6fc166d53d8382430766" + integrity sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ== + dependencies: + "@vitest/utils" "3.2.4" + pathe "^2.0.3" + strip-literal "^3.0.0" + "@vitest/snapshot@2.1.9": version "2.1.9" resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.9.tgz#24260b93f798afb102e2dcbd7e61c6dfa118df91" @@ -2788,6 +3258,15 @@ magic-string "^0.30.12" pathe "^1.1.2" +"@vitest/snapshot@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.2.4.tgz#40a8bc0346ac0aee923c0eefc2dc005d90bc987c" + integrity sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ== + dependencies: + "@vitest/pretty-format" "3.2.4" + magic-string "^0.30.17" + pathe "^2.0.3" + "@vitest/spy@2.1.9": version "2.1.9" resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.9.tgz#cb28538c5039d09818b8bfa8edb4043c94727c60" @@ -2795,6 +3274,13 @@ dependencies: tinyspy "^3.0.2" +"@vitest/spy@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.2.4.tgz#cc18f26f40f3f028da6620046881f4e4518c2599" + integrity sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw== + dependencies: + tinyspy "^4.0.3" + "@vitest/utils@2.1.9": version "2.1.9" resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.9.tgz#4f2486de8a54acf7ecbf2c5c24ad7994a680a6c1" @@ -2804,6 +3290,15 @@ loupe "^3.1.2" tinyrainbow "^1.2.0" +"@vitest/utils@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.2.4.tgz#c0813bc42d99527fb8c5b138c7a88516bca46fea" + integrity sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA== + dependencies: + "@vitest/pretty-format" "3.2.4" + loupe "^3.1.4" + tinyrainbow "^2.0.0" + "@vue-leaflet/vue-leaflet@^0.10.1": version "0.10.1" resolved "https://registry.yarnpkg.com/@vue-leaflet/vue-leaflet/-/vue-leaflet-0.10.1.tgz#17330515629d500ac113009a49f8cf98d140e8cc" @@ -4299,7 +4794,7 @@ caniuse-lite@^1.0.30001688, caniuse-lite@^1.0.30001702: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz#bd325a37ad366e3fe90827d74062807a34fbaeb2" integrity sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw== -chai@^5.1.2: +chai@^5.1.2, chai@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05" integrity sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw== @@ -4516,7 +5011,7 @@ colord@^2.9.3: resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== -colorette@^2.0.16: +colorette@^2.0.16, colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -4895,6 +5390,13 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.5, debug@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + debugging-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/debugging-stream/-/debugging-stream-2.0.0.tgz#515cad5a35299cf4b4bc0afcbd69d52c809c84ce" @@ -5423,7 +5925,7 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-module-lexer@^1.2.1, es-module-lexer@^1.5.4: +es-module-lexer@^1.2.1, es-module-lexer@^1.5.4, es-module-lexer@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== @@ -5495,6 +5997,37 @@ esbuild@^0.21.3: "@esbuild/win32-ia32" "0.21.5" "@esbuild/win32-x64" "0.21.5" +esbuild@^0.25.0: + version "0.25.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.5.tgz#71075054993fdfae76c66586f9b9c1f8d7edd430" + integrity sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.5" + "@esbuild/android-arm" "0.25.5" + "@esbuild/android-arm64" "0.25.5" + "@esbuild/android-x64" "0.25.5" + "@esbuild/darwin-arm64" "0.25.5" + "@esbuild/darwin-x64" "0.25.5" + "@esbuild/freebsd-arm64" "0.25.5" + "@esbuild/freebsd-x64" "0.25.5" + "@esbuild/linux-arm" "0.25.5" + "@esbuild/linux-arm64" "0.25.5" + "@esbuild/linux-ia32" "0.25.5" + "@esbuild/linux-loong64" "0.25.5" + "@esbuild/linux-mips64el" "0.25.5" + "@esbuild/linux-ppc64" "0.25.5" + "@esbuild/linux-riscv64" "0.25.5" + "@esbuild/linux-s390x" "0.25.5" + "@esbuild/linux-x64" "0.25.5" + "@esbuild/netbsd-arm64" "0.25.5" + "@esbuild/netbsd-x64" "0.25.5" + "@esbuild/openbsd-arm64" "0.25.5" + "@esbuild/openbsd-x64" "0.25.5" + "@esbuild/sunos-x64" "0.25.5" + "@esbuild/win32-arm64" "0.25.5" + "@esbuild/win32-ia32" "0.25.5" + "@esbuild/win32-x64" "0.25.5" + esbuild@^0.25.2, esbuild@^0.25.3, esbuild@~0.25.0: version "0.25.3" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.3.tgz#371f7cb41283e5b2191a96047a7a89562965a285" @@ -5936,7 +6469,7 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect-type@^1.1.0: +expect-type@^1.1.0, expect-type@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.1.tgz#af76d8b357cf5fa76c41c09dafb79c549e75f71f" integrity sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw== @@ -8499,6 +9032,11 @@ loupe@^3.1.0, loupe@^3.1.2: resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.3.tgz#042a8f7986d77f3d0f98ef7990a2b2fef18b0fd2" integrity sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug== +loupe@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.4.tgz#784a0060545cb38778ffb19ccde44d7870d5fdd9" + integrity sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg== + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -9221,6 +9759,25 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" +oxc-resolver@^5.0.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/oxc-resolver/-/oxc-resolver-5.3.0.tgz#1f648a5ce0472ab40472e344f513dec824723c2e" + integrity sha512-FHqtZx0idP5QRPSNcI5g2ItmADg7fhR3XIeWg5eRMGfp44xqRpfkdvo+EX4ZceqV9bxvl0Z8vaqMqY0gYaNYNA== + optionalDependencies: + "@oxc-resolver/binding-darwin-arm64" "5.3.0" + "@oxc-resolver/binding-darwin-x64" "5.3.0" + "@oxc-resolver/binding-freebsd-x64" "5.3.0" + "@oxc-resolver/binding-linux-arm-gnueabihf" "5.3.0" + "@oxc-resolver/binding-linux-arm64-gnu" "5.3.0" + "@oxc-resolver/binding-linux-arm64-musl" "5.3.0" + "@oxc-resolver/binding-linux-riscv64-gnu" "5.3.0" + "@oxc-resolver/binding-linux-s390x-gnu" "5.3.0" + "@oxc-resolver/binding-linux-x64-gnu" "5.3.0" + "@oxc-resolver/binding-linux-x64-musl" "5.3.0" + "@oxc-resolver/binding-wasm32-wasi" "5.3.0" + "@oxc-resolver/binding-win32-arm64-msvc" "5.3.0" + "@oxc-resolver/binding-win32-x64-msvc" "5.3.0" + p-cancelable@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" @@ -9490,7 +10047,7 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pirates@^4.0.4: +pirates@^4.0.4, pirates@^4.0.6: version "4.0.7" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== @@ -10165,6 +10722,35 @@ rollup@^4.20.0: "@rollup/rollup-win32-x64-msvc" "4.40.1" fsevents "~2.3.2" +rollup@^4.34.9: + version "4.44.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.44.0.tgz#0e10b98339b306edad1e612f1e5590a79aef521c" + integrity sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.44.0" + "@rollup/rollup-android-arm64" "4.44.0" + "@rollup/rollup-darwin-arm64" "4.44.0" + "@rollup/rollup-darwin-x64" "4.44.0" + "@rollup/rollup-freebsd-arm64" "4.44.0" + "@rollup/rollup-freebsd-x64" "4.44.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.44.0" + "@rollup/rollup-linux-arm-musleabihf" "4.44.0" + "@rollup/rollup-linux-arm64-gnu" "4.44.0" + "@rollup/rollup-linux-arm64-musl" "4.44.0" + "@rollup/rollup-linux-loongarch64-gnu" "4.44.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.44.0" + "@rollup/rollup-linux-riscv64-gnu" "4.44.0" + "@rollup/rollup-linux-riscv64-musl" "4.44.0" + "@rollup/rollup-linux-s390x-gnu" "4.44.0" + "@rollup/rollup-linux-x64-gnu" "4.44.0" + "@rollup/rollup-linux-x64-musl" "4.44.0" + "@rollup/rollup-win32-arm64-msvc" "4.44.0" + "@rollup/rollup-win32-ia32-msvc" "4.44.0" + "@rollup/rollup-win32-x64-msvc" "4.44.0" + fsevents "~2.3.2" + rrweb-cssom@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b" @@ -11211,21 +11797,44 @@ tinyglobby@^0.2.12, tinyglobby@^0.2.13: fdir "^6.4.4" picomatch "^4.0.2" +tinyglobby@^0.2.14: + version "0.2.14" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.14.tgz#5280b0cf3f972b050e74ae88406c0a6a58f4079d" + integrity sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ== + dependencies: + fdir "^6.4.4" + picomatch "^4.0.2" + tinypool@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== +tinypool@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.1.tgz#059f2d042bd37567fbc017d3d426bdd2a2612591" + integrity sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg== + tinyrainbow@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== +tinyrainbow@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + tinyspy@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== +tinyspy@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.3.tgz#d1d0f0602f4c15f1aae083a34d6d0df3363b1b52" + integrity sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A== + titleize@2: version "2.1.0" resolved "https://registry.yarnpkg.com/titleize/-/titleize-2.1.0.tgz#5530de07c22147a0488887172b5bd94f5b30a48f" @@ -11442,7 +12051,7 @@ tslib@^1.10.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.5.0, tslib@^2.8.0, tslib@^2.8.1: +tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.3, tslib@^2.8.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -11457,6 +12066,16 @@ tsx@^4.19.4: optionalDependencies: fsevents "~2.3.3" +tsx@^4.20.3: + version "4.20.3" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.20.3.tgz#f913e4911d59ad177c1bcee19d1035ef8dd6e2fb" + integrity sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ== + dependencies: + esbuild "~0.25.0" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" + tua-body-scroll-lock@^1.5.1: version "1.5.3" resolved "https://registry.yarnpkg.com/tua-body-scroll-lock/-/tua-body-scroll-lock-1.5.3.tgz#3f1464d599aea4734f31e0754088acd42804e3e8" @@ -11648,6 +12267,11 @@ typescript@^4.9.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + uc.micro@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" @@ -11962,6 +12586,17 @@ vite-node@2.1.9: pathe "^1.1.2" vite "^5.0.0" +vite-node@3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07" + integrity sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg== + dependencies: + cac "^6.7.14" + debug "^4.4.1" + es-module-lexer "^1.7.0" + pathe "^2.0.3" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-plugin-commonjs@^0.10.1: version "0.10.4" resolved "https://registry.yarnpkg.com/vite-plugin-commonjs/-/vite-plugin-commonjs-0.10.4.tgz#5bcf316a323e3b88e392ec2de8754ca5f249b74f" @@ -12024,6 +12659,20 @@ vite@^5.0.0, vite@^5.4.14: optionalDependencies: fsevents "~2.3.3" +"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0": + version "6.3.5" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.3.5.tgz#fec73879013c9c0128c8d284504c6d19410d12a3" + integrity sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ== + dependencies: + esbuild "^0.25.0" + fdir "^6.4.4" + picomatch "^4.0.2" + postcss "^8.5.3" + rollup "^4.34.9" + tinyglobby "^0.2.13" + optionalDependencies: + fsevents "~2.3.3" + vitest-canvas-mock@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/vitest-canvas-mock/-/vitest-canvas-mock-0.3.3.tgz#97e3b5f53003c5cbb9540204ff3122cd25be4dcd" @@ -12057,6 +12706,35 @@ vitest@^2.0.5: vite-node "2.1.9" why-is-node-running "^2.3.0" +vitest@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea" + integrity sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A== + dependencies: + "@types/chai" "^5.2.2" + "@vitest/expect" "3.2.4" + "@vitest/mocker" "3.2.4" + "@vitest/pretty-format" "^3.2.4" + "@vitest/runner" "3.2.4" + "@vitest/snapshot" "3.2.4" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + debug "^4.4.1" + expect-type "^1.2.1" + magic-string "^0.30.17" + pathe "^2.0.3" + picomatch "^4.0.2" + std-env "^3.9.0" + tinybench "^2.9.0" + tinyexec "^0.3.2" + tinyglobby "^0.2.14" + tinypool "^1.1.1" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-node "3.2.4" + why-is-node-running "^2.3.0" + void-elements@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" From c9338affd8aa5563bc54a64dd4f6e060de883b08 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 20 Jun 2025 16:49:37 +0200 Subject: [PATCH 23/46] fixed biome version --- bun.lock | 20 +---------------- shared/package.json | 2 +- yarn.lock | 54 --------------------------------------------- 3 files changed, 2 insertions(+), 74 deletions(-) diff --git a/bun.lock b/bun.lock index 005ccc1ae..6e2eb4afe 100644 --- a/bun.lock +++ b/bun.lock @@ -407,7 +407,7 @@ "zod": "^3.25.61", }, "devDependencies": { - "@biomejs/biome": "1.9.4", + "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", "typescript": "^5.8.3", }, @@ -3880,8 +3880,6 @@ "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], - "shared/@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=="], - "shared/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], "simple-update-notifier/semver": ["semver@7.0.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A=="], @@ -4240,22 +4238,6 @@ "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "shared/@biomejs/biome/@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], - - "shared/@biomejs/biome/@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], - - "shared/@biomejs/biome/@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], - - "shared/@biomejs/biome/@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], - - "shared/@biomejs/biome/@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], - - "shared/@biomejs/biome/@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], - - "shared/@biomejs/biome/@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], - - "shared/@biomejs/biome/@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], - "sodium-secretstream/sodium-universal/sodium-native": ["sodium-native@5.0.1", "", { "dependencies": { "require-addon": "^1.1.0", "which-runtime": "^1.2.1" } }, "sha512-Q305aUXc0OzK7VVRvWkeEQJQIHs6slhFwWpyqLB5iJqhpyt2lYIVu96Y6PQ7TABIlWXVF3YiWDU3xS2Snkus+g=="], "streamroller/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], diff --git a/shared/package.json b/shared/package.json index e16907502..279c27ee9 100644 --- a/shared/package.json +++ b/shared/package.json @@ -24,7 +24,7 @@ "lint:fix": "biome check --error-on-warnings . --write" }, "devDependencies": { - "@biomejs/biome": "1.9.4", + "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", "typescript": "^5.8.3" }, diff --git a/yarn.lock b/yarn.lock index 29e6db28e..6366943f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -386,20 +386,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@biomejs/biome@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" - integrity sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog== - 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" - "@biomejs/biome@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-2.0.0.tgz#dc770781565640b9f884ad3d6d6383f64ca257c2" @@ -414,81 +400,41 @@ "@biomejs/cli-win32-arm64" "2.0.0" "@biomejs/cli-win32-x64" "2.0.0" -"@biomejs/cli-darwin-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz#dfa376d23a54a2d8f17133c92f23c1bf2e62509f" - integrity sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw== - "@biomejs/cli-darwin-arm64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0.tgz#67135faa8bd52933fdaad09a160f9fc3a9defef3" integrity sha512-QvqWYtFFhhxdf8jMAdJzXW+Frc7X8XsnHQLY+TBM1fnT1TfeV/v9vsFI5L2J7GH6qN1+QEEJ19jHibCY2Ypplw== -"@biomejs/cli-darwin-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz#eafc2ce3849d385fc02238aad1ca4a73395a64d9" - integrity sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg== - "@biomejs/cli-darwin-x64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.0.0.tgz#49a7e064bad53e095d8a152b072adffcdeb4fb8e" integrity sha512-5JFhls1EfmuIH4QGFPlNpxJQFC6ic3X1ltcoLN+eSRRIPr6H/lUS1ttuD0Fj7rPgPhZqopK/jfH8UVj/1hIsQw== -"@biomejs/cli-linux-arm64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz#d780c3e01758fc90f3268357e3f19163d1f84fca" - integrity sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA== - "@biomejs/cli-linux-arm64-musl@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0.tgz#bfde27de8262a20e57e153f3807f47a01ccaeab3" integrity sha512-Bxsz8ki8+b3PytMnS5SgrGV+mbAWwIxI3ydChb/d1rURlJTMdxTTq5LTebUnlsUWAX6OvJuFeiVq9Gjn1YbCyA== -"@biomejs/cli-linux-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz#8ed1dd0e89419a4b66a47f95aefb8c46ae6041c9" - integrity sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g== - "@biomejs/cli-linux-arm64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.0.0.tgz#c2404b3869c03a6fa5a8b979755bc6dfd5a5ec47" integrity sha512-BAH4QVi06TzAbVchXdJPsL0Z/P87jOfes15rI+p3EX9/EGTfIjaQ9lBVlHunxcmoptaA5y1Hdb9UYojIhmnjIw== -"@biomejs/cli-linux-x64-musl@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz#f36982b966bd671a36671e1de4417963d7db15fb" - integrity sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg== - "@biomejs/cli-linux-x64-musl@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0.tgz#8ab214ac7e21a2af29435439145888c50f2bdd2f" integrity sha512-tiQ0ABxMJb9I6GlfNp0ulrTiQSFacJRJO8245FFwE3ty3bfsfxlU/miblzDIi+qNrgGsLq5wIZcVYGp4c+HXZA== -"@biomejs/cli-linux-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz#a0a7f56680c76b8034ddc149dbf398bdd3a462e8" - integrity sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg== - "@biomejs/cli-linux-x64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-2.0.0.tgz#cbd172ead9e5bba8cd590d06e6e548445cf7ab2a" integrity sha512-09PcOGYTtkopWRm6mZ/B6Mr6UHdkniUgIG/jLBv+2J8Z61ezRE+xQmpi3yNgUrFIAU4lPA9atg7mhvE/5Bo7Wg== -"@biomejs/cli-win32-arm64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz#e2ef4e0084e76b7e26f0fc887c5ef1265ea56200" - integrity sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg== - "@biomejs/cli-win32-arm64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.0.0.tgz#4677f4e034b3f4906e448b704f3314b38062a111" integrity sha512-vrTtuGu91xNTEQ5ZcMJBZuDlqr32DWU1r14UfePIGndF//s2WUAmer4FmgoPgruo76rprk37e8S2A2c0psXdxw== -"@biomejs/cli-win32-x64@1.9.4": - version "1.9.4" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz#4c7afa90e3970213599b4095e62f87e5972b2340" - integrity sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA== - "@biomejs/cli-win32-x64@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-2.0.0.tgz#f460a6950235c8f4bbd4cc405b210fec5cdb8ac9" From 9e1cb68ef96b52e5b53f6066f829bdebdf5b775e Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 20 Jun 2025 16:50:32 +0200 Subject: [PATCH 24/46] add shared to lint_pr --- .github/workflows/lint_pr.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint_pr.yml b/.github/workflows/lint_pr.yml index d5b4cf72f..7ec370242 100644 --- a/.github/workflows/lint_pr.yml +++ b/.github/workflows/lint_pr.yml @@ -35,6 +35,7 @@ jobs: workflow docker other + shared # Configure that a scope must always be provided. requireScope: true # Configure which scopes (newline delimited) are disallowed in PR From 3385f88c78097e6e4c65a2a0d0fa7f79d6e2c4b4 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 21 Jun 2025 14:30:02 +0200 Subject: [PATCH 25/46] add additional module core, move some code into it --- .github/workflows/test_database.yml | 4 +- .github/workflows/test_shared.yml | 2 +- backend/package.json | 8 +- .../federation/validateCommunities.test.ts | 10 +- backend/src/graphql/resolver/UserResolver.ts | 2 +- .../resolver/util/findUserByIdentifier.ts | 4 +- .../resolver/util/validateAlias.test.ts | 129 ---------------- .../graphql/resolver/util/validateAlias.ts | 46 ------ backend/test/helpers.ts | 4 +- bun.lock | 146 +++++++++--------- config-schema/package.json | 4 +- {shared => core}/bunfig.toml | 0 core/package.json | 40 +++++ {shared => core}/src/config/const.ts | 0 core/src/index.ts | 1 + core/src/schema/index.ts | 4 + core/src/schema/user.schema.test.ts | 68 ++++++++ core/src/schema/user.schema.ts | 27 ++++ core/tsconfig.json | 73 +++++++++ core/turbo.json | 24 +++ database/package.json | 5 +- database/src/AppDatabase.ts | 2 +- database/src/seeds/factory/user.ts | 8 +- dht-node/package.json | 3 +- dht-node/test/helpers.ts | 4 +- dht-node/tsconfig.json | 2 +- federation/package.json | 2 +- federation/test/helpers.ts | 4 +- frontend/package.json | 2 +- package.json | 1 + scripts/release.sh | 9 ++ shared/package.json | 7 +- shared/src/enum/OptInType.ts | 8 + shared/src/enum/PasswordEncryptionType.ts | 11 ++ shared/src/enum/RoleNames.ts | 13 ++ shared/src/enum/UserContactType.ts | 9 ++ shared/src/enum/index.ts | 4 + shared/src/index.ts | 5 +- shared/src/schema/index.ts | 5 +- shared/src/schema/user.schema.test.ts | 142 ++++------------- shared/src/schema/user.schema.ts | 29 +--- yarn.lock | 54 +++++-- 42 files changed, 479 insertions(+), 446 deletions(-) delete mode 100644 backend/src/graphql/resolver/util/validateAlias.test.ts delete mode 100644 backend/src/graphql/resolver/util/validateAlias.ts rename {shared => core}/bunfig.toml (100%) create mode 100644 core/package.json rename {shared => core}/src/config/const.ts (100%) create mode 100644 core/src/index.ts create mode 100644 core/src/schema/index.ts create mode 100644 core/src/schema/user.schema.test.ts create mode 100644 core/src/schema/user.schema.ts create mode 100644 core/tsconfig.json create mode 100644 core/turbo.json create mode 100644 shared/src/enum/OptInType.ts create mode 100644 shared/src/enum/PasswordEncryptionType.ts create mode 100644 shared/src/enum/RoleNames.ts create mode 100644 shared/src/enum/UserContactType.ts create mode 100644 shared/src/enum/index.ts diff --git a/.github/workflows/test_database.yml b/.github/workflows/test_database.yml index a4e6de787..97b0e5fe8 100644 --- a/.github/workflows/test_database.yml +++ b/.github/workflows/test_database.yml @@ -59,10 +59,10 @@ jobs: bun install --global --no-save turbo@^2 - name: Database | up + test - run: turbo test + run: turbo database#test - name: Database | reset - run: turbo reset + run: turbo database#reset lint: if: needs.files-changed.outputs.database == 'true' diff --git a/.github/workflows/test_shared.yml b/.github/workflows/test_shared.yml index 5fa1068da..8a1484655 100644 --- a/.github/workflows/test_shared.yml +++ b/.github/workflows/test_shared.yml @@ -34,7 +34,7 @@ jobs: uses: oven-sh/setup-bun@v2 - name: install dependencies - run: bun install --filter shared database --frozen-lockfile + run: bun install --filter shared --frozen-lockfile - name: typecheck run: cd shared && yarn typecheck diff --git a/backend/package.json b/backend/package.json index aeb6fcb5e..cd06351aa 100644 --- a/backend/package.json +++ b/backend/package.json @@ -57,6 +57,7 @@ "axios": "^0.21.1", "class-validator": "^0.13.1", "config-schema": "*", + "core": "*", "cors": "^2.8.5", "database": "*", "decimal.js-light": "^2.5.1", @@ -65,7 +66,7 @@ "express": "^4.17.21", "express-slow-down": "^2.0.1", "faker": "^5.5.3", - "graphql": "^15.10.1", + "graphql": "15.10.1", "graphql-parse-resolve-info": "^4.13.1", "graphql-request": "5.0.0", "graphql-tag": "^2.12.6", @@ -88,13 +89,14 @@ "random-bigint": "^0.0.1", "reflect-metadata": "^0.1.13", "regenerator-runtime": "^0.14.1", + "shared": "*", "source-map-support": "^0.5.21", - "ts-jest": "27.0.5", + "ts-jest": "29.4.0", "ts-node": "^10.9.2", "tsconfig-paths": "^4.1.1", "type-graphql": "^1.1.1", "typed-rest-client": "^1.8.11", - "typeorm": "^0.3.22", + "typeorm": "^0.3.25", "typescript": "^4.9.5", "uuid": "^8.3.2", "workerpool": "^9.2.0", diff --git a/backend/src/federation/validateCommunities.test.ts b/backend/src/federation/validateCommunities.test.ts index 160068140..ecb78124a 100644 --- a/backend/src/federation/validateCommunities.test.ts +++ b/backend/src/federation/validateCommunities.test.ts @@ -2,12 +2,12 @@ import { ApolloServerTestClient } from 'apollo-server-testing' import { FederatedCommunity as DbFederatedCommunity } from 'database' import { GraphQLClient } from 'graphql-request' import { Response } from 'graphql-request/dist/types' -import { DataSource } from 'typeorm' +import { DataSource, Not } from 'typeorm' import { LOG4JS_FEDERATION_CATEGORY_NAME } from '@/federation' import { LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME } from '@/federation/client/1_0' import { cleanDB, testEnvironment } from '@test/helpers' -import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' +import { getLogger } from 'config-schema/test/testSetup' import { validateCommunities } from './validateCommunities' @@ -204,7 +204,7 @@ describe('validate Communities', () => { overwrite: ['end_point', 'last_announced_at'], }) .execute() - await DbFederatedCommunity.update({}, { verifiedAt: null }) + await DbFederatedCommunity.update({ id: Not(0) }, { verifiedAt: null }) // jest.clearAllMocks() await validateCommunities() }) @@ -271,7 +271,7 @@ describe('validate Communities', () => { }) .execute() - await DbFederatedCommunity.update({}, { verifiedAt: null }) + await DbFederatedCommunity.update({ id: Not(0) }, { verifiedAt: null }) // jest.clearAllMocks() await validateCommunities() }) @@ -323,7 +323,7 @@ describe('validate Communities', () => { dbCom = await DbFederatedCommunity.findOneOrFail({ where: { publicKey: variables3.publicKey, apiVersion: variables3.apiVersion }, }) - await DbFederatedCommunity.update({}, { verifiedAt: null }) + await DbFederatedCommunity.update({ id: Not(0) }, { verifiedAt: null }) // jest.clearAllMocks() await validateCommunities() }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 197ff3e6c..f7688d1ca 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -103,7 +103,7 @@ import { getKlicktippState } from './util/getKlicktippState' import { deleteUserRole, setUserRole } from './util/modifyUserRole' import { sendUserToGms } from './util/sendUserToGms' import { syncHumhub } from './util/syncHumhub' -import { validateAlias } from './util/validateAlias' +import { validateAlias } from 'core' const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl'] const DEFAULT_LANGUAGE = 'de' diff --git a/backend/src/graphql/resolver/util/findUserByIdentifier.ts b/backend/src/graphql/resolver/util/findUserByIdentifier.ts index 1bdb186ce..ef8b91c2c 100644 --- a/backend/src/graphql/resolver/util/findUserByIdentifier.ts +++ b/backend/src/graphql/resolver/util/findUserByIdentifier.ts @@ -6,7 +6,7 @@ import { validate, version } from 'uuid' import { LogError } from '@/server/LogError' import { isEMail, isUUID4 } from '@/util/validate' -import { VALID_ALIAS_REGEX } from './validateAlias' +import { aliasSchema } from 'shared' /** * @@ -49,7 +49,7 @@ export const findUserByIdentifier = async ( } user = userContact.user user.emailContact = userContact - } else if (VALID_ALIAS_REGEX.exec(identifier)) { + } else if (aliasSchema.safeParse(identifier).success) { user = await DbUser.findOne({ where: { alias: identifier, community: communityWhere }, relations: ['emailContact', 'community'], diff --git a/backend/src/graphql/resolver/util/validateAlias.test.ts b/backend/src/graphql/resolver/util/validateAlias.test.ts deleted file mode 100644 index f10f54886..000000000 --- a/backend/src/graphql/resolver/util/validateAlias.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -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 } 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'] - query: ApolloServerTestClient['query'] - con: DataSource -} - -beforeAll(async () => { - testEnv = await testEnvironment(getLogger('apollo'), localization) - con = testEnv.con - await cleanDB() -}) - -afterAll(async () => { - await cleanDB() - await con.destroy() -}) - -describe('validate alias', () => { - beforeAll(() => { - jest.clearAllMocks() - }) - - describe('alias too short', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('Bi')).rejects.toEqual(new Error('Given alias is too short')) - expect(logger.error).toBeCalledWith('Given alias is too short', 'Bi') - }) - }) - - describe('alias too long', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('BibiBloxbergHexHexHex')).rejects.toEqual( - new Error('Given alias is too long'), - ) - expect(logger.error).toBeCalledWith('Given alias is too long', 'BibiBloxbergHexHexHex') - }) - }) - - describe('alias contains invalid characters', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('Bibi.Bloxberg')).rejects.toEqual( - new Error('Invalid characters in alias'), - ) - expect(logger.error).toBeCalledWith('Invalid characters in alias', 'Bibi.Bloxberg') - }) - }) - - describe('alias is a reserved word', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('admin')).rejects.toEqual(new Error('Alias is not allowed')) - expect(logger.error).toBeCalledWith('Alias is not allowed', 'admin') - }) - }) - - describe('alias is a reserved word with uppercase characters', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('Admin')).rejects.toEqual(new Error('Alias is not allowed')) - expect(logger.error).toBeCalledWith('Alias is not allowed', 'Admin') - }) - }) - - describe('hyphens and underscore', () => { - describe('alias starts with underscore', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('_bibi')).rejects.toEqual( - new Error('Invalid characters in alias'), - ) - expect(logger.error).toBeCalledWith('Invalid characters in alias', '_bibi') - }) - }) - - describe('alias contains two following hyphens', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('bi--bi')).rejects.toEqual( - new Error('Invalid characters in alias'), - ) - expect(logger.error).toBeCalledWith('Invalid characters in alias', 'bi--bi') - }) - }) - }) - - describe('test against existing alias in database', () => { - beforeAll(async () => { - const bibi = await userFactory(testEnv, bibiBloxberg) - const user = await User.findOne({ where: { id: bibi.id } }) - if (user) { - user.alias = 'b-b' - await user.save() - } - }) - - describe('alias exists in database', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('b-b')).rejects.toEqual(new Error('Alias already in use')) - expect(logger.error).toBeCalledWith('Alias already in use', 'b-b') - }) - }) - - describe('alias exists in database with in lower-case', () => { - it('throws and logs an error', async () => { - await expect(validateAlias('b-B')).rejects.toEqual(new Error('Alias already in use')) - expect(logger.error).toBeCalledWith('Alias already in use', 'b-B') - }) - }) - - describe('valid alias', () => { - it('resolves to true', async () => { - await expect(validateAlias('bibi')).resolves.toEqual(true) - }) - }) - }) -}) diff --git a/backend/src/graphql/resolver/util/validateAlias.ts b/backend/src/graphql/resolver/util/validateAlias.ts deleted file mode 100644 index 4eccad63f..000000000 --- a/backend/src/graphql/resolver/util/validateAlias.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { User as DbUser } from 'database' -import { Raw } from 'typeorm' - -import { LogError } from '@/server/LogError' - -export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/ - -const RESERVED_ALIAS = [ - 'admin', - 'email', - 'gast', - 'gdd', - 'gradido', - 'guest', - 'home', - 'root', - 'support', - 'temp', - 'tmp', - 'tmp', - 'user', - 'usr', - 'var', -] - -export const validateAlias = async (alias: string): Promise => { - if (alias.length < 3) { - throw new LogError('Given alias is too short', alias) - } - if (alias.length > 20) { - throw new LogError('Given alias is too long', alias) - } - if (!alias.match(VALID_ALIAS_REGEX)) { - throw new LogError('Invalid characters in alias', alias) - } - if (RESERVED_ALIAS.includes(alias.toLowerCase())) { - throw new LogError('Alias is not allowed', alias) - } - const aliasInUse = await DbUser.find({ - where: { alias: Raw((a) => `LOWER(${a}) = "${alias.toLowerCase()}"`) }, - }) - if (aliasInUse.length !== 0) { - throw new LogError('Alias already in use', alias) - } - return true -} diff --git a/backend/test/helpers.ts b/backend/test/helpers.ts index a966eccf6..c7f533931 100644 --- a/backend/test/helpers.ts +++ b/backend/test/helpers.ts @@ -23,7 +23,9 @@ const context = { export const cleanDB = async () => { // this only works as long we do not have foreign key constraints for (const entity of entities) { - await resetEntity(entity) + if (entity.name !== 'Migration') { + await resetEntity(entity) + } } } diff --git a/bun.lock b/bun.lock index 6e2eb4afe..dbf19bd65 100644 --- a/bun.lock +++ b/bun.lock @@ -112,16 +112,17 @@ "await-semaphore": "^0.1.3", "axios": "^0.21.1", "class-validator": "^0.13.1", - "config-schema": "*", + "config-schema": "workspace:*", + "core": "workspace:*", "cors": "^2.8.5", - "database": "*", + "database": "workspace:*", "decimal.js-light": "^2.5.1", "dotenv": "^10.0.0", "esbuild": "^0.25.2", "express": "^4.17.21", "express-slow-down": "^2.0.1", "faker": "^5.5.3", - "graphql": "^15.10.1", + "graphql": "15.10.1", "graphql-parse-resolve-info": "^4.13.1", "graphql-request": "5.0.0", "graphql-tag": "^2.12.6", @@ -144,13 +145,14 @@ "random-bigint": "^0.0.1", "reflect-metadata": "^0.1.13", "regenerator-runtime": "^0.14.1", + "shared": "workspace:*", "source-map-support": "^0.5.21", - "ts-jest": "27.0.5", + "ts-jest": "29.4.0", "ts-node": "^10.9.2", "tsconfig-paths": "^4.1.1", "type-graphql": "^1.1.1", "typed-rest-client": "^1.8.11", - "typeorm": "^0.3.22", + "typeorm": "^0.3.25", "typescript": "^4.9.5", "uuid": "^8.3.2", "workerpool": "^9.2.0", @@ -172,7 +174,22 @@ "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", "jest": "27.2.4", - "typescript": "^5.8.3", + "typescript": "^4.9.5", + }, + }, + "core": { + "name": "core", + "version": "1.0.0", + "dependencies": { + "database": "workspace:*", + "esbuild": "^0.25.2", + "log4js": "^6.9.1", + "zod": "^3.25.61", + }, + "devDependencies": { + "@biomejs/biome": "2.0.0", + "@types/node": "^17.0.21", + "typescript": "^4.9.5", }, }, "database": { @@ -189,10 +206,11 @@ "log4js": "^6.9.1", "mysql2": "^2.3.0", "reflect-metadata": "^0.1.13", + "shared": "workspace:*", "source-map-support": "^0.5.21", "ts-mysql-migrate": "^1.0.2", "tsx": "^4.20.3", - "typeorm": "^0.3.22", + "typeorm": "^0.3.25", "uuid": "^8.3.2", "wkx": "^0.5.0", }, @@ -234,8 +252,8 @@ "@types/joi": "^17.2.3", "@types/node": "^17.0.45", "@types/uuid": "^8.3.4", - "config-schema": "*", - "database": "*", + "config-schema": "workspace:*", + "database": "workspace:*", "dotenv": "10.0.0", "esbuild": "^0.25.3", "jest": "27.5.1", @@ -245,8 +263,9 @@ "prettier": "^2.8.8", "source-map-support": "^0.5.21", "ts-jest": "27.1.4", + "tsconfig-paths": "^4.1.1", "tsx": "^4.19.4", - "typeorm": "^0.3.22", + "typeorm": "^0.3.25", "typescript": "^4.9.5", "uuid": "^8.3.2", }, @@ -274,9 +293,9 @@ "apollo-server-testing": "2.25.2", "await-semaphore": "0.1.3", "class-validator": "^0.13.2", - "config-schema": "*", + "config-schema": "workspace:*", "cors": "2.8.5", - "database": "*", + "database": "workspace:*", "decimal.js-light": "^2.5.1", "dotenv": "10.0.0", "express": "^4.17.21", @@ -297,7 +316,7 @@ "ts-jest": "27.0.5", "tsconfig-paths": "^4.1.1", "type-graphql": "^1.1.1", - "typeorm": "^0.3.22", + "typeorm": "^0.3.25", "typescript": "^4.9.5", "uuid": "8.3.2", }, @@ -401,7 +420,6 @@ "name": "shared", "version": "1.0.0", "dependencies": { - "database": "*", "esbuild": "^0.25.2", "log4js": "^6.9.1", "zod": "^3.25.61", @@ -409,7 +427,7 @@ "devDependencies": { "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", - "typescript": "^5.8.3", + "typescript": "^4.9.5", }, }, }, @@ -924,29 +942,29 @@ "@swc-node/sourcemap-support": ["@swc-node/sourcemap-support@0.5.1", "", { "dependencies": { "source-map-support": "^0.5.21", "tslib": "^2.6.3" } }, "sha512-JxIvIo/Hrpv0JCHSyRpetAdQ6lB27oFYhv0PKCNf1g2gUXOjpeR1exrXccRxLMuAV5WAmGFBwRnNOJqN38+qtg=="], - "@swc/cli": ["@swc/cli@0.7.3", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-rnVXNnlURjdOuPaBIwZ3TmBA44BF/eP0j154LanlgPEYfau74ige7cpKlKkZr1IBqMOG99lAnYNxQipDWA3hdg=="], + "@swc/cli": ["@swc/cli@0.7.7", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-j4yYm9bx3pxWofaJKX1BFwj/3ngUDynN4UIQ2Xd2h0h/7Gt7zkReBTpDN7g5S13mgAYxacaTHTOUsz18097E8w=="], - "@swc/core": ["@swc/core@1.11.24", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.21" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.11.24", "@swc/core-darwin-x64": "1.11.24", "@swc/core-linux-arm-gnueabihf": "1.11.24", "@swc/core-linux-arm64-gnu": "1.11.24", "@swc/core-linux-arm64-musl": "1.11.24", "@swc/core-linux-x64-gnu": "1.11.24", "@swc/core-linux-x64-musl": "1.11.24", "@swc/core-win32-arm64-msvc": "1.11.24", "@swc/core-win32-ia32-msvc": "1.11.24", "@swc/core-win32-x64-msvc": "1.11.24" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg=="], + "@swc/core": ["@swc/core@1.12.4", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.4", "@swc/core-darwin-x64": "1.12.4", "@swc/core-linux-arm-gnueabihf": "1.12.4", "@swc/core-linux-arm64-gnu": "1.12.4", "@swc/core-linux-arm64-musl": "1.12.4", "@swc/core-linux-x64-gnu": "1.12.4", "@swc/core-linux-x64-musl": "1.12.4", "@swc/core-win32-arm64-msvc": "1.12.4", "@swc/core-win32-ia32-msvc": "1.12.4", "@swc/core-win32-x64-msvc": "1.12.4" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-hn30ebV4njAn0NAUM+3a0qCF+MJgqTNSrfA/hUAbC6TVjOQy2OYGQwkUvCu/V7S2+rZxrUsTpKOnZ7qqECZV9Q=="], - "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.11.24", "", { "os": "darwin", "cpu": "arm64" }, "sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA=="], + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HihKfeitjZU2ab94Zf893sxzFryLKX0TweGsNXXOLNtkSMLw50auuYfpRM0BOL9/uXXtuCWgRIF6P030SAX5xQ=="], - "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.11.24", "", { "os": "darwin", "cpu": "x64" }, "sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ=="], + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-meYCXHyYb6RDdu2N5PNAf0EelyxPBFhRcVo4kBFLuvuNb0m6EUg///VWy8MUMXq9/s9uzGS9kJVXXdRdr/d6FA=="], - "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.11.24", "", { "os": "linux", "cpu": "arm" }, "sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw=="], + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.4", "", { "os": "linux", "cpu": "arm" }, "sha512-szfDbf7mE8V64of0q/LSqbk+em+T+TD3uqnH40Z7Qu/aL8vi5CHgyLjWG2SLkLLpyjgkAUF6AKrupgnBYcC2NA=="], - "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.11.24", "", { "os": "linux", "cpu": "arm64" }, "sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg=="], + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-n0IY76w+Scx8m3HIVRvLkoResuwsQgjDfAk9bxn99dq4leQO+mE0fkPl0Yw/1BIsPh+kxGfopIJH9zsZ1Z2YrA=="], - "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.11.24", "", { "os": "linux", "cpu": "arm64" }, "sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw=="], + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-wE5jmFi5cEQyLy8WmCWmNwfKETrnzy2D8YNi/xpYWpLPWqPhcelpa6tswkfYlbsMmmOh7hQNoTba1QdGu0jvHQ=="], - "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.11.24", "", { "os": "linux", "cpu": "x64" }, "sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg=="], + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6S50Xd/7ePjEwrXyHMxpKTZ+KBrgUwMA8hQPbArUOwH4S5vHBr51heL0iXbUkppn1bkSr0J0IbOove5hzn+iqQ=="], - "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.11.24", "", { "os": "linux", "cpu": "x64" }, "sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw=="], + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-hbYRyaHhC13vYKuGG5BrAG5fjjWEQFfQetuFp/4QKEoXDzdnabJoixxWTQACDL3m0JW32nJ+gUzsYIPtFYkwXg=="], - "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.11.24", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ=="], + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-e6EbfjPL8GA/bb1lc9Omtxjlz+1ThTsAuBsy4Q3Kpbuh6B3jclg8KzxU/6t91v23wG593mieTyR5f3Pr7X3AWw=="], - "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.11.24", "", { "os": "win32", "cpu": "ia32" }, "sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ=="], + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-RG2FzmllBTUf4EksANlIvLckcBrLZEA0t13LIa6L213UZKQfEuDNHezqESgoVhJMg2S/tWauitATOCFgZNSmjg=="], - "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.11.24", "", { "os": "win32", "cpu": "x64" }, "sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w=="], + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.4", "", { "os": "win32", "cpu": "x64" }, "sha512-oRHKnZlR83zaMeVUCmHENa4j5uNRAWbmEpjYbzRcfC45LPFNWKGWGAGERLx0u87XMUtTGqnVYxnBTHN/rzDHOw=="], "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], @@ -954,7 +972,7 @@ "@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=="], + "@swc/types": ["@swc/types@0.1.23", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw=="], "@szmarczak/http-timer": ["@szmarczak/http-timer@5.0.1", "", { "dependencies": { "defer-to-connect": "^2.0.1" } }, "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw=="], @@ -1570,6 +1588,8 @@ "copy-anything": ["copy-anything@3.0.5", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="], + "core": ["core@workspace:core"], + "core-js-pure": ["core-js-pure@3.42.0", "", {}, "sha512-007bM04u91fF4kMgwom2I5cQxAFIy8jVulgr9eozILl/SZE53QOqnW/+vviC+wQWLv+AunBG+8Q0TLoeSsSxRQ=="], "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], @@ -1634,7 +1654,7 @@ "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], - "dedent": ["dedent@0.7.0", "", {}, "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA=="], + "dedent": ["dedent@1.6.0", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA=="], "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], @@ -3126,7 +3146,7 @@ "ts-invariant": ["ts-invariant@0.10.3", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ=="], - "ts-jest": ["ts-jest@27.0.5", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w=="], + "ts-jest": ["ts-jest@29.4.0", "", { "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", "semver": "^7.7.2", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@jest/transform": "^29.0.0 || ^30.0.0", "@jest/types": "^29.0.0 || ^30.0.0", "babel-jest": "^29.0.0 || ^30.0.0", "jest": "^29.0.0 || ^30.0.0", "jest-util": "^29.0.0 || ^30.0.0", "typescript": ">=4.3 <6" }, "optionalPeers": ["@babel/core", "@jest/transform", "@jest/types", "babel-jest", "jest-util"], "bin": { "ts-jest": "cli.js" } }, "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q=="], "ts-mysql-migrate": ["ts-mysql-migrate@1.1.2", "", { "dependencies": { "@types/mysql": "^2.15.8", "mysql": "^2.18.1" }, "bin": { "generate-migration": "dist/generate-migration.js" } }, "sha512-jwhVaMrYBNNhAoZ5XISxzqbnXTHqwazqu/r1UQ6kUaGNPGL43ZFnBiXVj4Gm3pfe3xtCGIaNInehDfdDuigPgw=="], @@ -3178,7 +3198,7 @@ "typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="], - "typeorm": ["typeorm@0.3.22", "", { "dependencies": { "@sqltools/formatter": "^1.2.5", "ansis": "^3.17.0", "app-root-path": "^3.1.0", "buffer": "^6.0.3", "dayjs": "^1.11.13", "debug": "^4.4.0", "dotenv": "^16.4.7", "glob": "^10.4.5", "sha.js": "^2.4.11", "sql-highlight": "^6.0.0", "tslib": "^2.8.1", "uuid": "^11.1.0", "yargs": "^17.7.2" }, "peerDependencies": { "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0", "@sap/hana-client": "^2.12.25", "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", "hdb-pool": "^0.1.6", "ioredis": "^5.0.4", "mongodb": "^5.8.0 || ^6.0.0", "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", "mysql2": "^2.2.5 || ^3.0.1", "oracledb": "^6.3.0", "pg": "^8.5.1", "pg-native": "^3.0.0", "pg-query-stream": "^4.0.0", "redis": "^3.1.1 || ^4.0.0", "reflect-metadata": "^0.1.14 || ^0.2.0", "sql.js": "^1.4.0", "sqlite3": "^5.0.3", "ts-node": "^10.7.0", "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" }, "optionalPeers": ["@google-cloud/spanner", "@sap/hana-client", "better-sqlite3", "hdb-pool", "ioredis", "mongodb", "mssql", "mysql2", "oracledb", "pg", "pg-native", "pg-query-stream", "redis", "sql.js", "sqlite3", "ts-node", "typeorm-aurora-data-api-driver"], "bin": { "typeorm": "cli.js", "typeorm-ts-node-esm": "cli-ts-node-esm.js", "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js" } }, "sha512-P/Tsz3UpJ9+K0oryC0twK5PO27zejLYYwMsE8SISfZc1lVHX+ajigiOyWsKbuXpEFMjD9z7UjLzY3+ElVOMMDA=="], + "typeorm": ["typeorm@0.3.25", "", { "dependencies": { "@sqltools/formatter": "^1.2.5", "ansis": "^3.17.0", "app-root-path": "^3.1.0", "buffer": "^6.0.3", "dayjs": "^1.11.13", "debug": "^4.4.0", "dedent": "^1.6.0", "dotenv": "^16.4.7", "glob": "^10.4.5", "sha.js": "^2.4.11", "sql-highlight": "^6.0.0", "tslib": "^2.8.1", "uuid": "^11.1.0", "yargs": "^17.7.2" }, "peerDependencies": { "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0", "@sap/hana-client": "^2.12.25", "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", "hdb-pool": "^0.1.6", "ioredis": "^5.0.4", "mongodb": "^5.8.0 || ^6.0.0", "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", "mysql2": "^2.2.5 || ^3.0.1", "oracledb": "^6.3.0", "pg": "^8.5.1", "pg-native": "^3.0.0", "pg-query-stream": "^4.0.0", "redis": "^3.1.1 || ^4.0.0", "reflect-metadata": "^0.1.14 || ^0.2.0", "sql.js": "^1.4.0", "sqlite3": "^5.0.3", "ts-node": "^10.7.0", "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" }, "optionalPeers": ["@google-cloud/spanner", "@sap/hana-client", "better-sqlite3", "hdb-pool", "ioredis", "mongodb", "mssql", "mysql2", "oracledb", "pg", "pg-native", "pg-query-stream", "redis", "sql.js", "sqlite3", "ts-node", "typeorm-aurora-data-api-driver"], "bin": { "typeorm": "cli.js", "typeorm-ts-node-esm": "cli-ts-node-esm.js", "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js" } }, "sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg=="], "typescript": ["typescript@4.9.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="], @@ -3376,7 +3396,7 @@ "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - "yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], "yauzl": ["yauzl@3.2.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "pend": "~1.2.0" } }, "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w=="], @@ -3652,20 +3672,16 @@ "concurrently/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], - "config-schema/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], - "css-select/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="], "css-select/domutils": ["domutils@2.8.0", "", { "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.2.0" } }, "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="], "cssstyle/rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], - "database/@swc/cli": ["@swc/cli@0.7.7", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^4.0.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-j4yYm9bx3pxWofaJKX1BFwj/3ngUDynN4UIQ2Xd2h0h/7Gt7zkReBTpDN7g5S13mgAYxacaTHTOUsz18097E8w=="], - - "database/@swc/core": ["@swc/core@1.12.4", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.4", "@swc/core-darwin-x64": "1.12.4", "@swc/core-linux-arm-gnueabihf": "1.12.4", "@swc/core-linux-arm64-gnu": "1.12.4", "@swc/core-linux-arm64-musl": "1.12.4", "@swc/core-linux-x64-gnu": "1.12.4", "@swc/core-linux-x64-musl": "1.12.4", "@swc/core-win32-arm64-msvc": "1.12.4", "@swc/core-win32-ia32-msvc": "1.12.4", "@swc/core-win32-x64-msvc": "1.12.4" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-hn30ebV4njAn0NAUM+3a0qCF+MJgqTNSrfA/hUAbC6TVjOQy2OYGQwkUvCu/V7S2+rZxrUsTpKOnZ7qqECZV9Q=="], - "database/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="], + "database/ts-jest": ["ts-jest@27.0.5", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w=="], + "database/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], "decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], @@ -3678,8 +3694,6 @@ "dht-node/ts-jest": ["ts-jest@27.1.4", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash.memoize": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ=="], - "dht-node/tsx": ["tsx@4.19.4", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q=="], - "dht-rpc/sodium-universal": ["sodium-universal@5.0.1", "", { "dependencies": { "sodium-native": "^5.0.1" }, "peerDependencies": { "sodium-javascript": "~0.8.0" }, "optionalPeers": ["sodium-javascript"] }, "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q=="], "domexception/webidl-conversions": ["webidl-conversions@5.0.0", "", {}, "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA=="], @@ -3722,6 +3736,8 @@ "federation/helmet": ["helmet@7.2.0", "", {}, "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw=="], + "federation/ts-jest": ["ts-jest@27.0.5", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w=="], + "file-type/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], @@ -3766,6 +3782,8 @@ "jest-circus/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="], + "jest-circus/dedent": ["dedent@0.7.0", "", {}, "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA=="], + "jest-cli/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], "jest-environment-jsdom/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="], @@ -3880,8 +3898,6 @@ "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], - "shared/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], - "simple-update-notifier/semver": ["semver@7.0.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A=="], "sodium-secretstream/sodium-universal": ["sodium-universal@5.0.1", "", { "dependencies": { "sodium-native": "^5.0.1" }, "peerDependencies": { "sodium-javascript": "~0.8.0" }, "optionalPeers": ["sodium-javascript"] }, "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q=="], @@ -3934,8 +3950,16 @@ "test-exclude/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "ts-jest/jest": ["jest@27.5.1", "", { "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", "jest-cli": "^27.5.1" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "optionalPeers": ["node-notifier"], "bin": { "jest": "bin/jest.js" } }, "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ=="], + + "ts-jest/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "ts-jest/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "typed-rest-client/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + "typeorm/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + "typeorm/dotenv": ["dotenv@16.5.0", "", {}, "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg=="], "typeorm/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], @@ -4000,8 +4024,6 @@ "xss/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - "yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], - "@apollographql/graphql-upload-8-fork/http-errors/depd": ["depd@1.1.2", "", {}, "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="], "@apollographql/graphql-upload-8-fork/http-errors/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="], @@ -4086,31 +4108,7 @@ "css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], - "database/@swc/cli/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], - - "database/@swc/cli/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - - "database/@swc/core/@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HihKfeitjZU2ab94Zf893sxzFryLKX0TweGsNXXOLNtkSMLw50auuYfpRM0BOL9/uXXtuCWgRIF6P030SAX5xQ=="], - - "database/@swc/core/@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-meYCXHyYb6RDdu2N5PNAf0EelyxPBFhRcVo4kBFLuvuNb0m6EUg///VWy8MUMXq9/s9uzGS9kJVXXdRdr/d6FA=="], - - "database/@swc/core/@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.4", "", { "os": "linux", "cpu": "arm" }, "sha512-szfDbf7mE8V64of0q/LSqbk+em+T+TD3uqnH40Z7Qu/aL8vi5CHgyLjWG2SLkLLpyjgkAUF6AKrupgnBYcC2NA=="], - - "database/@swc/core/@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-n0IY76w+Scx8m3HIVRvLkoResuwsQgjDfAk9bxn99dq4leQO+mE0fkPl0Yw/1BIsPh+kxGfopIJH9zsZ1Z2YrA=="], - - "database/@swc/core/@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-wE5jmFi5cEQyLy8WmCWmNwfKETrnzy2D8YNi/xpYWpLPWqPhcelpa6tswkfYlbsMmmOh7hQNoTba1QdGu0jvHQ=="], - - "database/@swc/core/@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6S50Xd/7ePjEwrXyHMxpKTZ+KBrgUwMA8hQPbArUOwH4S5vHBr51heL0iXbUkppn1bkSr0J0IbOove5hzn+iqQ=="], - - "database/@swc/core/@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.4", "", { "os": "linux", "cpu": "x64" }, "sha512-hbYRyaHhC13vYKuGG5BrAG5fjjWEQFfQetuFp/4QKEoXDzdnabJoixxWTQACDL3m0JW32nJ+gUzsYIPtFYkwXg=="], - - "database/@swc/core/@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-e6EbfjPL8GA/bb1lc9Omtxjlz+1ThTsAuBsy4Q3Kpbuh6B3jclg8KzxU/6t91v23wG593mieTyR5f3Pr7X3AWw=="], - - "database/@swc/core/@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-RG2FzmllBTUf4EksANlIvLckcBrLZEA0t13LIa6L213UZKQfEuDNHezqESgoVhJMg2S/tWauitATOCFgZNSmjg=="], - - "database/@swc/core/@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.4", "", { "os": "win32", "cpu": "x64" }, "sha512-oRHKnZlR83zaMeVUCmHENa4j5uNRAWbmEpjYbzRcfC45LPFNWKGWGAGERLx0u87XMUtTGqnVYxnBTHN/rzDHOw=="], - - "database/@swc/core/@swc/types": ["@swc/types@0.1.23", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw=="], + "database/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], "database/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], @@ -4136,6 +4134,8 @@ "database/vitest/vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + "dht-node/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + "dht-rpc/sodium-universal/sodium-native": ["sodium-native@5.0.1", "", { "dependencies": { "require-addon": "^1.1.0", "which-runtime": "^1.2.1" } }, "sha512-Q305aUXc0OzK7VVRvWkeEQJQIHs6slhFwWpyqLB5iJqhpyt2lYIVu96Y6PQ7TABIlWXVF3YiWDU3xS2Snkus+g=="], "editorconfig/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], @@ -4146,6 +4146,8 @@ "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "federation/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + "file-type/get-stream/is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], @@ -4164,6 +4166,8 @@ "jest-cli/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], + "jest-cli/yargs/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + "jest-environment-jsdom/jsdom/cssstyle": ["cssstyle@2.3.0", "", { "dependencies": { "cssom": "~0.3.6" } }, "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A=="], "jest-environment-jsdom/jsdom/data-urls": ["data-urls@2.0.0", "", { "dependencies": { "abab": "^2.0.3", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.0.0" } }, "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ=="], @@ -4362,8 +4366,6 @@ "css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], - "database/@swc/cli/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - "database/vitest/@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], "database/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], @@ -4436,8 +4438,6 @@ "chokidar-cli/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@4.1.1", "", {}, "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="], - "database/@swc/cli/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "js-beautify/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], diff --git a/config-schema/package.json b/config-schema/package.json index 17afc7977..c3a6a2b1d 100644 --- a/config-schema/package.json +++ b/config-schema/package.json @@ -1,6 +1,6 @@ { "name": "config-schema", - "version": "1.0.0", + "version": "2.6.0", "description": "Gradido Config for validate config", "main": "./build/index.js", "types": "./src/index.ts", @@ -27,7 +27,7 @@ "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", "jest": "27.2.4", - "typescript": "^5.8.3" + "typescript": "^4.9.5" }, "dependencies": { "esbuild": "^0.25.2", diff --git a/shared/bunfig.toml b/core/bunfig.toml similarity index 100% rename from shared/bunfig.toml rename to core/bunfig.toml diff --git a/core/package.json b/core/package.json new file mode 100644 index 000000000..26ed4e13b --- /dev/null +++ b/core/package.json @@ -0,0 +1,40 @@ +{ + "name": "core", + "version": "2.6.0", + "description": "Gradido Core Code, High-Level Shared Code, with dependencies onto other modules", + "main": "./build/index.js", + "types": "./src/index.ts", + "exports": { + ".": { + "import": "./build/index.js", + "require": "./build/index.js" + } + }, + "repository": "https://github.com/gradido/gradido/core", + "author": "Gradido Academy - https://www.gradido.net", + "license": "Apache-2.0", + "private": true, + "scripts": { + "build": "esbuild src/index.ts --outdir=build --platform=node --target=node18.20.7 --bundle --packages=external", + "build:bun": "bun build src/index.ts --outdir=build --target=bun --packages=external", + "test": "bun test", + "test:debug": "bun test --inspect-brk", + "typecheck": "tsc --noEmit", + "lint": "biome check --error-on-warnings .", + "lint:fix": "biome check --error-on-warnings . --write" + }, + "devDependencies": { + "@biomejs/biome": "2.0.0", + "@types/node": "^17.0.21", + "typescript": "^4.9.5" + }, + "dependencies": { + "database": "*", + "esbuild": "^0.25.2", + "log4js": "^6.9.1", + "zod": "^3.25.61" + }, + "engines": { + "node": ">=18" + } +} diff --git a/shared/src/config/const.ts b/core/src/config/const.ts similarity index 100% rename from shared/src/config/const.ts rename to core/src/config/const.ts diff --git a/core/src/index.ts b/core/src/index.ts new file mode 100644 index 000000000..0cd6afc7d --- /dev/null +++ b/core/src/index.ts @@ -0,0 +1 @@ +export * from './schema' \ No newline at end of file diff --git a/core/src/schema/index.ts b/core/src/schema/index.ts new file mode 100644 index 000000000..6c1cda831 --- /dev/null +++ b/core/src/schema/index.ts @@ -0,0 +1,4 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' + +export const LOG_CATEGORY_SCHEMA_ALIAS = `${LOG4JS_BASE_CATEGORY_NAME}.schema` +export * from './user.schema' \ No newline at end of file diff --git a/core/src/schema/user.schema.test.ts b/core/src/schema/user.schema.test.ts new file mode 100644 index 000000000..e8faf5abe --- /dev/null +++ b/core/src/schema/user.schema.test.ts @@ -0,0 +1,68 @@ +import { validateAlias } from './user.schema' +import { getLogger } from '../../../config-schema/test/testSetup.bun' +import { LOG_CATEGORY_SCHEMA_ALIAS } from '.' +import { describe, it, expect, beforeEach, mock, jest } from 'bun:test' +import { aliasExists } from 'database' + +const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`) + +mock.module('database', () => ({ + aliasExists: jest.fn(), +})) +mock.module('shared/src/schema/user.schema', () => ({ + aliasSchema: { + parse: jest.fn(), + }, +})) + +describe('validate alias', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + describe('zod throw an validation error', () => { + it('throws and logs an error', () => { + expect(validateAlias('Bi')).rejects.toThrowError(new Error('Given alias is too short')) + expect(logger.warn.mock.calls[0]).toEqual([ + 'invalid alias', + 'Bi', + expect.arrayContaining([ + // error vor zod v4 + /*expect.objectContaining({ + code: 'too_small', + minimum: 3, + origin: 'string', + message: 'Given alias is too short', + }),*/ + expect.objectContaining({ + code: 'too_small', + exact: false, + inclusive: true, + minimum: 3, + type: 'string', + message: 'Given alias is too short', + path: [], + }), + ]), + ]) + }) + }) + + + describe('test against existing alias in database', () => { + describe('alias exists in database', () => { + it('throws and logs an error', () => { + (aliasExists as jest.Mock).mockResolvedValue(true) + expect(validateAlias('b-b')).rejects.toEqual(new Error('Given alias is already in use')) + expect(logger.warn.mock.calls[0]).toEqual(['alias already in use', 'b-b']) + }) + }) + + describe('valid alias', () => { + it('resolves to true', async () => { + (aliasExists as jest.Mock).mockResolvedValue(false) + expect(validateAlias('bibi')).resolves.toEqual(true) + }) + }) + }) +}) diff --git a/core/src/schema/user.schema.ts b/core/src/schema/user.schema.ts new file mode 100644 index 000000000..85d3774d0 --- /dev/null +++ b/core/src/schema/user.schema.ts @@ -0,0 +1,27 @@ +import { ZodError } from 'zod' +import { getLogger } from 'log4js' +import { LOG_CATEGORY_SCHEMA_ALIAS } from '.' +import { aliasExists } from 'database' +import { aliasSchema } from 'shared' + +const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`) + +export async function validateAlias(alias: string): Promise { + try { + aliasSchema.parse(alias) + } catch (err) { + if (err instanceof ZodError || (err as Error).name === 'ZodError') { + // throw only first error, but log all errors + logger.warn('invalid alias', alias, (err as ZodError).issues) + throw new Error((err as ZodError).issues[0].message) + } + throw err + } + + if (await aliasExists(alias)) { + logger.warn('alias already in use', alias) + throw new Error('Given alias is already in use') + } + + return true +} diff --git a/core/tsconfig.json b/core/tsconfig.json new file mode 100644 index 000000000..75b686340 --- /dev/null +++ b/core/tsconfig.json @@ -0,0 +1,73 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "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. */ + // "outFile": "./build/outfile.js", /* Concatenate and emit output to single file. */ + "outDir": "./build", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [".", "../database"], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": ["bun-types"], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "references": [], /* Any project that is referenced must itself have a `references` array (which may be empty). */ + "exclude": ["**/*.test.ts", "**/*.spec.ts", "test/*", "**/bun.d.ts"], +} diff --git a/core/turbo.json b/core/turbo.json new file mode 100644 index 000000000..9f85ac803 --- /dev/null +++ b/core/turbo.json @@ -0,0 +1,24 @@ +{ + "extends": ["//"], + "tasks": { + "lint": { + }, + "lint:fix": { + }, + "test": { + "dependsOn": ["config-schema#build"] + }, + "typecheck": { + }, + "dev": { + "dependsOn": ["database#build", "config-schema#build"], + "persistent": true, + "cache": false + }, + "build": { + "dependsOn": ["^build"], + "outputs": ["build/**"], + "cache": true + } + } +} \ No newline at end of file diff --git a/database/package.json b/database/package.json index e62875f4a..cd1ad5f87 100644 --- a/database/package.json +++ b/database/package.json @@ -13,7 +13,7 @@ "repository": "https://github.com/gradido/gradido/database", "author": "Gradido Academy - https://www.gradido.net", "license": "Apache-2.0", - "private": false, + "private": true, "scripts": { "build": "tsx ./esbuild.config.ts", "typecheck": "tsc --noEmit", @@ -58,10 +58,11 @@ "log4js": "^6.9.1", "mysql2": "^2.3.0", "reflect-metadata": "^0.1.13", + "shared": "*", "source-map-support": "^0.5.21", "ts-mysql-migrate": "^1.0.2", "tsx": "^4.20.3", - "typeorm": "^0.3.22", + "typeorm": "^0.3.25", "uuid": "^8.3.2", "wkx": "^0.5.0" }, diff --git a/database/src/AppDatabase.ts b/database/src/AppDatabase.ts index f10c095bb..f995a176a 100644 --- a/database/src/AppDatabase.ts +++ b/database/src/AppDatabase.ts @@ -4,7 +4,7 @@ import { Migration, entities } from './entity' import { getLogger } from 'log4js' import { latestDbVersion } from '.' import { CONFIG } from './config' -import { LOG4JS_BASE_CATEGORY_NAME, MIGRATIONS_TABLE } from './config/const' +import { LOG4JS_BASE_CATEGORY_NAME } from './config/const' const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.AppDatabase`) diff --git a/database/src/seeds/factory/user.ts b/database/src/seeds/factory/user.ts index fbca53490..412d0088a 100644 --- a/database/src/seeds/factory/user.ts +++ b/database/src/seeds/factory/user.ts @@ -2,12 +2,13 @@ import { UserInterface } from '../users/UserInterface' import { User, UserContact } from '../../entity' import { generateRandomNumber, generateRandomNumericString } from '../utils' import { v4 } from 'uuid' +import { UserContactType, OptInType, PasswordEncryptionType } from 'shared' export const userFactory = async (user: UserInterface): Promise => { let dbUserContact = new UserContact() dbUserContact.email = user.email ?? '' - dbUserContact.type = 'email' //UserContactType.USER_CONTACT_EMAIL + dbUserContact.type = UserContactType.USER_CONTACT_EMAIL let dbUser = new User() dbUser.firstName = user.firstName ?? '' @@ -21,11 +22,10 @@ export const userFactory = async (user: UserInterface): Promise => { if (user.emailChecked) { dbUserContact.emailVerificationCode = generateRandomNumericString(64) - dbUserContact.emailOptInTypeId = 1 //OptInType.EMAIL_OPT_IN_REGISTER + dbUserContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_REGISTER dbUserContact.emailChecked = true dbUser.password = generateRandomNumber() - // TODO: think where to put enums - dbUser.passwordEncryptionType = 2 //PasswordEncryptionType.GRADIDO_ID + dbUser.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID } // TODO: improve with cascade dbUser = await dbUser.save() diff --git a/dht-node/package.json b/dht-node/package.json index a973f7087..8a5cd4462 100644 --- a/dht-node/package.json +++ b/dht-node/package.json @@ -47,8 +47,9 @@ "prettier": "^2.8.8", "source-map-support": "^0.5.21", "ts-jest": "27.1.4", + "tsconfig-paths": "^4.1.1", "tsx": "^4.19.4", - "typeorm": "^0.3.22", + "typeorm": "^0.3.25", "typescript": "^4.9.5", "uuid": "^8.3.2" }, diff --git a/dht-node/test/helpers.ts b/dht-node/test/helpers.ts index 52b847b4c..5978f7bb9 100644 --- a/dht-node/test/helpers.ts +++ b/dht-node/test/helpers.ts @@ -16,7 +16,9 @@ const context = { export const cleanDB = async () => { // this only works as long we do not have foreign key constraints for (const entity of entities) { - await resetEntity(entity) + if (entity.name !== 'Migration') { + await resetEntity(entity) + } } } diff --git a/dht-node/tsconfig.json b/dht-node/tsconfig.json index d87ceed66..9628b7c41 100644 --- a/dht-node/tsconfig.json +++ b/dht-node/tsconfig.json @@ -54,7 +54,7 @@ "typeRoots": [ /* List of folders to include type definitions from. */ "src/dht_node/@types", "node_modules/@types", - // "../node_modules/@types" + "../node_modules/@types" ], // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ diff --git a/federation/package.json b/federation/package.json index 3e15b8e14..f2bae7d73 100644 --- a/federation/package.json +++ b/federation/package.json @@ -63,7 +63,7 @@ "ts-jest": "27.0.5", "tsconfig-paths": "^4.1.1", "type-graphql": "^1.1.1", - "typeorm": "^0.3.22", + "typeorm": "^0.3.25", "typescript": "^4.9.5", "uuid": "8.3.2" }, diff --git a/federation/test/helpers.ts b/federation/test/helpers.ts index c8523fe7e..7c6280a44 100644 --- a/federation/test/helpers.ts +++ b/federation/test/helpers.ts @@ -21,7 +21,9 @@ const context = { export const cleanDB = async () => { // this only works as long we do not have foreign key constraints for (const entity of entities) { - await resetEntity(entity) + if (entity.name !== 'Migration') { + await resetEntity(entity) + } } } diff --git a/frontend/package.json b/frontend/package.json index 7e122a237..4dbace5c1 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -81,7 +81,7 @@ "@vue/test-utils": "^2.4.6", "chokidar-cli": "^3.0.0", "concurrently": "^9.1.2", - "config-schema": "*", + "config-schema": "2.6.0", "cross-env": "^7.0.3", "dotenv-webpack": "^7.0.3", "eslint": "8.57.1", diff --git a/package.json b/package.json index 5f9d212c7..350091046 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "admin", "backend", "config-schema", + "core", "database", "dht-node", "federation", diff --git a/scripts/release.sh b/scripts/release.sh index caa8d517c..45221398d 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -6,6 +6,9 @@ PROJECT_DIR="${SCRIPT_DIR}/../" FRONTEND_DIR="${PROJECT_DIR}/frontend/" BACKEND_DIR="${PROJECT_DIR}/backend/" DATABASE_DIR="${PROJECT_DIR}/database/" +SHARED_DIR="${PROJECT_DIR}/shared/" +CONFIG_SCHEMA_DIR="${PROJECT_DIR}/config-schema/" +CORE_DIR="${PROJECT_DIR}/core/" ADMIN_DIR="${PROJECT_DIR}/admin/" DHTNODE_DIR="${PROJECT_DIR}/dht-node/" FEDERATION_DIR="${PROJECT_DIR}/federation/" @@ -27,6 +30,12 @@ cd ${BACKEND_DIR} yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION} cd ${DATABASE_DIR} yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION} +cd ${SHARED_DIR} +yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION} +cd ${CONFIG_SCHEMA_DIR} +yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION} +cd ${CORE_DIR} +yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION} cd ${ADMIN_DIR} yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION} cd ${DHTNODE_DIR} diff --git a/shared/package.json b/shared/package.json index 279c27ee9..5c8ba7195 100644 --- a/shared/package.json +++ b/shared/package.json @@ -1,7 +1,7 @@ { "name": "shared", - "version": "1.0.0", - "description": "Gradido Shared Code", + "version": "2.6.0", + "description": "Gradido Shared Code, Low-Level Shared Code, without dependencies onto other modules", "main": "./build/index.js", "types": "./src/index.ts", "exports": { @@ -26,10 +26,9 @@ "devDependencies": { "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", - "typescript": "^5.8.3" + "typescript": "^4.9.5" }, "dependencies": { - "database": "*", "esbuild": "^0.25.2", "log4js": "^6.9.1", "zod": "^3.25.61" diff --git a/shared/src/enum/OptInType.ts b/shared/src/enum/OptInType.ts new file mode 100644 index 000000000..1f6300b39 --- /dev/null +++ b/shared/src/enum/OptInType.ts @@ -0,0 +1,8 @@ +// not compatible with typescript 4 +// import { enum as zEnum } from 'zod/v4-mini' +export enum OptInType { + EMAIL_OPT_IN_REGISTER = 1, + EMAIL_OPT_IN_RESET_PASSWORD = 2, +} + +// export const OptInTypeSchema = zEnum(OptInType) \ No newline at end of file diff --git a/shared/src/enum/PasswordEncryptionType.ts b/shared/src/enum/PasswordEncryptionType.ts new file mode 100644 index 000000000..13b72be0c --- /dev/null +++ b/shared/src/enum/PasswordEncryptionType.ts @@ -0,0 +1,11 @@ +// not compatible with typescript 4 +// import { enum as zEnum } from 'zod/v4-mini' + +export enum PasswordEncryptionType { + NO_PASSWORD = 0, + EMAIL = 1, + GRADIDO_ID = 2, +} + +// export const PasswordEncryptionTypeSchema = zEnum(PasswordEncryptionType) + diff --git a/shared/src/enum/RoleNames.ts b/shared/src/enum/RoleNames.ts new file mode 100644 index 000000000..fec1f47a1 --- /dev/null +++ b/shared/src/enum/RoleNames.ts @@ -0,0 +1,13 @@ +// not compatible with typescript 4 +// import { enum as zEnum } from 'zod/v4-mini' + +export enum RoleNames { + UNAUTHORIZED = 'UNAUTHORIZED', + USER = 'USER', + MODERATOR = 'MODERATOR', + MODERATOR_AI = 'MODERATOR_AI', + ADMIN = 'ADMIN', + DLT_CONNECTOR = 'DLT_CONNECTOR_ROLE', +} + +// export const RoleNamesSchema = zEnum(RoleNames) diff --git a/shared/src/enum/UserContactType.ts b/shared/src/enum/UserContactType.ts new file mode 100644 index 000000000..c6f1ef03b --- /dev/null +++ b/shared/src/enum/UserContactType.ts @@ -0,0 +1,9 @@ +// not compatible with typescript 4 +// import { enum as zEnum } from 'zod/v4-mini' + +export enum UserContactType { + USER_CONTACT_EMAIL = 'EMAIL', + USER_CONTACT_PHONE = 'PHONE', +} + +// export const UserContactTypeSchema = zEnum(UserContactType) diff --git a/shared/src/enum/index.ts b/shared/src/enum/index.ts new file mode 100644 index 000000000..efd722624 --- /dev/null +++ b/shared/src/enum/index.ts @@ -0,0 +1,4 @@ +export * from './RoleNames' +export * from './UserContactType' +export * from './PasswordEncryptionType' +export * from './OptInType' diff --git a/shared/src/index.ts b/shared/src/index.ts index 30612341b..d252da56f 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -1,3 +1,2 @@ -import * as schema from './schema' - -export { schema } \ No newline at end of file +export * from './schema' +export * from './enum' \ No newline at end of file diff --git a/shared/src/schema/index.ts b/shared/src/schema/index.ts index 6c1cda831..b60401cfa 100644 --- a/shared/src/schema/index.ts +++ b/shared/src/schema/index.ts @@ -1,4 +1 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' - -export const LOG_CATEGORY_SCHEMA_ALIAS = `${LOG4JS_BASE_CATEGORY_NAME}.schema` -export * from './user.schema' \ No newline at end of file +export * from './user.schema' diff --git a/shared/src/schema/user.schema.test.ts b/shared/src/schema/user.schema.test.ts index 1b4ae1d9d..f29bb2af5 100644 --- a/shared/src/schema/user.schema.test.ts +++ b/shared/src/schema/user.schema.test.ts @@ -1,10 +1,6 @@ -import { validateAlias } from './user.schema' -import { getLogger } from '../../../config-schema/test/testSetup.bun' -import { LOG_CATEGORY_SCHEMA_ALIAS } from '.' +import { aliasSchema } from './user.schema' import { describe, it, expect, beforeEach, mock, jest } from 'bun:test' -import { aliasExists } from 'database' -const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`) mock.module('database', () => ({ aliasExists: jest.fn(), @@ -15,76 +11,25 @@ describe('validate alias', () => { jest.clearAllMocks() }) - describe('alias too short', () => { - it('throws and logs an error', () => { - expect(validateAlias('Bi')).rejects.toThrowError(new Error('Given alias is too short')) - expect(logger.warn.mock.calls[0]).toEqual([ - 'invalid alias', - 'Bi', - expect.arrayContaining([ - expect.objectContaining({ - code: 'too_small', - minimum: 3, - type: 'string', - inclusive: true, - exact: false, - message: 'Given alias is too short', - path: [], - }), - ]), - ]) - }) - }) - - describe('alias too long', () => { - it('throws and logs an error', () => { - expect(validateAlias('BibiBloxbergHexHexHex')).rejects.toThrowError( - new Error('Given alias is too long'), - ) - expect(logger.warn.mock.calls[0]).toEqual([ - 'invalid alias', - 'BibiBloxbergHexHexHex', - expect.arrayContaining([ - expect.objectContaining({ - code: 'too_big', - maximum: 20, - type: 'string', - inclusive: true, - exact: false, - message: 'Given alias is too long', - path: [], - }), - ]), - ]) - }) - }) - describe('alias contains invalid characters', () => { it('throws and logs an error', () => { - expect(validateAlias('Bibi.Bloxberg')).rejects.toEqual( - new Error('Invalid characters in alias'), - ) - expect(logger.warn.mock.calls[0]).toEqual([ - 'invalid alias', - 'Bibi.Bloxberg', + expect(() => aliasSchema.parse('Bibi.Bloxberg')).toThrowError(expect.objectContaining( expect.arrayContaining([ expect.objectContaining({ - validation: 'regex', - code: 'invalid_string', + origin: 'string', + code: 'invalid_format', + format: 'regex', message: 'Invalid characters in alias', path: [], - }), - ]), - ]) + }) + ]) + )) }) }) describe('alias is a reserved word', () => { it('throws and logs an error', () => { - expect(validateAlias('admin')).rejects.toEqual(new Error('Given alias is not allowed')) - expect(logger.warn.mock.calls[0]).toEqual([ - 'invalid alias', - 'admin', + expect(() => aliasSchema.parse('admin')).toThrowError(expect.objectContaining( expect.arrayContaining([ expect.objectContaining({ code: 'custom', @@ -92,16 +37,13 @@ describe('validate alias', () => { path: [], }), ]), - ]) + )) }) }) describe('alias is a reserved word with uppercase characters', () => { it('throws and logs an error', () => { - expect(validateAlias('Admin')).rejects.toEqual(new Error('Given alias is not allowed')) - expect(logger.warn.mock.calls[0]).toEqual([ - 'invalid alias', - 'Admin', + expect(() => aliasSchema.parse('Admin')).toThrowError(expect.objectContaining( expect.arrayContaining([ expect.objectContaining({ code: 'custom', @@ -109,72 +51,40 @@ describe('validate alias', () => { path: [], }), ]), - ]) + )) }) }) describe('hyphens and underscore', () => { describe('alias starts with underscore', () => { it('throws and logs an error', () => { - expect(validateAlias('_bibi')).rejects.toEqual( - new Error('Invalid characters in alias'), - ) - expect(logger.warn.mock.calls[0]).toEqual([ - 'invalid alias', - '_bibi', + expect(() => aliasSchema.parse('_bibi')).toThrowError(expect.objectContaining( expect.arrayContaining([ expect.objectContaining({ - validation: 'regex', - code: 'invalid_string', + origin: 'string', + code: 'invalid_format', + format: 'regex', message: 'Invalid characters in alias', path: [], - }), - ]), - ]) + }) + ]) + )) }) }) describe('alias contains two following hyphens', () => { it('throws and logs an error', () => { - expect(validateAlias('bi--bi')).rejects.toEqual( - new Error('Invalid characters in alias'), - ) - expect(logger.warn.mock.calls[0]).toEqual([ - 'invalid alias', - 'bi--bi', + expect(() => aliasSchema.parse('bi--bi')).toThrowError(expect.objectContaining( expect.arrayContaining([ expect.objectContaining({ - validation: 'regex', - code: 'invalid_string', + origin: 'string', + code: 'invalid_format', + format: 'regex', message: 'Invalid characters in alias', path: [], - }), - ]), - ]) - }) - }) - }) -// TODO: add integration test with real database to test the query, maybe move query - describe('test against existing alias in database', () => { - describe('alias exists in database', () => { - it('throws and logs an error', () => { - (aliasExists as jest.Mock).mockResolvedValue(true) - expect(validateAlias('b-b')).rejects.toEqual(new Error('Given alias is already in use')) - expect(logger.warn.mock.calls[0]).toEqual(['alias already in use', 'b-b']) - }) - }) - -/* describe('alias exists in database with in lower-case', () => { - it('throws and logs an error', () => { - expect(validateAlias('b-B')).rejects.toEqual(new Error('Given alias is already in use')) - expect(logger.warn.mock.calls[0]).toEqual(['alias already in use', 'b-B']) - }) - }) -*/ - describe('valid alias', () => { - it('resolves to true', async () => { - (aliasExists as jest.Mock).mockResolvedValue(false) - expect(validateAlias('bibi')).resolves.toEqual(true) + }) + ]) + )) }) }) }) diff --git a/shared/src/schema/user.schema.ts b/shared/src/schema/user.schema.ts index 23dbe3b85..4a1c51d74 100644 --- a/shared/src/schema/user.schema.ts +++ b/shared/src/schema/user.schema.ts @@ -1,10 +1,6 @@ -import { z } from 'zod' -import { getLogger } from 'log4js' -import { LOG_CATEGORY_SCHEMA_ALIAS } from '.' -import { aliasExists } from 'database' +import { string } from 'zod' export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/ -const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`) const RESERVED_ALIAS = [ 'admin', @@ -24,31 +20,10 @@ const RESERVED_ALIAS = [ 'var', ] -export const aliasSchema = z - .string() +export const aliasSchema = string() .min(3, 'Given alias is too short') .max(20, 'Given alias is too long') .regex(VALID_ALIAS_REGEX, 'Invalid characters in alias') .refine((val) => !RESERVED_ALIAS.includes(val.toLowerCase()), { message: 'Given alias is not allowed', }) - -export const validateAlias = async (alias: string): Promise => { - try { - aliasSchema.parse(alias) - } catch (err) { - if (err instanceof z.ZodError) { - // throw only first error, but log all errors - logger.warn('invalid alias', alias, err.errors) - throw new Error(err.errors[0].message) - } - throw err - } - - if (await aliasExists(alias)) { - logger.warn('alias already in use', alias) - throw new Error('Given alias is already in use') - } - - return true -} diff --git a/yarn.lock b/yarn.lock index 6366943f1..36e413b72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4573,7 +4573,7 @@ browserslist@^4.24.0, browserslist@^4.24.4: node-releases "^2.0.19" update-browserslist-db "^1.1.1" -bs-logger@0.x: +bs-logger@0.x, bs-logger@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== @@ -5377,6 +5377,11 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +dedent@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" + integrity sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA== + deep-eql@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" @@ -5698,7 +5703,7 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -ejs@^3.1.6: +ejs@^3.1.10, ejs@^3.1.6: version "3.1.10" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== @@ -6550,7 +6555,7 @@ fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.5, fast-glob@^3.2.9, fast-g merge2 "^1.3.0" micromatch "^4.0.8" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -8915,7 +8920,7 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== -lodash.memoize@4.x: +lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== @@ -9067,7 +9072,7 @@ make-dir@^4.0.0: dependencies: semver "^7.5.3" -make-error@1.x, make-error@^1.1.1: +make-error@1.x, make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -10869,6 +10874,11 @@ semver@^6.1.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.7.2: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + semver@~7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" @@ -11946,6 +11956,21 @@ ts-jest@27.1.4: semver "7.x" yargs-parser "20.x" +ts-jest@29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.0.tgz#bef0ee98d94c83670af7462a1617bf2367a83740" + integrity sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q== + dependencies: + bs-logger "^0.2.6" + ejs "^3.1.10" + fast-json-stable-stringify "^2.1.0" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.7.2" + type-fest "^4.41.0" + yargs-parser "^21.1.1" + ts-mysql-migrate@^1.0.2: version "1.1.2" resolved "https://registry.yarnpkg.com/ts-mysql-migrate/-/ts-mysql-migrate-1.1.2.tgz#6f280f4684cb95440ffa7254b926b494badb8cf3" @@ -12106,6 +12131,11 @@ type-fest@^4.26.1, type-fest@^4.8.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.40.1.tgz#d78a09f08dd1081a434dd377967650cfd565401d" integrity sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA== +type-fest@^4.41.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + type-graphql@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/type-graphql/-/type-graphql-1.1.1.tgz#dc0710d961713b92d3fee927981fa43bf71667a4" @@ -12189,10 +12219,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typeorm@^0.3.22: - version "0.3.22" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.22.tgz#74b56af4a495b6c8eda887dc9aa4670782b991ff" - integrity sha512-P/Tsz3UpJ9+K0oryC0twK5PO27zejLYYwMsE8SISfZc1lVHX+ajigiOyWsKbuXpEFMjD9z7UjLzY3+ElVOMMDA== +typeorm@^0.3.25: + version "0.3.25" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.25.tgz#9a416f93cda0f612b20f8450e03d6b0e11b467fb" + integrity sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg== dependencies: "@sqltools/formatter" "^1.2.5" ansis "^3.17.0" @@ -12200,6 +12230,7 @@ typeorm@^0.3.22: buffer "^6.0.3" dayjs "^1.11.13" debug "^4.4.0" + dedent "^1.6.0" dotenv "^16.4.7" glob "^10.4.5" sha.js "^2.4.11" @@ -12213,11 +12244,6 @@ typescript@^4.9.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -typescript@^5.8.3: - version "5.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" - integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== - uc.micro@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" From ed5bf96939b1f589a549d08d311a91fb2457cc5a Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 21 Jun 2025 15:07:01 +0200 Subject: [PATCH 26/46] setup improvements --- .github/workflows/test_backend.yml | 2 +- backend/turbo.json | 13 ++--- bun.lock | 47 ++++++++++++++++--- core/README.md | 10 ++++ core/package.json | 2 +- core/src/config/const.ts | 2 +- core/src/index.ts | 2 +- core/src/{schema => validation}/index.ts | 2 +- .../user.test.ts} | 0 .../user.schema.ts => validation/user.ts} | 0 core/turbo.json | 24 ---------- database/Dockerfile | 5 +- database/README.md | 2 + database/turbo.json | 2 +- dht-node/turbo.json | 4 +- federation/package.json | 5 +- federation/tsconfig.json | 2 +- federation/turbo.json | 4 +- shared/README.md | 13 +++++ shared/package.json | 2 +- shared/turbo.json | 11 ----- turbo.json | 3 +- 22 files changed, 90 insertions(+), 67 deletions(-) create mode 100644 core/README.md rename core/src/{schema => validation}/index.ts (82%) rename core/src/{schema/user.schema.test.ts => validation/user.test.ts} (100%) rename core/src/{schema/user.schema.ts => validation/user.ts} (100%) delete mode 100644 core/turbo.json create mode 100644 shared/README.md diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml index ca3ce0e69..d983f331a 100644 --- a/.github/workflows/test_backend.yml +++ b/.github/workflows/test_backend.yml @@ -57,7 +57,7 @@ jobs: - name: install dependencies run: | - bun install --filter backend --frozen-lockfile + bun install --filter backend --filter core --frozen-lockfile bun install --global --no-save turbo@^2 - name: Backend | Unit tests diff --git a/backend/turbo.json b/backend/turbo.json index fa9af6ce2..822b2765f 100644 --- a/backend/turbo.json +++ b/backend/turbo.json @@ -2,25 +2,22 @@ "extends": ["//"], "tasks": { "seed": { - "dependsOn": ["database#up", "config-schema#build", "database#build"], + "dependsOn": ["database#up", "^build"], "cache": false }, "locales": {}, "locales:fix": {}, "lint": { - "dependsOn": ["locales", "database#build"] + "dependsOn": ["locales"] }, "lint:fix": { - "dependsOn": ["locales:fix", "database#build"] - }, - "typecheck": { - "dependsOn": ["database#build", "config-schema#build"] + "dependsOn": ["locales:fix"] }, "test": { - "dependsOn": ["database#up:backend_test", "config-schema#build", "database#build"] + "dependsOn": ["database#up:backend_test", "^build"] }, "dev": { - "dependsOn": ["database#up"] + "dependsOn": ["database#up", "^build"] }, "start": { "dependsOn": ["database#up", "build"] diff --git a/bun.lock b/bun.lock index dbf19bd65..48ccce4be 100644 --- a/bun.lock +++ b/bun.lock @@ -161,7 +161,7 @@ }, "config-schema": { "name": "config-schema", - "version": "1.0.0", + "version": "2.6.0", "dependencies": { "esbuild": "^0.25.2", "joi": "^17.13.3", @@ -179,7 +179,7 @@ }, "core": { "name": "core", - "version": "1.0.0", + "version": "2.6.0", "dependencies": { "database": "workspace:*", "esbuild": "^0.25.2", @@ -206,7 +206,7 @@ "log4js": "^6.9.1", "mysql2": "^2.3.0", "reflect-metadata": "^0.1.13", - "shared": "workspace:*", + "shared": "*", "source-map-support": "^0.5.21", "ts-mysql-migrate": "^1.0.2", "tsx": "^4.20.3", @@ -293,9 +293,9 @@ "apollo-server-testing": "2.25.2", "await-semaphore": "0.1.3", "class-validator": "^0.13.2", - "config-schema": "workspace:*", + "config-schema": "*", "cors": "2.8.5", - "database": "workspace:*", + "database": "*", "decimal.js-light": "^2.5.1", "dotenv": "10.0.0", "express": "^4.17.21", @@ -319,6 +319,7 @@ "typeorm": "^0.3.25", "typescript": "^4.9.5", "uuid": "8.3.2", + "vitest": "^3.2.4", }, }, "frontend": { @@ -418,7 +419,7 @@ }, "shared": { "name": "shared", - "version": "1.0.0", + "version": "2.6.0", "dependencies": { "esbuild": "^0.25.2", "log4js": "^6.9.1", @@ -3738,6 +3739,8 @@ "federation/ts-jest": ["ts-jest@27.0.5", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w=="], + "federation/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + "file-type/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], @@ -4148,6 +4151,32 @@ "federation/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + "federation/vitest/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="], + + "federation/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + + "federation/vitest/@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], + + "federation/vitest/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + + "federation/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], + + "federation/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], + + "federation/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + + "federation/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + + "federation/vitest/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "federation/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "federation/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], + + "federation/vitest/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "federation/vitest/vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + "file-type/get-stream/is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], @@ -4374,6 +4403,12 @@ "editorconfig/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "federation/vitest/@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "federation/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], + + "federation/vitest/@vitest/utils/loupe": ["loupe@3.1.4", "", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="], + "filelist/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "html-to-text/htmlparser2/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], diff --git a/core/README.md b/core/README.md new file mode 100644 index 000000000..4a361b0b8 --- /dev/null +++ b/core/README.md @@ -0,0 +1,10 @@ +# core +Gradido Core Code, High-Level Shared Code, with dependencies on other modules + +## Bun-Compatibility +Full bun compatible + +## Validation +All validation logic used across more than one module +Anything more complex than simple zod schemas is implemented here +Tests written for bun \ No newline at end of file diff --git a/core/package.json b/core/package.json index 26ed4e13b..98863bb60 100644 --- a/core/package.json +++ b/core/package.json @@ -1,7 +1,7 @@ { "name": "core", "version": "2.6.0", - "description": "Gradido Core Code, High-Level Shared Code, with dependencies onto other modules", + "description": "Gradido Core Code, High-Level Shared Code, with dependencies on other modules", "main": "./build/index.js", "types": "./src/index.ts", "exports": { diff --git a/core/src/config/const.ts b/core/src/config/const.ts index 9e9fdf08d..1c19265d0 100644 --- a/core/src/config/const.ts +++ b/core/src/config/const.ts @@ -1 +1 @@ -export const LOG4JS_BASE_CATEGORY_NAME = 'shared' \ No newline at end of file +export const LOG4JS_BASE_CATEGORY_NAME = 'core' \ No newline at end of file diff --git a/core/src/index.ts b/core/src/index.ts index 0cd6afc7d..99830d342 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -1 +1 @@ -export * from './schema' \ No newline at end of file +export * from './validation' \ No newline at end of file diff --git a/core/src/schema/index.ts b/core/src/validation/index.ts similarity index 82% rename from core/src/schema/index.ts rename to core/src/validation/index.ts index 6c1cda831..28d7d3d07 100644 --- a/core/src/schema/index.ts +++ b/core/src/validation/index.ts @@ -1,4 +1,4 @@ import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' export const LOG_CATEGORY_SCHEMA_ALIAS = `${LOG4JS_BASE_CATEGORY_NAME}.schema` -export * from './user.schema' \ No newline at end of file +export * from './user' \ No newline at end of file diff --git a/core/src/schema/user.schema.test.ts b/core/src/validation/user.test.ts similarity index 100% rename from core/src/schema/user.schema.test.ts rename to core/src/validation/user.test.ts diff --git a/core/src/schema/user.schema.ts b/core/src/validation/user.ts similarity index 100% rename from core/src/schema/user.schema.ts rename to core/src/validation/user.ts diff --git a/core/turbo.json b/core/turbo.json deleted file mode 100644 index 9f85ac803..000000000 --- a/core/turbo.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "lint": { - }, - "lint:fix": { - }, - "test": { - "dependsOn": ["config-schema#build"] - }, - "typecheck": { - }, - "dev": { - "dependsOn": ["database#build", "config-schema#build"], - "persistent": true, - "cache": false - }, - "build": { - "dependsOn": ["^build"], - "outputs": ["build/**"], - "cache": true - } - } -} \ No newline at end of file diff --git a/database/Dockerfile b/database/Dockerfile index 5c4e144e5..1ce9843f8 100644 --- a/database/Dockerfile +++ b/database/Dockerfile @@ -64,7 +64,7 @@ RUN bun install --production --no-cache --frozen-lockfile FROM installer as build RUN bun install --no-cache --frozen-lockfile \ - yarn build && yarn typecheck + && yarn build && yarn typecheck ################################################################################## # PRODUCTION IMAGE ############################################################### @@ -72,8 +72,7 @@ RUN bun install --no-cache --frozen-lockfile \ FROM base as production COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/src ./src -COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/migrations ./migrations -COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/entity ./entity +COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/migration ./migration COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/node_modules ./node_modules COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/package.json ./package.json COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/tsconfig.json ./tsconfig.json diff --git a/database/README.md b/database/README.md index 780545381..fe7ed3631 100644 --- a/database/README.md +++ b/database/README.md @@ -49,3 +49,5 @@ yarn clear ``` +## Tests +Currently written for vitest, but can be transformed into bun test after switching out TypeORM with DrizzleORM \ No newline at end of file diff --git a/database/turbo.json b/database/turbo.json index f0d5ac9a5..8ea6e46f5 100644 --- a/database/turbo.json +++ b/database/turbo.json @@ -21,7 +21,7 @@ "dependsOn": ["build"] }, "test": { - "dependsOn": ["up:test"] + "dependsOn": ["up:test", "^build"] }, "down": { "cache": false, diff --git a/dht-node/turbo.json b/dht-node/turbo.json index 3044e8840..27d8044a8 100644 --- a/dht-node/turbo.json +++ b/dht-node/turbo.json @@ -2,10 +2,10 @@ "extends": ["//"], "tasks": { "test": { - "dependsOn": ["config-schema#build", "database#build", "database#up:dht_test"] + "dependsOn": ["database#up:dht_test", "^build"] }, "dev": { - "dependsOn": ["config-schema#build", "database#build", "database#up"] + "dependsOn": ["database#up", "^build"] }, "start": { "dependsOn": ["database#up", "build"] diff --git a/federation/package.json b/federation/package.json index f2bae7d73..7ee32842c 100644 --- a/federation/package.json +++ b/federation/package.json @@ -14,7 +14,7 @@ "dev": "cross-env TZ=UTC nodemon -w src --ext ts,json,css -r tsconfig-paths/register src/index.ts", "dev:bun": "cross-env TZ=UTC bun --hot src/index.ts", "typecheck": "tsc --noEmit", - "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --runInBand --forceExit --detectOpenHandles", + "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation vitest run", "test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles", "test:coverage": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --coverage --runInBand --forceExit --detectOpenHandles", "lint": "biome check --error-on-warnings .", @@ -65,7 +65,8 @@ "type-graphql": "^1.1.1", "typeorm": "^0.3.25", "typescript": "^4.9.5", - "uuid": "8.3.2" + "uuid": "8.3.2", + "vitest": "^3.2.4" }, "nodemonConfig": { "ignore": [ diff --git a/federation/tsconfig.json b/federation/tsconfig.json index 2d0e18ed8..86b102fa6 100644 --- a/federation/tsconfig.json +++ b/federation/tsconfig.json @@ -51,7 +51,7 @@ // "@arg/*": ["src/graphql/arg/*"], // "@enum/*": ["src/graphql/enum/*"], // "@model/*": ["src/graphql/model/*"], - "@repository/*": ["src/typeorm/repository/*"], + // "@repository/*": ["src/typeorm/repository/*"], "@test/*": ["test/*"], /* common */ // "@common/*": ["../common/src/*"], diff --git a/federation/turbo.json b/federation/turbo.json index 2533a8de2..77a13a055 100644 --- a/federation/turbo.json +++ b/federation/turbo.json @@ -2,10 +2,10 @@ "extends": ["//"], "tasks": { "test": { - "dependsOn": ["database#up:federation_test", "config-schema#build", "database#build"] + "dependsOn": ["database#up:federation_test", "^build"] }, "dev": { - "dependsOn": ["database#up"] + "dependsOn": ["database#up", "^build"] }, "start": { "dependsOn": ["database#up", "build"] diff --git a/shared/README.md b/shared/README.md new file mode 100644 index 000000000..43517fe98 --- /dev/null +++ b/shared/README.md @@ -0,0 +1,13 @@ +# shared +Gradido Shared Code, Low-Level Shared Code, without dependencies on other modules + +## Bun-Compatibility +Full bun compatible + +## Enums +All enums used across more than one module +Additional with zod Schema but working only with zod v4 and this needs typescript 5 + +## Schemas +All schemas for data validation used across more than one module +Tests written for bun diff --git a/shared/package.json b/shared/package.json index 5c8ba7195..0225a39e5 100644 --- a/shared/package.json +++ b/shared/package.json @@ -1,7 +1,7 @@ { "name": "shared", "version": "2.6.0", - "description": "Gradido Shared Code, Low-Level Shared Code, without dependencies onto other modules", + "description": "Gradido Shared Code, Low-Level Shared Code, without dependencies on other modules", "main": "./build/index.js", "types": "./src/index.ts", "exports": { diff --git a/shared/turbo.json b/shared/turbo.json index 9f85ac803..399303c0c 100644 --- a/shared/turbo.json +++ b/shared/turbo.json @@ -1,20 +1,9 @@ { "extends": ["//"], "tasks": { - "lint": { - }, - "lint:fix": { - }, "test": { "dependsOn": ["config-schema#build"] }, - "typecheck": { - }, - "dev": { - "dependsOn": ["database#build", "config-schema#build"], - "persistent": true, - "cache": false - }, "build": { "dependsOn": ["^build"], "outputs": ["build/**"], diff --git a/turbo.json b/turbo.json index 65b0842a3..f3f8d92d2 100644 --- a/turbo.json +++ b/turbo.json @@ -6,11 +6,12 @@ "lint:fix": { }, "test": { + "dependsOn": ["^build"] }, "typecheck": { }, "dev": { - "dependsOn": ["config-schema#build"], + "dependsOn": ["^build"], "persistent": true, "cache": false }, From 217eb8ce397763652d28358f93de8cbf0264ffdf Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 21 Jun 2025 15:19:55 +0200 Subject: [PATCH 27/46] fix database docker --- database/Dockerfile | 8 ++++---- database/package.json | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/database/Dockerfile b/database/Dockerfile index 1ce9843f8..5413b891b 100644 --- a/database/Dockerfile +++ b/database/Dockerfile @@ -55,8 +55,8 @@ ENV PATH="/root/.bun/bin:${PATH}" ################################################################################## FROM bun-base as installer -COPY --chown=app:app ./database . -RUN bun install --production --no-cache --frozen-lockfile +COPY --chown=app:app . . +RUN bun install --filter database --production --no-cache --frozen-lockfile ################################################################################## # Build ########################################################################## @@ -64,7 +64,8 @@ RUN bun install --production --no-cache --frozen-lockfile FROM installer as build RUN bun install --no-cache --frozen-lockfile \ - && yarn build && yarn typecheck + && cd shared && yarn build \ + && cd ../database && yarn build && yarn typecheck ################################################################################## # PRODUCTION IMAGE ############################################################### @@ -82,7 +83,6 @@ COPY --chown=app:app --from=installer ${DOCKER_WORKDIR}/tsconfig.json ./tsconfig ################################################################################## FROM production as up - # Run command CMD /bin/sh -c "yarn up" diff --git a/database/package.json b/database/package.json index cd1ad5f87..ed5cd2fe3 100644 --- a/database/package.json +++ b/database/package.json @@ -44,7 +44,6 @@ "ts-jest": "27.0.5", "ts-node": "^10.9.2", "typescript": "^4.9.5", - "uuid": "^8.3.2", "vitest": "^3.2.4" }, "dependencies": { From f277a9c2b05a8b0d0ea90752ee65cc12f83bb132 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 21 Jun 2025 15:31:59 +0200 Subject: [PATCH 28/46] fix --- .github/workflows/lint.yml | 32 +++++++++++++++++++++++++++++++- backend/src/seeds/index.ts | 4 +++- bun.lock | 36 ------------------------------------ federation/package.json | 5 ++--- 4 files changed, 36 insertions(+), 41 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4ff2632e4..1416b2e04 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,6 +7,8 @@ jobs: runs-on: ubuntu-latest outputs: config-schema: ${{ steps.config-schema.outputs.success }} + shared: ${{ steps.shared.outputs.success }} + core: ${{ steps.core.outputs.success }} backend: ${{ steps.backend.outputs.success }} database: ${{ steps.database.outputs.success }} dht-node: ${{ steps.dht-node.outputs.success }} @@ -17,7 +19,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: | @@ -25,6 +27,18 @@ jobs: biome ci . echo $? echo "success=$([ $? -eq 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT + - name: Lint - Shared + id: shared + run: | + cd ./shared + biome ci . + echo "success=$([ $? -eq 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT + - name: Lint - Core + id: core + run: | + cd ./core + biome ci . + echo "success=$([ $? -eq 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT - name: Lint - Backend id: backend run: | @@ -58,6 +72,22 @@ jobs: - name: Check result from previous step run: if [ "${{ needs.lint.outputs.config-schema }}" != "true" ]; then exit 1; fi + lint_shared: + name: Lint - Shared + needs: lint + runs-on: ubuntu-latest + steps: + - name: Check result from previous step + run: if [ "${{ needs.lint.outputs.shared }}" != "true" ]; then exit 1; fi + + lint_core: + name: Lint - Core + needs: lint + runs-on: ubuntu-latest + steps: + - name: Check result from previous step + run: if [ "${{ needs.lint.outputs.core }}" != "true" ]; then exit 1; fi + lint_backend: name: Lint - Backend needs: lint diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts index 506c93d41..4189f87e3 100644 --- a/backend/src/seeds/index.ts +++ b/backend/src/seeds/index.ts @@ -37,7 +37,9 @@ const context = { export const cleanDB = async () => { // this only works as long we do not have foreign key constraints for (const entity of entities) { - await resetEntity(entity) + if (entity.name !== 'Migration') { + await resetEntity(entity) + } } } diff --git a/bun.lock b/bun.lock index 48ccce4be..0a2bf2b49 100644 --- a/bun.lock +++ b/bun.lock @@ -228,7 +228,6 @@ "ts-jest": "27.0.5", "ts-node": "^10.9.2", "typescript": "^4.9.5", - "uuid": "^8.3.2", "vitest": "^3.2.4", }, }, @@ -319,7 +318,6 @@ "typeorm": "^0.3.25", "typescript": "^4.9.5", "uuid": "8.3.2", - "vitest": "^3.2.4", }, }, "frontend": { @@ -3739,8 +3737,6 @@ "federation/ts-jest": ["ts-jest@27.0.5", "", { "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^27.0.0", "json5": "2.x", "lodash": "4.x", "make-error": "1.x", "semver": "7.x", "yargs-parser": "20.x" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@types/jest": "^27.0.0", "babel-jest": ">=27.0.0 <28", "jest": "^27.0.0", "typescript": ">=3.8 <5.0" }, "optionalPeers": ["@babel/core", "@types/jest", "babel-jest"], "bin": { "ts-jest": "cli.js" } }, "sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w=="], - "federation/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - "file-type/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], @@ -4151,32 +4147,6 @@ "federation/ts-jest/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], - "federation/vitest/@types/node": ["@types/node@18.19.96", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-PzBvgsZ7YdFs/Kng1BSW8IGv68/SPcOxYYhT7luxD7QyzIhFS1xPTpfK3K9eHBa7hVwlW+z8nN0mOd515yaduQ=="], - - "federation/vitest/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "federation/vitest/@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], - - "federation/vitest/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], - - "federation/vitest/@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "federation/vitest/@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "federation/vitest/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "federation/vitest/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "federation/vitest/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], - - "federation/vitest/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "federation/vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "federation/vitest/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], - - "federation/vitest/vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "file-type/get-stream/is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], @@ -4403,12 +4373,6 @@ "editorconfig/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "federation/vitest/@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - - "federation/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], - - "federation/vitest/@vitest/utils/loupe": ["loupe@3.1.4", "", {}, "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg=="], - "filelist/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "html-to-text/htmlparser2/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], diff --git a/federation/package.json b/federation/package.json index 7ee32842c..f2bae7d73 100644 --- a/federation/package.json +++ b/federation/package.json @@ -14,7 +14,7 @@ "dev": "cross-env TZ=UTC nodemon -w src --ext ts,json,css -r tsconfig-paths/register src/index.ts", "dev:bun": "cross-env TZ=UTC bun --hot src/index.ts", "typecheck": "tsc --noEmit", - "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation vitest run", + "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --runInBand --forceExit --detectOpenHandles", "test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles", "test:coverage": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test_federation jest --coverage --runInBand --forceExit --detectOpenHandles", "lint": "biome check --error-on-warnings .", @@ -65,8 +65,7 @@ "type-graphql": "^1.1.1", "typeorm": "^0.3.25", "typescript": "^4.9.5", - "uuid": "8.3.2", - "vitest": "^3.2.4" + "uuid": "8.3.2" }, "nodemonConfig": { "ignore": [ From bd6e804a56f3d78ddfd8fa3a85620ca09b4775c5 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sat, 21 Jun 2025 15:42:53 +0200 Subject: [PATCH 29/46] add workflow for core --- .github/file-filters.yml | 3 ++ .github/workflows/test_backend.yml | 8 ++++-- .github/workflows/test_core.yml | 44 +++++++++++++++++++++++++++++ .github/workflows/test_database.yml | 5 ++-- .github/workflows/test_shared.yml | 4 +-- 5 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/test_core.yml diff --git a/.github/file-filters.yml b/.github/file-filters.yml index 15791d4b1..e54a3a6af 100644 --- a/.github/file-filters.yml +++ b/.github/file-filters.yml @@ -39,6 +39,9 @@ config: &config shared: &shared - 'shared/**/*' +core: &core + - 'core/**/*' + database: &database - 'database/**/*' diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml index d983f331a..efb59ba37 100644 --- a/.github/workflows/test_backend.yml +++ b/.github/workflows/test_backend.yml @@ -9,6 +9,8 @@ jobs: outputs: backend: ${{ steps.changes.outputs.backend }} config: ${{ steps.changes.outputs.config }} + core: ${{ steps.changes.outputs.core }} + shared: ${{ steps.changes.outputs.shared }} database: ${{ steps.changes.outputs.database }} docker-compose: ${{ steps.changes.outputs.docker-compose }} mariadb: ${{ steps.changes.outputs.mariadb }} @@ -24,7 +26,7 @@ jobs: list-files: shell build_test: - if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.config == 'true' || needs.files-changed.outputs.database == 'true' + if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.config == 'true' || needs.files-changed.outputs.core == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.shared == 'true' || needs.files-changed.outputs.docker-compose == 'true' name: Docker Build Test - Backend needs: files-changed runs-on: ubuntu-latest @@ -36,7 +38,7 @@ jobs: run: docker build -f ./backend/Dockerfile --target production -t "gradido/backend:production" . unit_test: - if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' || needs.files-changed.outputs.config == 'true' + if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' || needs.files-changed.outputs.config == 'true' || needs.files-changed.outputs.core == 'true' || needs.files-changed.outputs.shared == 'true' name: Unit tests - Backend needs: files-changed runs-on: ubuntu-latest @@ -64,7 +66,7 @@ jobs: run: turbo backend#test typecheck: - if: needs.files-changed.outputs.backend == 'true' + if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.core == 'true' || needs.files-changed.outputs.shared == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.config == 'true' name: Typecheck - Backend needs: files-changed runs-on: ubuntu-latest diff --git a/.github/workflows/test_core.yml b/.github/workflows/test_core.yml new file mode 100644 index 000000000..01e86c453 --- /dev/null +++ b/.github/workflows/test_core.yml @@ -0,0 +1,44 @@ +name: Gradido Core Test CI + +on: push + +jobs: + files-changed: + name: Detect File Changes - Core + runs-on: ubuntu-latest + outputs: + core: ${{ steps.changes.outputs.core }} + database: ${{ steps.changes.outputs.database }} + shared: ${{ steps.changes.outputs.shared }} + steps: + - uses: actions/checkout@v3.3.0 + + - name: Check for core file changes + uses: dorny/paths-filter@v2.11.1 + id: changes + with: + token: ${{ github.token }} + filters: .github/file-filters.yml + list-files: shell + + build: + name: Unit Tests, typecheck - Core + if: needs.files-changed.outputs.core == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.shared == 'true' + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: install bun + uses: oven-sh/setup-bun@v2 + + - name: install dependencies + run: bun install --filter core --frozen-lockfile + + - name: typecheck + run: cd core && yarn typecheck + + - name: unit tests + run: cd core && yarn test + diff --git a/.github/workflows/test_database.yml b/.github/workflows/test_database.yml index 97b0e5fe8..d9a2d3acc 100644 --- a/.github/workflows/test_database.yml +++ b/.github/workflows/test_database.yml @@ -8,6 +8,7 @@ jobs: runs-on: ubuntu-latest outputs: database: ${{ steps.changes.outputs.database }} + shared: ${{ steps.changes.outputs.shared }} docker-compose: ${{ steps.changes.outputs.docker-compose }} mariadb: ${{ steps.changes.outputs.mariadb }} steps: @@ -22,7 +23,7 @@ jobs: list-files: shell build: - if: needs.files-changed.outputs.database == 'true' + if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.shared == 'true' name: Docker Build Test - Database up needs: files-changed runs-on: ubuntu-latest @@ -34,7 +35,7 @@ jobs: run: docker build --target build -t "gradido/database:build" -f database/Dockerfile . database_migration_test: - if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' + if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' || needs.files-changed.outputs.shared == 'true' name: Database Migration Test - Up, Test + Reset needs: files-changed runs-on: ubuntu-latest diff --git a/.github/workflows/test_shared.yml b/.github/workflows/test_shared.yml index 8a1484655..6e377dbbf 100644 --- a/.github/workflows/test_shared.yml +++ b/.github/workflows/test_shared.yml @@ -8,8 +8,6 @@ jobs: runs-on: ubuntu-latest outputs: shared: ${{ steps.changes.outputs.shared }} - docker-compose: ${{ steps.changes.outputs.docker-compose }} - database: ${{ steps.changes.outputs.database }} steps: - uses: actions/checkout@v3.3.0 @@ -23,7 +21,7 @@ jobs: build: name: Unit Tests, typecheck - Shared - if: needs.files-changed.outputs.shared == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.database == 'true' + if: needs.files-changed.outputs.shared == 'true' needs: files-changed runs-on: ubuntu-latest steps: From aab6dcd98bd2738e99a1702ab243b3c09956e659 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 22 Jun 2025 15:14:23 +0200 Subject: [PATCH 30/46] move findUserByIdentifier and validateAlias into database inclusive refactored tests for this --- .../resolver/TransactionResolver.test.ts | 30 +--- .../graphql/resolver/TransactionResolver.ts | 2 +- .../src/graphql/resolver/UserResolver.test.ts | 166 +----------------- backend/src/graphql/resolver/UserResolver.ts | 11 +- .../resolver/util/findUserByIdentifier.ts | 65 ------- .../util/findUserByIdentifiers.test.ts | 92 ---------- bun.lock | 14 +- config-schema/test/testSetup.vitest.ts | 106 +++++++++++ core/src/index.ts | 2 +- core/src/validation/index.ts | 3 +- core/src/validation/user.test.ts | 10 +- core/src/validation/user.ts | 9 +- database/package.json | 3 +- database/src/queries/communities.test.ts | 4 +- database/src/queries/index.ts | 4 + database/src/queries/user.test.ts | 145 ++++++++++++--- database/src/queries/user.ts | 57 +++++- database/src/seeds/factory/user.ts | 12 +- database/src/seeds/utils.ts | 10 -- database/vitest.config.js | 7 + dht-node/jest.config.js | 2 +- dht-node/test/helpers.test.ts | 2 +- .../api/1_0/resolver/SendCoinsResolver.ts | 52 +++--- .../src/graphql/util/findUserByIdentifier.ts | 57 ------ federation/src/graphql/util/validateAlias.ts | 46 ----- shared/package.json | 5 +- shared/src/schema/base.schema.test.ts | 12 ++ shared/src/schema/base.schema.ts | 6 + shared/src/schema/index.ts | 1 + shared/turbo.json | 13 -- yarn.lock | 19 ++ 31 files changed, 410 insertions(+), 557 deletions(-) delete mode 100644 backend/src/graphql/resolver/util/findUserByIdentifier.ts delete mode 100644 backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts create mode 100644 config-schema/test/testSetup.vitest.ts delete mode 100644 database/src/seeds/utils.ts create mode 100644 database/vitest.config.js delete mode 100644 federation/src/graphql/util/findUserByIdentifier.ts delete mode 100644 federation/src/graphql/util/validateAlias.ts create mode 100644 shared/src/schema/base.schema.test.ts create mode 100644 shared/src/schema/base.schema.ts delete mode 100644 shared/turbo.json diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 97b3084d9..77026b445 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -137,19 +137,11 @@ describe('send coins', () => { }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError('No user with this credentials')], + errors: [new GraphQLError('The recipient user was not found')], }), ) }) - it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith( - 'No user with this credentials', - 'wrong@email.com', - homeCom.communityUuid, - ) - }) - describe('deleted recipient', () => { it('throws an error', async () => { jest.clearAllMocks() @@ -170,18 +162,10 @@ describe('send coins', () => { }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError('No user with this credentials')], + errors: [new GraphQLError('The recipient user was not found')], }), ) }) - - it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith( - 'No user with this credentials', - 'stephen@hawking.uk', - homeCom.communityUuid, - ) - }) }) describe('recipient account not activated', () => { @@ -204,18 +188,10 @@ describe('send coins', () => { }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError('No user with this credentials')], + errors: [new GraphQLError('The recipient user was not found')], }), ) }) - - it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith( - 'No user with this credentials', - 'garrick@ollivander.com', - homeCom.communityUuid, - ) - }) }) }) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 511c7167d..cfb4308be 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -5,6 +5,7 @@ import { Transaction as dbTransaction, TransactionLink as dbTransactionLink, User as dbUser, + findUserByIdentifier } from 'database' import { Decimal } from 'decimal.js-light' import { Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql' @@ -40,7 +41,6 @@ import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' import { BalanceResolver } from './BalanceResolver' import { GdtResolver } from './GdtResolver' import { getCommunityByIdentifier, getCommunityName, isHomeCommunity } from './util/communities' -import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' import { diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 9a8c223f1..01340f355 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -68,7 +68,7 @@ import { printTimeDuration } from '@/util/time' import { objectValuesToArray } from '@/util/utilities' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' +import { getLogger } from 'config-schema/test/testSetup' import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' import { Location2Point } from './util/Location2Point' @@ -698,9 +698,6 @@ describe('UserResolver', () => { }) describe('no users in database', () => { - beforeAll(() => { - clearLogs() - }) it('throws an error', async () => { jest.clearAllMocks() const result = await mutate({ mutation: login, variables }) @@ -712,7 +709,6 @@ describe('UserResolver', () => { }) it('logs the error found', () => { - printLogs() expect(logger.warn).toBeCalledWith( `findUserByEmail failed, user with email=${variables.email} not found`, ) @@ -2698,166 +2694,6 @@ describe('UserResolver', () => { expect(logErrorLogger.error).toBeCalledWith('401 Unauthorized') }) }) - - describe('authenticated', () => { - const uuid = uuidv4() - - beforeAll(async () => { - user = await userFactory(testEnv, bibiBloxberg) - await mutate({ - mutation: login, - variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, - }) - // first set alias to null, because updating alias isn't currently allowed - await User.update({ alias: 'BBB' }, { alias: () => 'NULL' }) - await mutate({ - mutation: updateUserInfos, - variables: { - alias: 'bibi', - }, - }) - }) - - describe('identifier is no gradido ID, no email and no alias', () => { - it('throws and logs "Unknown identifier type" error', async () => { - await expect( - query({ - query: userQuery, - variables: { - identifier: 'identifier_is_no_valid_alias!', - communityIdentifier: homeCom1.communityUuid, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [new GraphQLError('Unknown identifier type')], - }), - ) - expect(logErrorLogger.error).toBeCalledWith( - 'Unknown identifier type', - 'identifier_is_no_valid_alias!', - ) - }) - }) - - describe('identifier is not found', () => { - it('throws and logs "No user found to given identifier" error', async () => { - await expect( - query({ - query: userQuery, - variables: { - identifier: uuid, - communityIdentifier: homeCom1.communityUuid, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [new GraphQLError('No user found to given identifier(s)')], - }), - ) - expect(logErrorLogger.error).toBeCalledWith( - 'No user found to given identifier(s)', - uuid, - homeCom1.communityUuid, - ) - }) - }) - - describe('identifier is found via email, but not matching community', () => { - it('returns user', async () => { - await expect( - query({ - query: userQuery, - variables: { - identifier: 'bibi@bloxberg.de', - communityIdentifier: foreignCom1.communityUuid, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [new GraphQLError('No user with this credentials')], - }), - ) - expect(logErrorLogger.error).toBeCalledWith( - 'No user with this credentials', - 'bibi@bloxberg.de', - foreignCom1.communityUuid, - ) - }) - }) - - describe('identifier is found via email', () => { - it('returns user', async () => { - await expect( - query({ - query: userQuery, - variables: { - identifier: 'bibi@bloxberg.de', - communityIdentifier: homeCom1.communityUuid, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - user: expect.objectContaining({ - firstName: 'Bibi', - lastName: 'Bloxberg', - }), - }, - errors: undefined, - }), - ) - }) - }) - - describe('identifier is found via gradidoID', () => { - it('returns user', async () => { - await expect( - query({ - query: userQuery, - variables: { - identifier: user.gradidoID, - communityIdentifier: homeCom1.communityUuid, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - user: expect.objectContaining({ - firstName: 'Bibi', - lastName: 'Bloxberg', - }), - }, - errors: undefined, - }), - ) - }) - }) - - describe('identifier is found via alias', () => { - it('returns user', async () => { - await expect( - query({ - query: userQuery, - variables: { - identifier: 'bibi', - communityIdentifier: homeCom1.communityUuid, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - user: expect.objectContaining({ - firstName: 'Bibi', - lastName: 'Bloxberg', - }), - }, - errors: undefined, - }), - ) - }) - }) - }) }) describe('check username', () => { diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index f7688d1ca..ad952cabe 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -6,6 +6,8 @@ import { UserContact as DbUserContact, ProjectBranding, UserLoggingView, + getHomeCommunity, + findUserByIdentifier } from 'database' import { GraphQLResolveInfo } from 'graphql' import i18n from 'i18n' @@ -93,11 +95,9 @@ import { Logger, getLogger } from 'log4js' import { FULL_CREATION_AVAILABLE } from './const/const' import { Location2Point, Point2Location } from './util/Location2Point' import { authenticateGmsUserPlayground } from './util/authenticateGmsUserPlayground' -import { getHomeCommunity } from 'database' import { compareGmsRelevantUserSettings } from './util/compareGmsRelevantUserSettings' import { getUserCreations } from './util/creations' import { extractGraphQLFieldsForSelect } from './util/extractGraphQLFields' -import { findUserByIdentifier } from './util/findUserByIdentifier' import { findUsers } from './util/findUsers' import { getKlicktippState } from './util/getKlicktippState' import { deleteUserRole, setUserRole } from './util/modifyUserRole' @@ -1153,8 +1153,11 @@ export class UserResolver { { identifier, communityIdentifier }: UserArgs, ): Promise { const foundDbUser = await findUserByIdentifier(identifier, communityIdentifier) - const modelUser = new User(foundDbUser) - return modelUser + if (!foundDbUser) { + createLogger().debug('User not found', identifier, communityIdentifier) + throw new Error('User not found') + } + return new User(foundDbUser) } // FIELD RESOLVERS diff --git a/backend/src/graphql/resolver/util/findUserByIdentifier.ts b/backend/src/graphql/resolver/util/findUserByIdentifier.ts deleted file mode 100644 index ef8b91c2c..000000000 --- a/backend/src/graphql/resolver/util/findUserByIdentifier.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { isURL } from 'class-validator' -import { Community, User as DbUser, UserContact as DbUserContact } from 'database' -import { FindOptionsWhere } from 'typeorm' -import { validate, version } from 'uuid' - -import { LogError } from '@/server/LogError' -import { isEMail, isUUID4 } from '@/util/validate' - -import { aliasSchema } from 'shared' - -/** - * - * @param identifier could be gradidoID, alias or email of user - * @param communityIdentifier could be uuid or name of community - * @returns - */ -export const findUserByIdentifier = async ( - identifier: string, - communityIdentifier: string, -): Promise => { - let user: DbUser | null - const communityWhere: FindOptionsWhere = isURL(communityIdentifier) - ? { url: communityIdentifier } - : isUUID4(communityIdentifier) - ? { communityUuid: communityIdentifier } - : { name: communityIdentifier } - - if (validate(identifier) && version(identifier) === 4) { - user = await DbUser.findOne({ - where: { gradidoID: identifier, community: communityWhere }, - relations: ['emailContact', 'community'], - }) - if (!user) { - throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) - } - } else if (isEMail(identifier)) { - const userContact = await DbUserContact.findOne({ - where: { - email: identifier, - emailChecked: true, - user: { - community: communityWhere, - }, - }, - relations: { user: { community: true } }, - }) - if (!userContact) { - throw new LogError('No user with this credentials', identifier, communityIdentifier) - } - user = userContact.user - user.emailContact = userContact - } else if (aliasSchema.safeParse(identifier).success) { - user = await DbUser.findOne({ - where: { alias: identifier, community: communityWhere }, - relations: ['emailContact', 'community'], - }) - if (!user) { - throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) - } - } else { - throw new LogError('Unknown identifier type', identifier) - } - - return user -} diff --git a/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts b/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts deleted file mode 100644 index 94010b846..000000000 --- a/backend/src/graphql/resolver/util/findUserByIdentifiers.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { ApolloServerTestClient } from 'apollo-server-testing' -import { Community as DbCommunity, User as DbUser } from 'database' -import { DataSource } from 'typeorm' - -import { cleanDB, testEnvironment } from '@test/helpers' - -import { writeHomeCommunityEntry } from '@/seeds/community' -import { userFactory } from '@/seeds/factory/user' -import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' -import { bobBaumeister } from '@/seeds/users/bob-baumeister' -import { peterLustig } from '@/seeds/users/peter-lustig' - -import { findUserByIdentifier } from './findUserByIdentifier' - -jest.mock('@/password/EncryptorUtils') - -let con: DataSource -let testEnv: { - mutate: ApolloServerTestClient['mutate'] - query: ApolloServerTestClient['query'] - con: DataSource -} - -beforeAll(async () => { - testEnv = await testEnvironment() - con = testEnv.con - await cleanDB() -}) - -afterAll(async () => { - await cleanDB() - await con.destroy() -}) - -describe('graphql/resolver/util/findUserByIdentifier', () => { - let homeCom: DbCommunity - let communityUuid: string - let communityName: string - let userBibi: DbUser - - beforeAll(async () => { - homeCom = await writeHomeCommunityEntry() - - communityUuid = homeCom.communityUuid! - - communityName = homeCom.communityUuid! - - userBibi = await userFactory(testEnv, bibiBloxberg) - await userFactory(testEnv, peterLustig) - await userFactory(testEnv, bobBaumeister) - }) - - describe('communityIdentifier is community uuid', () => { - it('userIdentifier is gradido id', async () => { - const user = await findUserByIdentifier(userBibi.gradidoID, communityUuid) - user.userRoles = [] - expect(user).toMatchObject(userBibi) - }) - - it('userIdentifier is alias', async () => { - const user = await findUserByIdentifier(userBibi.alias, communityUuid) - user.userRoles = [] - expect(user).toMatchObject(userBibi) - }) - - it('userIdentifier is email', async () => { - const user = await findUserByIdentifier(userBibi.emailContact.email, communityUuid) - user.userRoles = [] - expect(user).toMatchObject(userBibi) - }) - }) - - describe('communityIdentifier is community name', () => { - it('userIdentifier is gradido id', async () => { - const user = await findUserByIdentifier(userBibi.gradidoID, communityName) - user.userRoles = [] - expect(user).toMatchObject(userBibi) - }) - - it('userIdentifier is alias', async () => { - const user = await findUserByIdentifier(userBibi.alias, communityName) - user.userRoles = [] - expect(user).toMatchObject(userBibi) - }) - - it('userIdentifier is email', async () => { - const user = await findUserByIdentifier(userBibi.emailContact.email, communityName) - user.userRoles = [] - expect(user).toMatchObject(userBibi) - }) - }) -}) diff --git a/bun.lock b/bun.lock index 0a2bf2b49..82616745c 100644 --- a/bun.lock +++ b/bun.lock @@ -181,7 +181,7 @@ "name": "core", "version": "2.6.0", "dependencies": { - "database": "workspace:*", + "database": "*", "esbuild": "^0.25.2", "log4js": "^6.9.1", "zod": "^3.25.61", @@ -224,6 +224,7 @@ "@types/geojson": "^7946.0.13", "@types/jest": "27.0.2", "@types/node": "^18.7.14", + "crypto-random-bigint": "^2.1.1", "jest": "27.2.4", "ts-jest": "27.0.5", "ts-node": "^10.9.2", @@ -251,8 +252,8 @@ "@types/joi": "^17.2.3", "@types/node": "^17.0.45", "@types/uuid": "^8.3.4", - "config-schema": "workspace:*", - "database": "workspace:*", + "config-schema": "*", + "database": "*", "dotenv": "10.0.0", "esbuild": "^0.25.3", "jest": "27.5.1", @@ -427,6 +428,7 @@ "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", "typescript": "^4.9.5", + "uuid": "^8.3.2", }, }, }, @@ -1605,6 +1607,8 @@ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "crypto-random-bigint": ["crypto-random-bigint@2.1.1", "", { "dependencies": { "uint-rng": "^1.2.1" } }, "sha512-96+FDrenXybkpnLML/60S8NcG32KgJ5Y8yvNNCYPW02r+ssoXFR5XKenuIQcHLWumnGj8UPqUUHBzXNrDGkDmQ=="], + "css-functions-list": ["css-functions-list@3.2.3", "", {}, "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA=="], "css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="], @@ -3099,6 +3103,8 @@ "tiny-case": ["tiny-case@1.0.3", "", {}, "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="], + "tiny-webcrypto": ["tiny-webcrypto@1.0.3", "", {}, "sha512-LQQdNMAgz9BXNT2SKbYh3eCb+fLV0p7JB7MwUjzY6IOlQLGIadfnFqRpshERsS5Dl2OM/hs0+4I/XmSrF+RBbw=="], + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], @@ -3209,6 +3215,8 @@ "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], + "uint-rng": ["uint-rng@1.2.1", "", { "dependencies": { "tiny-webcrypto": "^1.0.2" } }, "sha512-swhDg5H+3DX2sIvnYA7VMBMXV/t8mPxvh49CjCDkwFmj/3OZIDOQwJANBgM1MPSUBrUHNIlXmU7/GcL7m4907g=="], + "uint8array-extras": ["uint8array-extras@1.4.0", "", {}, "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ=="], "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], diff --git a/config-schema/test/testSetup.vitest.ts b/config-schema/test/testSetup.vitest.ts new file mode 100644 index 000000000..5d404c806 --- /dev/null +++ b/config-schema/test/testSetup.vitest.ts @@ -0,0 +1,106 @@ +import { vi } from 'vitest' +/* + * This file is used to mock the log4js logger in the tests. + * It is used to collect all log entries in the logs array. + * If you want to debug your test, you can use `printLogs()` to print all log entries collected through the tests. + * To have only the relevant logs, call `clearLogs()` before your calling the methods you like to test and `printLogs()` after. + */ + + +type LogEntry = { + level: string; + message: string; + logger: string; + context: string; + additional: any[]; +} + +const loggers: { [key: string]: any } = {} +const logs: LogEntry[] = [] + +function addLog(level: string, message: string, logger: string, context: Map, additional: any[]) { + logs.push({ + level, + context: [...context.entries()].map(([key, value]) => `${key}=${value}`).join(' ').trimEnd(), + message, + logger, + additional + }) +} + +export function printLogs() { + for (const log of logs) { + const messages = [] + messages.push(log.message) + messages.push(log.additional.map((d) => { + if (typeof d === 'object' && d.toString() === '[object Object]') { + return JSON.stringify(d) + } + if (d) { + return d.toString() + } + }).filter((d) => d)) + process.stdout.write(`${log.logger} [${log.level}] ${log.context} ${messages.join(' ')}\n`) + } +} + +export function clearLogs(): void { + logs.length = 0 +} + +const getLoggerMocked = vi.fn().mockImplementation((param: any) => { + if (loggers[param]) { + // TODO: check if it is working when tests run in parallel + loggers[param].clearContext() + return loggers[param] + } + // console.log('getLogger called with: ', param) + const fakeLogger = { + context: new Map(), + addContext: vi.fn((key: string, value: string) => { + fakeLogger.context.set(key, value) + }), + trace: vi.fn((message: string, ...args: any[]) => { + addLog('trace', message, param, fakeLogger.context, args) + }), + debug: vi.fn((message: string, ...args: any[]) => { + addLog('debug', message, param, fakeLogger.context, args) + }), + warn: vi.fn((message: string, ...args: any[]) => { + addLog('warn', message, param, fakeLogger.context, args) + }), + info: vi.fn((message: string, ...args: any[]) => { + addLog('info', message, param, fakeLogger.context, args) + }), + error: vi.fn((message: string, ...args: any[]) => { + addLog('error', message, param, fakeLogger.context, args) + }), + fatal: vi.fn((message: string, ...args: any[]) => { + addLog('fatal', message, param, fakeLogger.context, args) + }), + removeContext: vi.fn((key: string) => { + fakeLogger.context.delete(key) + }), + clearContext: vi.fn(() => { + fakeLogger.context.clear() + }) + } + loggers[param] = fakeLogger + return fakeLogger +}) + +vi.mock('log4js', () => { + const originalModule = vi.importActual('log4js') + return { + __esModule: true, + ...originalModule, + getLogger: getLoggerMocked + } +}) + +export function getLogger(name: string) { + if (!loggers[name]) { + return getLoggerMocked(name) + } + return loggers[name] +} diff --git a/core/src/index.ts b/core/src/index.ts index 99830d342..0984ebefc 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -1 +1 @@ -export * from './validation' \ No newline at end of file +export * from './validation/user' \ No newline at end of file diff --git a/core/src/validation/index.ts b/core/src/validation/index.ts index 28d7d3d07..b00a2f926 100644 --- a/core/src/validation/index.ts +++ b/core/src/validation/index.ts @@ -1,4 +1,3 @@ import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' -export const LOG_CATEGORY_SCHEMA_ALIAS = `${LOG4JS_BASE_CATEGORY_NAME}.schema` -export * from './user' \ No newline at end of file +export const LOG4JS_CATEGORY_SCHEMA_ALIAS = `${LOG4JS_BASE_CATEGORY_NAME}.schema` diff --git a/core/src/validation/user.test.ts b/core/src/validation/user.test.ts index e8faf5abe..4cfc76a7c 100644 --- a/core/src/validation/user.test.ts +++ b/core/src/validation/user.test.ts @@ -1,10 +1,10 @@ -import { validateAlias } from './user.schema' -import { getLogger } from '../../../config-schema/test/testSetup.bun' -import { LOG_CATEGORY_SCHEMA_ALIAS } from '.' +import { LOG4JS_CATEGORY_SCHEMA_ALIAS } from '.' +import { validateAlias } from './user' +import { getLogger, printLogs } from '../../../config-schema/test/testSetup.bun' import { describe, it, expect, beforeEach, mock, jest } from 'bun:test' import { aliasExists } from 'database' -const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`) +const logger = getLogger(`${LOG4JS_CATEGORY_SCHEMA_ALIAS}.alias`) mock.module('database', () => ({ aliasExists: jest.fn(), @@ -33,7 +33,7 @@ describe('validate alias', () => { minimum: 3, origin: 'string', message: 'Given alias is too short', - }),*/ + }), */ expect.objectContaining({ code: 'too_small', exact: false, diff --git a/core/src/validation/user.ts b/core/src/validation/user.ts index 85d3774d0..945b1c818 100644 --- a/core/src/validation/user.ts +++ b/core/src/validation/user.ts @@ -1,10 +1,10 @@ import { ZodError } from 'zod' import { getLogger } from 'log4js' -import { LOG_CATEGORY_SCHEMA_ALIAS } from '.' +import { LOG4JS_CATEGORY_SCHEMA_ALIAS } from '.' import { aliasExists } from 'database' import { aliasSchema } from 'shared' -const logger = getLogger(`${LOG_CATEGORY_SCHEMA_ALIAS}.alias`) +const logger = getLogger(`${LOG4JS_CATEGORY_SCHEMA_ALIAS}.alias`) export async function validateAlias(alias: string): Promise { try { @@ -12,9 +12,10 @@ export async function validateAlias(alias: string): Promise { } catch (err) { if (err instanceof ZodError || (err as Error).name === 'ZodError') { // throw only first error, but log all errors - logger.warn('invalid alias', alias, (err as ZodError).issues) - throw new Error((err as ZodError).issues[0].message) + logger.warn('invalid alias', alias, (err as ZodError).errors) + throw new Error((err as ZodError).errors[0].message) } + console.log(err) throw err } diff --git a/database/package.json b/database/package.json index ed5cd2fe3..21d027427 100644 --- a/database/package.json +++ b/database/package.json @@ -20,7 +20,7 @@ "lint": "biome check --error-on-warnings .", "lint:fix": "biome check --error-on-warnings . --write", "clear": "cross-env TZ=UTC tsx migration/index.ts clear", - "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test vitest run", + "test": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test vitest --reporter verbose --no-file-parallelism run", "test:debug": "cross-env TZ=UTC NODE_ENV=development DB_DATABASE=gradido_test node --inspect-brk node_modules/.bin/jest --bail --runInBand --forceExit --detectOpenHandles", "up": "cross-env TZ=UTC tsx migration/index.ts up", "down": "cross-env TZ=UTC tsx migration/index.ts down", @@ -40,6 +40,7 @@ "@types/geojson": "^7946.0.13", "@types/jest": "27.0.2", "@types/node": "^18.7.14", + "crypto-random-bigint": "^2.1.1", "jest": "27.2.4", "ts-jest": "27.0.5", "ts-node": "^10.9.2", diff --git a/database/src/queries/communities.test.ts b/database/src/queries/communities.test.ts index ca8ab0b12..9cfb210db 100644 --- a/database/src/queries/communities.test.ts +++ b/database/src/queries/communities.test.ts @@ -1,4 +1,4 @@ -import { Community } from '..' +import { Community as DbCommunity } from '..' import { AppDatabase } from '../AppDatabase' import { getHomeCommunity } from './communities' import { describe, expect, it, beforeAll, afterAll } from 'vitest' @@ -15,7 +15,7 @@ afterAll(async () => { describe('community.queries', () => { beforeAll(async () => { - await Community.clear() + await DbCommunity.clear() }) describe('getHomeCommunity', () => { it('should return null if no home community exists', async () => { diff --git a/database/src/queries/index.ts b/database/src/queries/index.ts index bb6255b2e..c244a4af0 100644 --- a/database/src/queries/index.ts +++ b/database/src/queries/index.ts @@ -1,2 +1,6 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' + export * from './user' export * from './communities' + +export const LOG4JS_QUERIES_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.queries` diff --git a/database/src/queries/user.test.ts b/database/src/queries/user.test.ts index 4ab567a24..a14f70a3c 100644 --- a/database/src/queries/user.test.ts +++ b/database/src/queries/user.test.ts @@ -1,11 +1,18 @@ -import { User, UserContact } from '..' +import { User as DbUser, UserContact as DbUserContact, Community as DbCommunity } from '..' import { AppDatabase } from '../AppDatabase' -import { aliasExists } from './user' +import { aliasExists, findUserByIdentifier } from './user' import { userFactory } from '../seeds/factory/user' import { bibiBloxberg } from '../seeds/users/bibi-bloxberg' -import { describe, expect, it, beforeAll, afterAll } from 'vitest' +import { describe, expect, it, beforeAll, afterAll, vi } from 'vitest' +import { createCommunity } from '../seeds/homeCommunity' +import { peterLustig } from '../seeds/users/peter-lustig' +import { bobBaumeister } from '../seeds/users/bob-baumeister' +import { getLogger, printLogs, clearLogs } from '../../../config-schema/test/testSetup.vitest.ts' +import { LOG4JS_QUERIES_CATEGORY_NAME } from '.' +import { beforeEach } from 'node:test' const db = AppDatabase.getInstance() +const userIdentifierLoggerName = `${LOG4JS_QUERIES_CATEGORY_NAME}.user.findUserByIdentifier` beforeAll(async () => { await db.init() @@ -14,29 +21,121 @@ afterAll(async () => { await db.destroy() }) -describe('integration test mysql queries', () => { - describe('user.queries', () => { - describe('aliasExists', () => { - beforeAll(async () => { - await User.clear() - await UserContact.clear() +describe('user.queries', () => { + describe('aliasExists', () => { + beforeAll(async () => { + await DbUser.clear() + await DbUserContact.clear() - const bibi = bibiBloxberg - bibi.alias = 'b-b' - await userFactory(bibi) + const bibi = bibiBloxberg + bibi.alias = 'b-b' + await userFactory(bibi) + }) + + it('should return true if alias exists', async () => { + expect(await aliasExists('b-b')).toBe(true) + }) + + it('should return true if alias exists even with deviating casing', async () => { + expect(await aliasExists('b-B')).toBe(true) + }) + + it('should return false if alias does not exist', async () => { + expect(await aliasExists('bibi')).toBe(false) + }) + }) + + describe('findUserByIdentifier', () => { + let homeCom: DbCommunity + let communityUuid: string + let communityName: string + let userBibi: DbUser + + beforeAll(async () => { + await DbUser.clear() + await DbUserContact.clear() + await DbCommunity.clear() + + homeCom = await createCommunity(false) + communityUuid = homeCom.communityUuid! + communityName = homeCom.name! + userBibi = await userFactory(bibiBloxberg) + await userFactory(peterLustig) + await userFactory(bobBaumeister) + }) + beforeEach(() => { + clearLogs() + }) + describe('communityIdentifier is community uuid', () => { + it('userIdentifier is gradido id', async () => { + const user = await findUserByIdentifier(userBibi.gradidoID, communityUuid) + expect(user).toMatchObject(userBibi) }) - - it('should return true if alias exists', async () => { - expect(await aliasExists('b-b')).toBe(true) + + it('userIdentifier is alias', async () => { + const user = await findUserByIdentifier(userBibi.alias, communityUuid) + expect(user).toMatchObject(userBibi) }) - - it('should return true if alias exists even with deviating casing', async () => { - expect(await aliasExists('b-B')).toBe(true) + + it('userIdentifier is email', async () => { + const user = await findUserByIdentifier(userBibi.emailContact.email, communityUuid) + expect(user).toMatchObject(userBibi) }) - - it('should return false if alias does not exist', async () => { - expect(await aliasExists('bibi')).toBe(false) + it('userIdentifier is unknown', async () => { + const user = await findUserByIdentifier('unknown', communityUuid) + expect(user).toBeNull() }) }) - }) -}) \ No newline at end of file + + describe('communityIdentifier is community name', () => { + it('userIdentifier is gradido id', async () => { + const user = await findUserByIdentifier(userBibi.gradidoID, communityName) + expect(user).toMatchObject(userBibi) + }) + + it('userIdentifier is alias', async () => { + const user = await findUserByIdentifier(userBibi.alias, communityName) + expect(user).toMatchObject(userBibi) + }) + + it('userIdentifier is email', async () => { + const user = await findUserByIdentifier(userBibi.emailContact.email, communityName) + expect(user).toMatchObject(userBibi) + }) + }) + describe('communityIdentifier is unknown', () => { + it('userIdentifier is gradido id', async () => { + const user = await findUserByIdentifier(userBibi.gradidoID, 'unknown') + expect(user).toBeNull() + }) + it('userIdentifier is unknown', async () => { + const user = await findUserByIdentifier('unknown', communityUuid) + expect(user).toBeNull() + }) + }) + describe('communityIdentifier is empty', () => { + it('userIdentifier is gradido id', async () => { + const user = await findUserByIdentifier(userBibi.gradidoID) + expect(user).toMatchObject(userBibi) + }) + + it('userIdentifier is alias', async () => { + const user = await findUserByIdentifier(userBibi.alias) + expect(user).toMatchObject(userBibi) + }) + + it('userIdentifier is email', async () => { + const user = await findUserByIdentifier(userBibi.emailContact.email) + expect(user).toMatchObject(userBibi) + }) + it('userIdentifier is unknown type', async () => { + const user = await findUserByIdentifier('sa') + printLogs() + expect(getLogger(userIdentifierLoggerName).warn).toHaveBeenCalledWith('Unknown identifier type', 'sa') + expect(user).toBeNull() + }) + }) + }) +}) + + diff --git a/database/src/queries/user.ts b/database/src/queries/user.ts index 49394ad8f..b968e9433 100644 --- a/database/src/queries/user.ts +++ b/database/src/queries/user.ts @@ -1,9 +1,62 @@ import { Raw } from 'typeorm' -import { User as DbUser } from '../entity' +import { Community, User as DbUser, UserContact as DbUserContact } from '../entity' +import { FindOptionsWhere } from 'typeorm' +import { aliasSchema, emailSchema, uuidv4Schema, urlSchema } from 'shared' +import { getLogger } from 'log4js' +import { LOG4JS_QUERIES_CATEGORY_NAME } from './index' export async function aliasExists(alias: string): Promise { const user = await DbUser.findOne({ where: { alias: Raw((a) => `LOWER(${a}) = LOWER(:alias)`, { alias }) }, }) return user !== null -} \ No newline at end of file +} + +/** + * + * @param identifier could be gradidoID, alias or email of user + * @param communityIdentifier could be uuid or name of community + * @returns + */ +export const findUserByIdentifier = async ( + identifier: string, + communityIdentifier?: string, +): Promise => { + const communityWhere: FindOptionsWhere = urlSchema.safeParse(communityIdentifier).success + ? { url: communityIdentifier } + : uuidv4Schema.safeParse(communityIdentifier).success + ? { communityUuid: communityIdentifier } + : { name: communityIdentifier } + + if (uuidv4Schema.safeParse(identifier).success) { + return DbUser.findOne({ + where: { gradidoID: identifier, community: communityWhere }, + relations: ['emailContact', 'community'], + }) + } else if (emailSchema.safeParse(identifier).success) { + const userContact = await DbUserContact.findOne({ + where: { + email: identifier, + emailChecked: true, + user: { + community: communityWhere, + }, + }, + relations: { user: { community: true } }, + }) + if (userContact) { + // TODO: remove circular reference + const user = userContact.user + user.emailContact = userContact + return user + } + } else if (aliasSchema.safeParse(identifier).success) { + return await DbUser.findOne({ + where: { alias: identifier, community: communityWhere }, + relations: ['emailContact', 'community'], + }) + } + // should don't happen often, so we create only in the rare case a logger for it + getLogger(`${LOG4JS_QUERIES_CATEGORY_NAME}.user.findUserByIdentifier`).warn('Unknown identifier type', identifier) + return null +} diff --git a/database/src/seeds/factory/user.ts b/database/src/seeds/factory/user.ts index 412d0088a..293ee4bb3 100644 --- a/database/src/seeds/factory/user.ts +++ b/database/src/seeds/factory/user.ts @@ -1,8 +1,9 @@ import { UserInterface } from '../users/UserInterface' import { User, UserContact } from '../../entity' -import { generateRandomNumber, generateRandomNumericString } from '../utils' import { v4 } from 'uuid' import { UserContactType, OptInType, PasswordEncryptionType } from 'shared' +import { getHomeCommunity } from '../../queries/communities' +import random from 'crypto-random-bigint' export const userFactory = async (user: UserInterface): Promise => { let dbUserContact = new UserContact() @@ -21,12 +22,17 @@ export const userFactory = async (user: UserInterface): Promise => { dbUser.gradidoID = v4() if (user.emailChecked) { - dbUserContact.emailVerificationCode = generateRandomNumericString(64) + dbUserContact.emailVerificationCode = random(64).toString() dbUserContact.emailOptInTypeId = OptInType.EMAIL_OPT_IN_REGISTER dbUserContact.emailChecked = true - dbUser.password = generateRandomNumber() + dbUser.password = random(64) dbUser.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID } + const homeCommunity = await getHomeCommunity() + if (homeCommunity) { + dbUser.community = homeCommunity + dbUser.communityUuid = homeCommunity.communityUuid! + } // TODO: improve with cascade dbUser = await dbUser.save() dbUserContact.userId = dbUser.id diff --git a/database/src/seeds/utils.ts b/database/src/seeds/utils.ts deleted file mode 100644 index 0aa6c2d0f..000000000 --- a/database/src/seeds/utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { randomBytes } from 'node:crypto' - -export function generateRandomNumber(): BigInt { - return BigInt(randomBytes(8).readBigUInt64LE()) -} -export function generateRandomNumericString(length: number = 64): string { - const digits = '0123456789' - const bytes = randomBytes(length / 8) - return Array.from(bytes, (b) => digits[b % 10]).join('').slice(0, length) -} \ No newline at end of file diff --git a/database/vitest.config.js b/database/vitest.config.js new file mode 100644 index 000000000..b0e269a1a --- /dev/null +++ b/database/vitest.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + setupFiles: '../config-schema/test/testSetup.vitest.ts', + }, +}) \ No newline at end of file diff --git a/dht-node/jest.config.js b/dht-node/jest.config.js index bca83b5ce..fe5fb923d 100644 --- a/dht-node/jest.config.js +++ b/dht-node/jest.config.js @@ -3,7 +3,7 @@ module.exports = { verbose: false, preset: 'ts-jest', collectCoverage: false, - collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], + collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!build/**'], coverageThreshold: { global: { lines: 82, diff --git a/dht-node/test/helpers.test.ts b/dht-node/test/helpers.test.ts index 69d8f3fa4..37810e878 100644 --- a/dht-node/test/helpers.test.ts +++ b/dht-node/test/helpers.test.ts @@ -1,4 +1,4 @@ -import { contributionDateFormatter } from '@test/helpers' +import { contributionDateFormatter } from './helpers' describe('contributionDateFormatter', () => { it('formats the date correctly', () => { diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index a6eeef7f8..d3c028df3 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -1,10 +1,10 @@ -import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier' import { fullName } from '@/graphql/util/fullName' import { LogError } from '@/server/LogError' import { Community as DbCommunity, PendingTransaction as DbPendingTransaction, PendingTransactionLoggingView, + findUserByIdentifier } from 'database' import Decimal from 'decimal.js-light' import { getLogger } from 'log4js' @@ -43,14 +43,14 @@ export class SendCoinsResolver { ) } let receiverUser - try { - // second check if receiver user exists in this community - receiverUser = await findUserByIdentifier( - args.recipientUserIdentifier, - args.recipientCommunityUuid, - ) - } catch (err) { - logger.error('Error in findUserByIdentifier:', err) + + // second check if receiver user exists in this community + receiverUser = await findUserByIdentifier( + args.recipientUserIdentifier, + args.recipientCommunityUuid, + ) + if (!receiverUser) { + logger.error('Error in findUserByIdentifier:') throw new LogError( `voteForSendCoins with unknown recipientUserIdentifier in the community=`, homeCom.name, @@ -126,11 +126,11 @@ export class SendCoinsResolver { ) } let receiverUser - try { - // second check if receiver user exists in this community - receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) - } catch (err) { - logger.error('Error in findUserByIdentifier:', err) + + // second check if receiver user exists in this community + receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) + if (!receiverUser) { + logger.error('Error in findUserByIdentifier') throw new LogError( `revertSendCoins with unknown recipientUserIdentifier in the community=`, homeCom.name, @@ -193,12 +193,11 @@ export class SendCoinsResolver { args.recipientCommunityUuid, ) } - let receiverUser - try { - // second check if receiver user exists in this community - receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) - } catch (err) { - logger.error('Error in findUserByIdentifier:', err) + + // second check if receiver user exists in this community + const receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) + if (!receiverUser) { + logger.error('Error in findUserByIdentifier') throw new LogError( `settleSendCoins with unknown recipientUserIdentifier in the community=`, homeCom.name, @@ -265,13 +264,12 @@ export class SendCoinsResolver { `revertSettledSendCoins with wrong recipientCommunityUuid`, args.recipientCommunityUuid, ) - } - let receiverUser - try { - // second check if receiver user exists in this community - receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) - } catch (err) { - logger.error('Error in findUserByIdentifier:', err) + } + + // second check if receiver user exists in this community + const receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) + if (!receiverUser) { + logger.error('Error in findUserByIdentifier') throw new LogError( `revertSettledSendCoins with unknown recipientUserIdentifier in the community=`, homeCom.name, diff --git a/federation/src/graphql/util/findUserByIdentifier.ts b/federation/src/graphql/util/findUserByIdentifier.ts deleted file mode 100644 index 7f5e8e329..000000000 --- a/federation/src/graphql/util/findUserByIdentifier.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { User as DbUser, UserContact as DbUserContact } from 'database' -import { validate, version } from 'uuid' - -import { LogError } from '@/server/LogError' - -import { VALID_ALIAS_REGEX } from './validateAlias' - -export const findUserByIdentifier = async ( - identifier: string, - communityIdentifier?: string, -): Promise => { - let user: DbUser | null - if (validate(identifier) && version(identifier) === 4) { - user = await DbUser.findOne({ - where: { gradidoID: identifier, communityUuid: communityIdentifier }, - relations: ['emailContact'], - }) - if (!user) { - throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) - } - } else if (/^.{2,}@.{2,}\..{2,}$/.exec(identifier)) { - const userContact = await DbUserContact.findOne({ - where: { - email: identifier, - emailChecked: true, - }, - relations: ['user'], - }) - if (!userContact) { - throw new LogError('No user with this credentials', identifier) - } - if (!userContact.user) { - throw new LogError('No user to given contact', identifier) - } - if (userContact.user.communityUuid !== communityIdentifier) { - throw new LogError( - 'Found user to given contact, but belongs to other community', - identifier, - communityIdentifier, - ) - } - user = userContact.user - user.emailContact = userContact - } else if (VALID_ALIAS_REGEX.exec(identifier)) { - user = await DbUser.findOne({ - where: { alias: identifier, communityUuid: communityIdentifier }, - relations: ['emailContact'], - }) - if (!user) { - throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) - } - } else { - throw new LogError('Unknown identifier type', identifier) - } - - return user -} diff --git a/federation/src/graphql/util/validateAlias.ts b/federation/src/graphql/util/validateAlias.ts deleted file mode 100644 index 4eccad63f..000000000 --- a/federation/src/graphql/util/validateAlias.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { User as DbUser } from 'database' -import { Raw } from 'typeorm' - -import { LogError } from '@/server/LogError' - -export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/ - -const RESERVED_ALIAS = [ - 'admin', - 'email', - 'gast', - 'gdd', - 'gradido', - 'guest', - 'home', - 'root', - 'support', - 'temp', - 'tmp', - 'tmp', - 'user', - 'usr', - 'var', -] - -export const validateAlias = async (alias: string): Promise => { - if (alias.length < 3) { - throw new LogError('Given alias is too short', alias) - } - if (alias.length > 20) { - throw new LogError('Given alias is too long', alias) - } - if (!alias.match(VALID_ALIAS_REGEX)) { - throw new LogError('Invalid characters in alias', alias) - } - if (RESERVED_ALIAS.includes(alias.toLowerCase())) { - throw new LogError('Alias is not allowed', alias) - } - const aliasInUse = await DbUser.find({ - where: { alias: Raw((a) => `LOWER(${a}) = "${alias.toLowerCase()}"`) }, - }) - if (aliasInUse.length !== 0) { - throw new LogError('Alias already in use', alias) - } - return true -} diff --git a/shared/package.json b/shared/package.json index 0225a39e5..88eb5b557 100644 --- a/shared/package.json +++ b/shared/package.json @@ -7,7 +7,7 @@ "exports": { ".": { "import": "./build/index.js", - "require": "./build/index.js" + "require": "./build/index.js" } }, "repository": "https://github.com/gradido/gradido/shared", @@ -26,7 +26,8 @@ "devDependencies": { "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "uuid": "^8.3.2" }, "dependencies": { "esbuild": "^0.25.2", diff --git a/shared/src/schema/base.schema.test.ts b/shared/src/schema/base.schema.test.ts new file mode 100644 index 000000000..3e56a9a22 --- /dev/null +++ b/shared/src/schema/base.schema.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, it } from 'bun:test' +import { uuidv4Schema } from './base.schema' +import { v4 as uuidv4 } from 'uuid' + +describe('uuidv4 schema', () => { + it('should validate uuidv4 (40x)', () => { + for (let i = 0; i < 40; i++) { + const uuid = uuidv4() + expect(uuidv4Schema.safeParse(uuid).success).toBeTruthy() + } + }) +}) diff --git a/shared/src/schema/base.schema.ts b/shared/src/schema/base.schema.ts new file mode 100644 index 000000000..ed341c48b --- /dev/null +++ b/shared/src/schema/base.schema.ts @@ -0,0 +1,6 @@ +import { string } from 'zod' +import { validate, version } from 'uuid' + +export const uuidv4Schema = string().refine((val: string) => validate(val) && version(val) === 4, 'Invalid uuid') +export const emailSchema = string().email() +export const urlSchema = string().url() \ No newline at end of file diff --git a/shared/src/schema/index.ts b/shared/src/schema/index.ts index b60401cfa..d8c9f9e4c 100644 --- a/shared/src/schema/index.ts +++ b/shared/src/schema/index.ts @@ -1 +1,2 @@ export * from './user.schema' +export * from './base.schema' \ No newline at end of file diff --git a/shared/turbo.json b/shared/turbo.json deleted file mode 100644 index 399303c0c..000000000 --- a/shared/turbo.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "test": { - "dependsOn": ["config-schema#build"] - }, - "build": { - "dependsOn": ["^build"], - "outputs": ["build/**"], - "cache": true - } - } -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 36e413b72..d68c092c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5180,6 +5180,13 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" +crypto-random-bigint@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/crypto-random-bigint/-/crypto-random-bigint-2.1.1.tgz#f80239ca9d69b53a4920fc5908949689d1b9db95" + integrity sha512-96+FDrenXybkpnLML/60S8NcG32KgJ5Y8yvNNCYPW02r+ssoXFR5XKenuIQcHLWumnGj8UPqUUHBzXNrDGkDmQ== + dependencies: + uint-rng "^1.2.1" + css-functions-list@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.2.3.tgz#95652b0c24f0f59b291a9fc386041a19d4f40dbe" @@ -11735,6 +11742,11 @@ tiny-case@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03" integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q== +tiny-webcrypto@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-webcrypto/-/tiny-webcrypto-1.0.3.tgz#a78e1c5707c546a7d086569368b13a0de56dc9f6" + integrity sha512-LQQdNMAgz9BXNT2SKbYh3eCb+fLV0p7JB7MwUjzY6IOlQLGIadfnFqRpshERsS5Dl2OM/hs0+4I/XmSrF+RBbw== + tinybench@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" @@ -12269,6 +12281,13 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== +uint-rng@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/uint-rng/-/uint-rng-1.2.1.tgz#4d1d22f75f52bc4baab739a0363fd054474be9c8" + integrity sha512-swhDg5H+3DX2sIvnYA7VMBMXV/t8mPxvh49CjCDkwFmj/3OZIDOQwJANBgM1MPSUBrUHNIlXmU7/GcL7m4907g== + dependencies: + tiny-webcrypto "^1.0.2" + uint8array-extras@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/uint8array-extras/-/uint8array-extras-1.4.0.tgz#e42a678a6dd335ec2d21661333ed42f44ae7cc74" From 70e9fb154223c2f1e59f7b391986b1c43819a6b0 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 22 Jun 2025 15:50:47 +0200 Subject: [PATCH 31/46] fix --- .github/workflows/test_core.yml | 11 +++++------ bun.lock | 3 +++ database/src/queries/user.test.ts | 4 ++-- database/tsconfig.json | 3 ++- shared/package.json | 3 ++- yarn.lock | 5 +++++ 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test_core.yml b/.github/workflows/test_core.yml index 01e86c453..b6e95f60c 100644 --- a/.github/workflows/test_core.yml +++ b/.github/workflows/test_core.yml @@ -34,11 +34,10 @@ jobs: uses: oven-sh/setup-bun@v2 - name: install dependencies - run: bun install --filter core --frozen-lockfile + run: | + bun install --filter core --frozen-lockfile + bun install --global turbo@^2 - - name: typecheck - run: cd core && yarn typecheck - - - name: unit tests - run: cd core && yarn test + - name: typecheck && unit test + run: turbo core#test core#typecheck diff --git a/bun.lock b/bun.lock index 82616745c..a6b408a40 100644 --- a/bun.lock +++ b/bun.lock @@ -427,6 +427,7 @@ "devDependencies": { "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", + "@types/uuid": "^10.0.0", "typescript": "^4.9.5", "uuid": "^8.3.2", }, @@ -3905,6 +3906,8 @@ "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], + "shared/@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="], + "simple-update-notifier/semver": ["semver@7.0.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A=="], "sodium-secretstream/sodium-universal": ["sodium-universal@5.0.1", "", { "dependencies": { "sodium-native": "^5.0.1" }, "peerDependencies": { "sodium-javascript": "~0.8.0" }, "optionalPeers": ["sodium-javascript"] }, "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q=="], diff --git a/database/src/queries/user.test.ts b/database/src/queries/user.test.ts index a14f70a3c..1b0008c94 100644 --- a/database/src/queries/user.test.ts +++ b/database/src/queries/user.test.ts @@ -3,11 +3,11 @@ import { AppDatabase } from '../AppDatabase' import { aliasExists, findUserByIdentifier } from './user' import { userFactory } from '../seeds/factory/user' import { bibiBloxberg } from '../seeds/users/bibi-bloxberg' -import { describe, expect, it, beforeAll, afterAll, vi } from 'vitest' +import { describe, expect, it, beforeAll, afterAll } from 'vitest' import { createCommunity } from '../seeds/homeCommunity' import { peterLustig } from '../seeds/users/peter-lustig' import { bobBaumeister } from '../seeds/users/bob-baumeister' -import { getLogger, printLogs, clearLogs } from '../../../config-schema/test/testSetup.vitest.ts' +import { getLogger, printLogs, clearLogs } from '../../../config-schema/test/testSetup.vitest' import { LOG4JS_QUERIES_CATEGORY_NAME } from '.' import { beforeEach } from 'node:test' diff --git a/database/tsconfig.json b/database/tsconfig.json index ca22317ab..ec6769fa2 100644 --- a/database/tsconfig.json +++ b/database/tsconfig.json @@ -74,5 +74,6 @@ "references": [], /* Any project that is referenced must itself have a `references` array (which may be empty). */ "ts-node": { "swc": true - } + }, + "exclude": ["**/*.test.ts", "**/*.spec.ts", "test/*"] } diff --git a/shared/package.json b/shared/package.json index 88eb5b557..31c7404bc 100644 --- a/shared/package.json +++ b/shared/package.json @@ -7,7 +7,7 @@ "exports": { ".": { "import": "./build/index.js", - "require": "./build/index.js" + "require": "./build/index.js" } }, "repository": "https://github.com/gradido/gradido/shared", @@ -26,6 +26,7 @@ "devDependencies": { "@biomejs/biome": "2.0.0", "@types/node": "^17.0.21", + "@types/uuid": "^10.0.0", "typescript": "^4.9.5", "uuid": "^8.3.2" }, diff --git a/yarn.lock b/yarn.lock index d68c092c1..3efa645a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2961,6 +2961,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== +"@types/uuid@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" + integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== + "@types/uuid@^8.3.4": version "8.3.4" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" From fbd38d2e5a7514ffd017c19353d3605cc5622f23 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 23 Jun 2025 13:47:34 +0200 Subject: [PATCH 32/46] move decay into shared --- backend/src/graphql/model/Decay.ts | 10 +-- .../src/graphql/resolver/BalanceResolver.ts | 2 +- .../graphql/resolver/ContributionResolver.ts | 3 +- .../graphql/resolver/StatisticsResolver.ts | 2 +- .../resolver/TransactionLinkResolver.ts | 2 +- backend/src/logging/DecayLogging.view.ts | 3 +- backend/src/util/calculateSenderBalance.ts | 4 +- backend/src/util/decay.ts | 71 ------------------- backend/src/util/validate.ts | 4 +- backend/src/util/virtualTransactions.ts | 2 +- bun.lock | 1 + federation/src/config/schema.ts | 2 - federation/src/graphql/api/1_0/model/Decay.ts | 10 +-- .../api/1_0/util/calculateRecipientBalance.ts | 4 +- federation/src/graphql/util/decay.test.ts | 42 ----------- shared/package.json | 1 + shared/src/const/index.ts | 2 + shared/src/index.ts | 3 +- .../util => shared/src/logic}/decay.test.ts | 0 .../util => shared/src/logic}/decay.ts | 37 ++++++---- shared/src/logic/index.ts | 4 ++ 21 files changed, 49 insertions(+), 160 deletions(-) delete mode 100644 backend/src/util/decay.ts delete mode 100644 federation/src/graphql/util/decay.test.ts create mode 100644 shared/src/const/index.ts rename {backend/src/util => shared/src/logic}/decay.test.ts (100%) rename {federation/src/graphql/util => shared/src/logic}/decay.ts (57%) create mode 100644 shared/src/logic/index.ts diff --git a/backend/src/graphql/model/Decay.ts b/backend/src/graphql/model/Decay.ts index a32b96c13..838e442f7 100644 --- a/backend/src/graphql/model/Decay.ts +++ b/backend/src/graphql/model/Decay.ts @@ -1,14 +1,6 @@ import { Decimal } from 'decimal.js-light' import { Field, Int, ObjectType } from 'type-graphql' - -interface DecayInterface { - balance: Decimal - decay: Decimal - roundedDecay: Decimal - start: Date | null - end: Date | null - duration: number | null -} +import { Decay as DecayInterface } from 'shared' @ObjectType() export class Decay { diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index f3c7d4709..9ac6b125b 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -9,7 +9,7 @@ import { RIGHTS } from '@/auth/RIGHTS' import { BalanceLoggingView } from '@/logging/BalanceLogging.view' import { DecayLoggingView } from '@/logging/DecayLogging.view' import { Context, getUser } from '@/server/context' -import { calculateDecay } from '@/util/decay' +import { calculateDecay } from 'shared' import { getLogger } from 'log4js' import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 687aa0196..21162a4cb 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -19,7 +19,6 @@ import { ContributionType } from '@enum/ContributionType' import { TransactionTypeId } from '@enum/TransactionTypeId' import { AdminUpdateContribution } from '@model/AdminUpdateContribution' import { Contribution, ContributionListResult } from '@model/Contribution' -import { Decay } from '@model/Decay' import { OpenCreation } from '@model/OpenCreation' import { UnconfirmedContribution } from '@model/UnconfirmedContribution' @@ -44,7 +43,7 @@ import { UpdateUnconfirmedContributionContext } from '@/interactions/updateUncon import { LogError } from '@/server/LogError' import { Context, getClientTimezoneOffset, getUser } from '@/server/context' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' -import { calculateDecay } from '@/util/decay' +import { calculateDecay, Decay } from 'shared' import { fullName } from '@/util/utilities' import { LOG4JS_RESOLVER_CATEGORY_NAME } from '@/graphql/resolver' diff --git a/backend/src/graphql/resolver/StatisticsResolver.ts b/backend/src/graphql/resolver/StatisticsResolver.ts index 6713cbb54..7ba54fafb 100644 --- a/backend/src/graphql/resolver/StatisticsResolver.ts +++ b/backend/src/graphql/resolver/StatisticsResolver.ts @@ -5,7 +5,7 @@ import { Authorized, FieldResolver, Query, Resolver } from 'type-graphql' import { CommunityStatistics, DynamicStatisticsFields } from '@model/CommunityStatistics' import { RIGHTS } from '@/auth/RIGHTS' -import { calculateDecay } from '@/util/decay' +import { calculateDecay } from 'shared' const db = AppDatabase.getInstance() diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 8d442b447..e75f79444 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -39,7 +39,7 @@ import { LogError } from '@/server/LogError' import { Context, getClientTimezoneOffset, getUser } from '@/server/context' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' import { TRANSACTION_LINK_LOCK } from '@/util/TRANSACTION_LINK_LOCK' -import { calculateDecay } from '@/util/decay' +import { calculateDecay } from 'shared' import { fullName } from '@/util/utilities' import { calculateBalance } from '@/util/validate' diff --git a/backend/src/logging/DecayLogging.view.ts b/backend/src/logging/DecayLogging.view.ts index f76f97821..713f069fa 100644 --- a/backend/src/logging/DecayLogging.view.ts +++ b/backend/src/logging/DecayLogging.view.ts @@ -1,9 +1,10 @@ import { AbstractLoggingView } from 'database' import { Decay } from '@/graphql/model/Decay' +import type { Decay as DecayType } from 'shared' export class DecayLoggingView extends AbstractLoggingView { - public constructor(private self: Decay) { + public constructor(private self: Decay | DecayType) { super() } diff --git a/backend/src/util/calculateSenderBalance.ts b/backend/src/util/calculateSenderBalance.ts index d2973c982..a01b1f15e 100644 --- a/backend/src/util/calculateSenderBalance.ts +++ b/backend/src/util/calculateSenderBalance.ts @@ -4,7 +4,7 @@ import { Decay } from '@model/Decay' import { getLastTransaction } from '@/graphql/resolver/util/getLastTransaction' -import { calculateDecay } from './decay' +import { calculateDecay } from 'shared' export async function calculateSenderBalance( userId: number, @@ -16,7 +16,7 @@ export async function calculateSenderBalance( return null } - const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time) + const decay = new Decay(calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)) const balance = decay.balance.add(amount.toString()) return { balance, lastTransactionId: lastTransaction.id, decay } diff --git a/backend/src/util/decay.ts b/backend/src/util/decay.ts deleted file mode 100644 index 96c7ddb4b..000000000 --- a/backend/src/util/decay.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Decimal } from 'decimal.js-light' - -import { Decay } from '@model/Decay' - -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 - -function decayFormula(value: Decimal, seconds: number): Decimal { - // TODO why do we need to convert this here to a stting to work properly? - return value.mul( - new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds).toString(), - ) -} - -function calculateDecay( - amount: Decimal, - from: Date, - to: Date, - startBlock: Date = DECAY_START_TIME, -): Decay { - const fromMs = from.getTime() - const toMs = to.getTime() - const startBlockMs = startBlock.getTime() - - if (toMs < fromMs) { - throw new LogError('calculateDecay: to < from, reverse decay calculation is invalid') - } - - // Initialize with no decay - const decay: Decay = { - balance: amount, - decay: new Decimal(0), - roundedDecay: new Decimal(0), - start: null, - end: null, - duration: null, - } - - // decay started after end date; no decay - if (startBlockMs > toMs) { - return decay - } - // decay started before start date; decay for full duration - if (startBlockMs < fromMs) { - decay.start = from - decay.duration = (toMs - fromMs) / 1000 - } - // decay started between start and end date; decay from decay start till end date - else { - decay.start = startBlock - decay.duration = (toMs - startBlockMs) / 1000 - } - - decay.end = to - decay.balance = decayFormula(amount, decay.duration) - decay.decay = decay.balance.minus(amount) - decay.roundedDecay = amount - .toDecimalPlaces(2, Decimal.ROUND_DOWN) - .minus(decay.balance.toDecimalPlaces(2, Decimal.ROUND_DOWN).toString()) - .mul(-1) - return decay -} - -export { decayFormula, calculateDecay } diff --git a/backend/src/util/validate.ts b/backend/src/util/validate.ts index d0e493ff5..1dd8a4529 100644 --- a/backend/src/util/validate.ts +++ b/backend/src/util/validate.ts @@ -7,7 +7,7 @@ import { Decay } from '@model/Decay' import { getLastTransaction } from '@/graphql/resolver/util/getLastTransaction' import { transactionLinkSummary } from '@/graphql/resolver/util/transactionLinkSummary' -import { calculateDecay } from './decay' +import { calculateDecay } from 'shared' function isStringBoolean(value: string): boolean { const lowerValue = value.toLowerCase() @@ -36,7 +36,7 @@ async function calculateBalance( return null } - const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time) + const decay = new Decay(calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)) const balance = decay.balance.add(amount.toString()) const { sumHoldAvailableAmount } = await transactionLinkSummary(userId, time) diff --git a/backend/src/util/virtualTransactions.ts b/backend/src/util/virtualTransactions.ts index b80642dfe..dabaee264 100644 --- a/backend/src/util/virtualTransactions.ts +++ b/backend/src/util/virtualTransactions.ts @@ -6,7 +6,7 @@ import { TransactionTypeId } from '@enum/TransactionTypeId' import { Transaction } from '@model/Transaction' import { User } from '@model/User' -import { calculateDecay } from './decay' +import { calculateDecay } from 'shared' const defaultModelFunctions = { hasId: function (): boolean { diff --git a/bun.lock b/bun.lock index a6b408a40..18cacf7bf 100644 --- a/bun.lock +++ b/bun.lock @@ -420,6 +420,7 @@ "name": "shared", "version": "2.6.0", "dependencies": { + "decimal.js-light": "^2.5.1", "esbuild": "^0.25.2", "log4js": "^6.9.1", "zod": "^3.25.61", diff --git a/federation/src/config/schema.ts b/federation/src/config/schema.ts index 82e05d3bc..07f5ed48f 100644 --- a/federation/src/config/schema.ts +++ b/federation/src/config/schema.ts @@ -1,5 +1,4 @@ import { - DECAY_START_TIME, GRAPHIQL, LOG4JS_CONFIG_PLACEHOLDER, LOG_FILES_BASE_PATH, @@ -10,7 +9,6 @@ import { import Joi from 'joi' export const schema = Joi.object({ - DECAY_START_TIME, GRAPHIQL, LOG4JS_CONFIG_PLACEHOLDER, LOG_FILES_BASE_PATH, diff --git a/federation/src/graphql/api/1_0/model/Decay.ts b/federation/src/graphql/api/1_0/model/Decay.ts index a32b96c13..f72fade3a 100644 --- a/federation/src/graphql/api/1_0/model/Decay.ts +++ b/federation/src/graphql/api/1_0/model/Decay.ts @@ -1,14 +1,6 @@ import { Decimal } from 'decimal.js-light' import { Field, Int, ObjectType } from 'type-graphql' - -interface DecayInterface { - balance: Decimal - decay: Decimal - roundedDecay: Decimal - start: Date | null - end: Date | null - duration: number | null -} +import { Decay as DecayInterface} from 'shared' @ObjectType() export class Decay { diff --git a/federation/src/graphql/api/1_0/util/calculateRecipientBalance.ts b/federation/src/graphql/api/1_0/util/calculateRecipientBalance.ts index 198c89289..ead2cc641 100644 --- a/federation/src/graphql/api/1_0/util/calculateRecipientBalance.ts +++ b/federation/src/graphql/api/1_0/util/calculateRecipientBalance.ts @@ -1,4 +1,4 @@ -import { calculateDecay } from '@/graphql/util/decay' +import { calculateDecay } from 'shared' import { getLastTransaction } from '@/graphql/util/getLastTransaction' import { Decimal } from 'decimal.js-light' import { Decay } from '../model/Decay' @@ -13,7 +13,7 @@ export async function calculateRecipientBalance( return null } - const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time) + const decay = new Decay(calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)) const balance = decay.balance.add(amount.toString()) diff --git a/federation/src/graphql/util/decay.test.ts b/federation/src/graphql/util/decay.test.ts deleted file mode 100644 index f419982ac..000000000 --- a/federation/src/graphql/util/decay.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Decimal } from 'decimal.js-light' - -import { calculateDecay, decayFormula } from './decay' - -describe('utils/decay', () => { - describe('decayFormula', () => { - it('has base 0.99999997802044727', () => { - const amount = new Decimal(1.0) - const seconds = 1 - // TODO: toString() was required, we could not compare two decimals - expect(decayFormula(amount, seconds).toString()).toBe('0.999999978035040489732012') - }) - it('has correct backward calculation', () => { - const amount = new Decimal(1.0) - const seconds = -1 - expect(decayFormula(amount, seconds).toString()).toBe('1.000000021964959992727444') - }) - // we get pretty close, but not exact here, skipping - - it.skip('has correct forward calculation', () => { - const amount = new Decimal(1.0).div( - new Decimal('0.99999997803504048973201202316767079413460520837376'), - ) - const seconds = 1 - expect(decayFormula(amount, seconds).toString()).toBe('1.0') - }) - }) - it('has base 0.99999997802044727', () => { - const now = new Date() - now.setSeconds(1) - const oneSecondAgo = new Date(now.getTime()) - oneSecondAgo.setSeconds(0) - expect(calculateDecay(new Decimal(1.0), oneSecondAgo, now).balance.toString()).toBe( - '0.999999978035040489732012', - ) - }) - - it('returns input amount when from and to is the same', () => { - const now = new Date() - expect(calculateDecay(new Decimal(100.0), now, now).balance.toString()).toBe('100') - }) -}) diff --git a/shared/package.json b/shared/package.json index 31c7404bc..31190ec7c 100644 --- a/shared/package.json +++ b/shared/package.json @@ -31,6 +31,7 @@ "uuid": "^8.3.2" }, "dependencies": { + "decimal.js-light": "^2.5.1", "esbuild": "^0.25.2", "log4js": "^6.9.1", "zod": "^3.25.61" diff --git a/shared/src/const/index.ts b/shared/src/const/index.ts new file mode 100644 index 000000000..6976b019a --- /dev/null +++ b/shared/src/const/index.ts @@ -0,0 +1,2 @@ +export const DECAY_START_TIME = new Date('2021-05-13T17:46:31Z') +export const LOG4JS_BASE_CATEGORY_NAME = 'shared' \ No newline at end of file diff --git a/shared/src/index.ts b/shared/src/index.ts index d252da56f..936131a69 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -1,2 +1,3 @@ export * from './schema' -export * from './enum' \ No newline at end of file +export * from './enum' +export * from './logic' diff --git a/backend/src/util/decay.test.ts b/shared/src/logic/decay.test.ts similarity index 100% rename from backend/src/util/decay.test.ts rename to shared/src/logic/decay.test.ts diff --git a/federation/src/graphql/util/decay.ts b/shared/src/logic/decay.ts similarity index 57% rename from federation/src/graphql/util/decay.ts rename to shared/src/logic/decay.ts index 9f2908b71..f75146515 100644 --- a/federation/src/graphql/util/decay.ts +++ b/shared/src/logic/decay.ts @@ -1,34 +1,47 @@ import { Decimal } from 'decimal.js-light' -import { LogError } from '@/server/LogError' -import { DECAY_START_TIME } from 'config-schema' -import { Decay } from '../api/1_0/model/Decay' +import { getLogger } from 'log4js' +import { LOG4JS_LOGIC_CATEGORY } from '.' +import { DECAY_START_TIME } from '../const' + +const logger = getLogger(`${LOG4JS_LOGIC_CATEGORY}.DecayLogic`) Decimal.set({ precision: 25, rounding: Decimal.ROUND_HALF_UP, }) -// TODO: externalize all those definitions and functions into an external decay library -function decayFormula(value: Decimal, seconds: number): Decimal { +export interface Decay { + balance: Decimal + decay: Decimal + roundedDecay: Decimal + start: Date | null + end: Date | null + duration: number | null +} + +export function decayFormula(value: Decimal, seconds: number): Decimal { // TODO why do we need to convert this here to a string to work properly? + // chatgpt: We convert to string here to avoid precision loss: + // .pow(seconds) can internally round the result, especially for large values of `seconds`. + // Using .toString() ensures full precision is preserved in the multiplication. return value.mul( new Decimal('0.99999997803504048973201202316767079413460520837376').pow(seconds).toString(), ) } -function calculateDecay( +export function calculateDecay( amount: Decimal, from: Date, - to: Date, - startBlock: Date = DECAY_START_TIME, + to: Date ): Decay { const fromMs = from.getTime() const toMs = to.getTime() - const startBlockMs = startBlock.getTime() + const startBlockMs = DECAY_START_TIME.getTime() if (toMs < fromMs) { - throw new LogError('calculateDecay: to < from, reverse decay calculation is invalid') + logger.error('calculateDecay: to < from, reverse decay calculation is invalid', from, to) + throw new Error('calculateDecay: to < from, reverse decay calculation is invalid') } // Initialize with no decay @@ -52,7 +65,7 @@ function calculateDecay( } // decay started between start and end date; decay from decay start till end date else { - decay.start = startBlock + decay.start = DECAY_START_TIME decay.duration = (toMs - startBlockMs) / 1000 } @@ -65,5 +78,3 @@ function calculateDecay( .mul(-1) return decay } - -export { decayFormula, calculateDecay } diff --git a/shared/src/logic/index.ts b/shared/src/logic/index.ts new file mode 100644 index 000000000..7056d5818 --- /dev/null +++ b/shared/src/logic/index.ts @@ -0,0 +1,4 @@ +import { LOG4JS_BASE_CATEGORY_NAME } from '../const' + +export const LOG4JS_LOGIC_CATEGORY = `${LOG4JS_BASE_CATEGORY_NAME}.logic` +export { calculateDecay, Decay } from './decay' \ No newline at end of file From 3afecc0c182b5e9802a60014dd4e793d585dcc61 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 23 Jun 2025 13:59:15 +0200 Subject: [PATCH 33/46] fix --- core/src/validation/user.ts | 1 - shared/src/index.ts | 2 +- shared/src/logic/decay.test.ts | 2 ++ shared/src/logic/index.ts | 1 - 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/validation/user.ts b/core/src/validation/user.ts index 945b1c818..b188e5d54 100644 --- a/core/src/validation/user.ts +++ b/core/src/validation/user.ts @@ -15,7 +15,6 @@ export async function validateAlias(alias: string): Promise { logger.warn('invalid alias', alias, (err as ZodError).errors) throw new Error((err as ZodError).errors[0].message) } - console.log(err) throw err } diff --git a/shared/src/index.ts b/shared/src/index.ts index 936131a69..4c6acd552 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -1,3 +1,3 @@ export * from './schema' export * from './enum' -export * from './logic' +export * from './logic/decay' diff --git a/shared/src/logic/decay.test.ts b/shared/src/logic/decay.test.ts index f419982ac..1db94c4fe 100644 --- a/shared/src/logic/decay.test.ts +++ b/shared/src/logic/decay.test.ts @@ -10,6 +10,7 @@ describe('utils/decay', () => { // TODO: toString() was required, we could not compare two decimals expect(decayFormula(amount, seconds).toString()).toBe('0.999999978035040489732012') }) + it('has correct backward calculation', () => { const amount = new Decimal(1.0) const seconds = -1 @@ -25,6 +26,7 @@ describe('utils/decay', () => { expect(decayFormula(amount, seconds).toString()).toBe('1.0') }) }) + it('has base 0.99999997802044727', () => { const now = new Date() now.setSeconds(1) diff --git a/shared/src/logic/index.ts b/shared/src/logic/index.ts index 7056d5818..fca61a4b1 100644 --- a/shared/src/logic/index.ts +++ b/shared/src/logic/index.ts @@ -1,4 +1,3 @@ import { LOG4JS_BASE_CATEGORY_NAME } from '../const' export const LOG4JS_LOGIC_CATEGORY = `${LOG4JS_BASE_CATEGORY_NAME}.logic` -export { calculateDecay, Decay } from './decay' \ No newline at end of file From 7b4db18c524862353b74d153e96ff068e5a2d51e Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 23 Jun 2025 14:06:21 +0200 Subject: [PATCH 34/46] use same name --- backend/src/logging/DecayLogging.view.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/logging/DecayLogging.view.ts b/backend/src/logging/DecayLogging.view.ts index 713f069fa..538beecd5 100644 --- a/backend/src/logging/DecayLogging.view.ts +++ b/backend/src/logging/DecayLogging.view.ts @@ -1,10 +1,10 @@ import { AbstractLoggingView } from 'database' import { Decay } from '@/graphql/model/Decay' -import type { Decay as DecayType } from 'shared' +import type { Decay as DecayInterface } from 'shared' export class DecayLoggingView extends AbstractLoggingView { - public constructor(private self: Decay | DecayType) { + public constructor(private self: Decay | DecayInterface) { super() } From b1c1b35bec1979ac33a6559d5e6f8e243e678669 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 25 Jun 2025 12:03:20 +0200 Subject: [PATCH 35/46] remove comments --- backend/src/graphql/resolver/UserResolver.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 14be59307..46c9c44ed 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -184,7 +184,6 @@ export class UserResolver { await delay(650 + Math.floor(Math.random() * 101) - 50) throw e } - // TODO: discuss need we logging all this cases? if (dbUser.deletedAt) { logger.warn('login failed, user was deleted') throw new Error('This user was permanently deleted. Contact support for questions') @@ -1173,7 +1172,6 @@ export async function findUserByEmail(email: string): Promise { } catch (e) { const logger = createLogger() if (e instanceof EntityNotFoundError || (e as Error).name === 'EntityNotFoundError') { - // TODO: discuss if it is ok to print email in log for this case logger.warn(`findUserByEmail failed, user with email=${email} not found`) } else { logger.error(`findUserByEmail failed, unknown error: ${e}`) From 9fc6a63bea6bc2c9732c8594fb9fd8f4d4fc3306 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 25 Jun 2025 12:29:40 +0200 Subject: [PATCH 36/46] fix 2 assumptions --- shared/src/schema/user.schema.test.ts | 15 +++++++++++++++ shared/src/schema/user.schema.ts | 1 - 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/shared/src/schema/user.schema.test.ts b/shared/src/schema/user.schema.test.ts index f29bb2af5..d8f91cb1e 100644 --- a/shared/src/schema/user.schema.test.ts +++ b/shared/src/schema/user.schema.test.ts @@ -41,6 +41,21 @@ describe('validate alias', () => { }) }) + describe('alias length', () => { + it('2 characters is not ok', () => { + expect(() => aliasSchema.parse('Bi')).toThrowError() + }) + it('3 characters is ok', () => { + expect(() => aliasSchema.parse('Bib')).not.toThrowError() + }) + it('20 characters is ok', () => { + expect(() => aliasSchema.parse('BibiBloxbergMondLich')).not.toThrowError() + }) + it('21 characters is not ok', () => { + expect(() => aliasSchema.parse('BibiBloxbergZauberwald')).toThrowError() + }) + }) + describe('alias is a reserved word with uppercase characters', () => { it('throws and logs an error', () => { expect(() => aliasSchema.parse('Admin')).toThrowError(expect.objectContaining( diff --git a/shared/src/schema/user.schema.ts b/shared/src/schema/user.schema.ts index 4a1c51d74..90897a637 100644 --- a/shared/src/schema/user.schema.ts +++ b/shared/src/schema/user.schema.ts @@ -14,7 +14,6 @@ const RESERVED_ALIAS = [ 'support', 'temp', 'tmp', - 'tmp', 'user', 'usr', 'var', From 85fa9976488d16fa8da8c62af47b995662e5b25f Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 25 Jun 2025 13:01:46 +0200 Subject: [PATCH 37/46] more schemas --- shared/src/schema/user.schema.test.ts | 45 ++++++++++++++++++++------- shared/src/schema/user.schema.ts | 17 ++++++++++ 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/shared/src/schema/user.schema.test.ts b/shared/src/schema/user.schema.test.ts index d8f91cb1e..015309dfd 100644 --- a/shared/src/schema/user.schema.test.ts +++ b/shared/src/schema/user.schema.test.ts @@ -1,16 +1,7 @@ -import { aliasSchema } from './user.schema' -import { describe, it, expect, beforeEach, mock, jest } from 'bun:test' - - -mock.module('database', () => ({ - aliasExists: jest.fn(), -})) +import { aliasSchema, firstNameSchema } from './user.schema' +import { describe, it, expect } from 'bun:test' describe('validate alias', () => { - beforeEach(() => { - jest.clearAllMocks() - }) - describe('alias contains invalid characters', () => { it('throws and logs an error', () => { expect(() => aliasSchema.parse('Bibi.Bloxberg')).toThrowError(expect.objectContaining( @@ -104,3 +95,35 @@ describe('validate alias', () => { }) }) }) + +describe('validate first name', () => { + describe('first name contains invalid characters', () => { + it('throws and logs an error', () => { + expect(() => firstNameSchema.parse('')).toThrowError(expect.objectContaining( + expect.arrayContaining([ + expect.objectContaining({ + origin: 'string', + code: 'invalid_format', + format: 'regex', + message: 'Invalid characters in first name', + path: [], + }) + ]) + )) + }) + }) + it('use greek symbols', () => { + expect(() => firstNameSchema.parse('Αλέξανδρος')).not.toThrowError() + }) + it('use korean symbols', () => { + expect(() => firstNameSchema.parse('김민수')).not.toThrowError() + }) + // TODO: use min length depending of language, because in asiatic languages first and/or last names can have only one character + it.skip('use japanese symbols', () => { + expect(() => firstNameSchema.parse('田中')).not.toThrowError() + }) + // TODO: fix this + it.skip('use chinese symbols', () => { + expect(() => firstNameSchema.parse('张三')).not.toThrowError() + }) +}) diff --git a/shared/src/schema/user.schema.ts b/shared/src/schema/user.schema.ts index 90897a637..494ee03b6 100644 --- a/shared/src/schema/user.schema.ts +++ b/shared/src/schema/user.schema.ts @@ -1,6 +1,11 @@ import { string } from 'zod' export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/ +// \p{L} = a character from every alphabet (latin, greek, cyrillic, etc.) +// first a character or ' is expected +// then all without the last a character, space, apostrophe or hyphen is expected +// last a character is expected +export const VALID_NAME_REGEX = /^[\p{L}'][ \p{L}'-_]*[\p{L}]$/u const RESERVED_ALIAS = [ 'admin', @@ -26,3 +31,15 @@ export const aliasSchema = string() .refine((val) => !RESERVED_ALIAS.includes(val.toLowerCase()), { message: 'Given alias is not allowed', }) + +// TODO: use this schemas in backend, think about case which currently not fullfil the regex +// (some user start there name with : ) +export const firstNameSchema = string() + .min(3, 'First name is too short') + .max(255, 'First name is too long') + .regex(VALID_NAME_REGEX) + +export const lastNameSchema = string() + .min(2, 'Last name is too short') + .max(255, 'Last name is too long') + .regex(VALID_NAME_REGEX) \ No newline at end of file From 26f51a1895bb9c6070576ff1fe588f886b5e7633 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 25 Jun 2025 13:04:51 +0200 Subject: [PATCH 38/46] use explicit biome version --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4ff2632e4..bd048b752 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -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: | From 7c325b6feabb77ed5de18110f5ec7d9a2a5f8bd9 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 26 Jun 2025 07:43:02 +0200 Subject: [PATCH 39/46] use base const and strings for logger categories for the time beeing --- backend/src/apis/HttpRequest.ts | 4 ++-- backend/src/apis/KlicktippController.ts | 4 ++-- backend/src/apis/dltConnector/DltConnectorClient.ts | 4 ++-- backend/src/apis/gms/ExportUsers.ts | 4 ++-- backend/src/apis/gms/GmsClient.ts | 4 ++-- backend/src/apis/gms/index.ts | 3 --- backend/src/apis/humhub/ExportUsers.ts | 4 ++-- backend/src/apis/humhub/HumHubClient.ts | 4 ++-- backend/src/apis/humhub/index.ts | 3 --- backend/src/apis/humhub/syncUser.ts | 4 ++-- backend/src/apis/index.ts | 3 --- backend/src/apis/openai/OpenaiClient.ts | 4 ++-- backend/src/emails/index.ts | 3 --- backend/src/emails/sendEmailTranslated.test.ts | 4 ++-- backend/src/emails/sendEmailTranslated.ts | 4 ++-- backend/src/emails/sendEmailVariants.test.ts | 5 +---- backend/src/federation/authenticateCommunities.ts | 4 ++-- .../src/federation/client/1_0/AuthenticationClient.ts | 4 ++-- backend/src/federation/client/1_0/FederationClient.ts | 4 ++-- backend/src/federation/client/1_0/SendCoinsClient.ts | 4 ++-- backend/src/federation/client/1_0/index.ts | 3 --- backend/src/federation/index.ts | 3 --- backend/src/federation/validateCommunities.test.ts | 9 ++++----- backend/src/federation/validateCommunities.ts | 4 ++-- backend/src/graphql/resolver/BalanceResolver.ts | 4 ++-- .../src/graphql/resolver/CommunityResolver.test.ts | 4 +--- .../resolver/ContributionMessageResolver.test.ts | 8 +++----- .../graphql/resolver/ContributionMessageResolver.ts | 4 ++-- .../src/graphql/resolver/ContributionResolver.test.ts | 2 +- backend/src/graphql/resolver/ContributionResolver.ts | 4 ++-- backend/src/graphql/resolver/GdtResolver.ts | 4 ++-- .../src/graphql/resolver/KlicktippResolver.test.ts | 4 ++-- .../src/graphql/resolver/ProjectBrandingResolver.ts | 4 ++-- .../src/graphql/resolver/TransactionLinkResolver.ts | 4 ++-- backend/src/graphql/resolver/TransactionResolver.ts | 4 ++-- backend/src/graphql/resolver/UserResolver.test.ts | 11 +++-------- backend/src/graphql/resolver/UserResolver.ts | 4 ++-- backend/src/graphql/resolver/index.ts | 3 --- .../resolver/util/authenticateGmsUserPlayground.ts | 4 ++-- .../resolver/util/compareGmsRelevantUserSettings.ts | 4 ++-- backend/src/graphql/resolver/util/creations.ts | 4 ++-- .../src/graphql/resolver/util/getKlicktippState.ts | 4 ++-- backend/src/graphql/resolver/util/index.ts | 3 --- .../src/graphql/resolver/util/processXComSendCoins.ts | 4 ++-- .../util/sendTransactionsToDltConnector.test.ts | 4 ++-- .../resolver/util/sendTransactionsToDltConnector.ts | 4 ++-- backend/src/graphql/resolver/util/sendUserToGms.ts | 4 ++-- .../resolver/util/settlePendingSenderTransaction.ts | 4 ++-- backend/src/graphql/resolver/util/storeForeignUser.ts | 4 ++-- backend/src/graphql/resolver/util/syncHumhub.test.ts | 4 ++-- backend/src/graphql/resolver/util/syncHumhub.ts | 4 ++-- backend/src/interactions/index.ts | 3 --- .../AbstractUnconfirmedContribution.role.ts | 4 ++-- backend/src/seeds/factory/user.ts | 4 ++-- dht-node/src/dht_node/index.test.ts | 10 ++++------ dht-node/src/dht_node/index.ts | 3 +-- federation/src/client/1_0/AuthenticationClient.ts | 4 ++-- federation/src/client/1_0/index.ts | 3 --- federation/src/graphql/api/1_0/index.ts | 3 --- .../api/1_0/resolver/AuthenticationResolver.ts | 4 ++-- .../1_0/resolver/PublicCommunityInfoResolver.test.ts | 3 +-- .../api/1_0/resolver/PublicCommunityInfoResolver.ts | 4 ++-- .../api/1_0/resolver/PublicKeyResolver.test.ts | 2 +- .../src/graphql/api/1_0/resolver/PublicKeyResolver.ts | 4 ++-- .../api/1_0/resolver/SendCoinsResolver.test.ts | 2 +- .../src/graphql/api/1_0/resolver/SendCoinsResolver.ts | 4 ++-- federation/src/graphql/api/1_0/resolver/index.ts | 3 --- .../src/graphql/api/1_0/util/authenticateCommunity.ts | 4 ++-- federation/src/graphql/api/1_0/util/index.ts | 3 --- .../api/1_0/util/revertSettledReceiveTransaction.ts | 4 ++-- .../api/1_0/util/settlePendingReceiveTransaction.ts | 4 ++-- .../src/graphql/api/1_0/util/storeForeignUser.ts | 4 ++-- .../src/graphql/api/1_1/resolver/PublicKeyResolver.ts | 4 ++-- federation/src/graphql/api/1_1/resolver/index.ts | 3 --- federation/src/graphql/api/index.ts | 3 --- federation/src/graphql/api/schema.ts | 4 ++-- federation/src/graphql/index.ts | 3 --- federation/src/graphql/util/checkTradingLevel.ts | 4 ++-- federation/src/graphql/util/index.ts | 3 --- 79 files changed, 123 insertions(+), 191 deletions(-) delete mode 100644 backend/src/apis/gms/index.ts delete mode 100644 backend/src/apis/humhub/index.ts delete mode 100644 backend/src/apis/index.ts delete mode 100644 backend/src/emails/index.ts delete mode 100644 backend/src/federation/client/1_0/index.ts delete mode 100644 backend/src/federation/index.ts delete mode 100644 backend/src/graphql/resolver/index.ts delete mode 100644 backend/src/graphql/resolver/util/index.ts delete mode 100644 backend/src/interactions/index.ts delete mode 100644 federation/src/client/1_0/index.ts delete mode 100644 federation/src/graphql/api/1_0/index.ts delete mode 100644 federation/src/graphql/api/1_0/resolver/index.ts delete mode 100644 federation/src/graphql/api/1_0/util/index.ts delete mode 100644 federation/src/graphql/api/1_1/resolver/index.ts delete mode 100644 federation/src/graphql/api/index.ts delete mode 100644 federation/src/graphql/index.ts delete mode 100644 federation/src/graphql/util/index.ts diff --git a/backend/src/apis/HttpRequest.ts b/backend/src/apis/HttpRequest.ts index 0b63c4af9..9a775ee28 100644 --- a/backend/src/apis/HttpRequest.ts +++ b/backend/src/apis/HttpRequest.ts @@ -1,10 +1,10 @@ import axios from 'axios' -import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { LogError } from '@/server/LogError' import { getLogger } from 'log4js' -const logger = getLogger(`${LOG4JS_APIS_CATEGORY_NAME}.HttpRequest`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.HttpRequest`) import { httpAgent, httpsAgent } from './ConnectionAgents' diff --git a/backend/src/apis/KlicktippController.ts b/backend/src/apis/KlicktippController.ts index 5fe55fb2e..5a33df9ae 100644 --- a/backend/src/apis/KlicktippController.ts +++ b/backend/src/apis/KlicktippController.ts @@ -1,10 +1,10 @@ -import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { CONFIG } from '@/config' import KlicktippConnector from 'klicktipp-api' import { getLogger } from 'log4js' const klicktippConnector = new KlicktippConnector() -const logger = getLogger(`${LOG4JS_APIS_CATEGORY_NAME}.KlicktippController`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.KlicktippController`) export const subscribe = async ( email: string, diff --git a/backend/src/apis/dltConnector/DltConnectorClient.ts b/backend/src/apis/dltConnector/DltConnectorClient.ts index 8eb8de3fb..6f5b437de 100644 --- a/backend/src/apis/dltConnector/DltConnectorClient.ts +++ b/backend/src/apis/dltConnector/DltConnectorClient.ts @@ -1,8 +1,8 @@ import { Transaction as DbTransaction } from 'database' import { GraphQLClient, gql } from 'graphql-request' -import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis/index' import { CONFIG } from '@/config' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId' import { LogError } from '@/server/LogError' import { getLogger } from 'log4js' @@ -10,7 +10,7 @@ import { getLogger } from 'log4js' import { TransactionResult } from './model/TransactionResult' import { UserIdentifier } from './model/UserIdentifier' -const logger = getLogger(`${LOG4JS_APIS_CATEGORY_NAME}.dltConnector`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.dltConnector`) const sendTransaction = gql` mutation ($input: TransactionInput!) { diff --git a/backend/src/apis/gms/ExportUsers.ts b/backend/src/apis/gms/ExportUsers.ts index aa210707c..36eee264c 100644 --- a/backend/src/apis/gms/ExportUsers.ts +++ b/backend/src/apis/gms/ExportUsers.ts @@ -1,7 +1,7 @@ import { User as DbUser } from 'database' // import { createTestClient } from 'apollo-server-testing' -import { LOG4JS_GMS_CATEGORY_NAME } from '@/apis/gms/index' +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' @@ -12,7 +12,7 @@ import { initLogging } from '@/server/logger' import { AppDatabase } from 'database' import { getLogger } from 'log4js' -const logger = getLogger(`${LOG4JS_GMS_CATEGORY_NAME}.ExportUsers`) +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 diff --git a/backend/src/apis/gms/GmsClient.ts b/backend/src/apis/gms/GmsClient.ts index 0dd5eccc5..bb4fce2e7 100644 --- a/backend/src/apis/gms/GmsClient.ts +++ b/backend/src/apis/gms/GmsClient.ts @@ -1,7 +1,7 @@ import axios from 'axios' import { httpAgent, httpsAgent } from '@/apis/ConnectionAgents' -import { LOG4JS_GMS_CATEGORY_NAME } from '@/apis/gms/index' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' import { ensureUrlEndsWithSlash } from '@/util/utilities' @@ -9,7 +9,7 @@ import { getLogger } from 'log4js' import { GmsUser } from './model/GmsUser' -const logger = getLogger(`${LOG4JS_GMS_CATEGORY_NAME}.GmsClient`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.gms.GmsClient`) /* export async function communityList(): Promise { diff --git a/backend/src/apis/gms/index.ts b/backend/src/apis/gms/index.ts deleted file mode 100644 index 620aaca4b..000000000 --- a/backend/src/apis/gms/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis' - -export const LOG4JS_GMS_CATEGORY_NAME = `${LOG4JS_APIS_CATEGORY_NAME}.gms` diff --git a/backend/src/apis/humhub/ExportUsers.ts b/backend/src/apis/humhub/ExportUsers.ts index 5eb3f8d6c..99ae859eb 100644 --- a/backend/src/apis/humhub/ExportUsers.ts +++ b/backend/src/apis/humhub/ExportUsers.ts @@ -1,7 +1,7 @@ import { AppDatabase, User } from 'database' import { IsNull, Not } from 'typeorm' -import { LOG4JS_HUMHUB_CATEGORY_NAME } from '@/apis/humhub/index' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { initLogging } from '@/server/logger' import { getLogger } from 'log4js' import { HumHubClient } from './HumHubClient' @@ -11,7 +11,7 @@ import { ExecutedHumhubAction, syncUser } from './syncUser' const USER_BULK_SIZE = 20 const HUMHUB_BULK_SIZE = 50 -const logger = getLogger(`${LOG4JS_HUMHUB_CATEGORY_NAME}.ExportUsers`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.humhub.ExportUsers`) function getUsersPage(page: number, limit: number): Promise<[User[], number]> { return User.findAndCount({ diff --git a/backend/src/apis/humhub/HumHubClient.ts b/backend/src/apis/humhub/HumHubClient.ts index ff4529234..0cdc683c5 100644 --- a/backend/src/apis/humhub/HumHubClient.ts +++ b/backend/src/apis/humhub/HumHubClient.ts @@ -5,7 +5,7 @@ import { IRequestOptions, IRestResponse, RestClient } from 'typed-rest-client' import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' -import { LOG4JS_HUMHUB_CATEGORY_NAME } from '@/apis/humhub/index' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { getLogger } from 'log4js' import { PostUserLoggingView } from './logging/PostUserLogging.view' import { GetUser } from './model/GetUser' @@ -14,7 +14,7 @@ import { Space } from './model/Space' import { SpacesResponse } from './model/SpacesResponse' import { UsersResponse } from './model/UsersResponse' -const logger = getLogger(`${LOG4JS_HUMHUB_CATEGORY_NAME}.HumHubClient`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.humhub.HumHubClient`) /** * HumHubClient as singleton class diff --git a/backend/src/apis/humhub/index.ts b/backend/src/apis/humhub/index.ts deleted file mode 100644 index dda319145..000000000 --- a/backend/src/apis/humhub/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis' - -export const LOG4JS_HUMHUB_CATEGORY_NAME = `${LOG4JS_APIS_CATEGORY_NAME}.humhub` diff --git a/backend/src/apis/humhub/syncUser.ts b/backend/src/apis/humhub/syncUser.ts index 257f16683..22ecde87c 100644 --- a/backend/src/apis/humhub/syncUser.ts +++ b/backend/src/apis/humhub/syncUser.ts @@ -1,6 +1,6 @@ import { User } from 'database' -import { LOG4JS_HUMHUB_CATEGORY_NAME } from '@/apis/humhub/index' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { LogError } from '@/server/LogError' import { getLogger } from 'log4js' @@ -9,7 +9,7 @@ import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' import { GetUser } from './model/GetUser' import { PostUser } from './model/PostUser' -const logger = getLogger(`${LOG4JS_HUMHUB_CATEGORY_NAME}.syncUser`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.humhub.syncUser`) export enum ExecutedHumhubAction { UPDATE, diff --git a/backend/src/apis/index.ts b/backend/src/apis/index.ts deleted file mode 100644 index c4e45826d..000000000 --- a/backend/src/apis/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' - -export const LOG4JS_APIS_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.apis` diff --git a/backend/src/apis/openai/OpenaiClient.ts b/backend/src/apis/openai/OpenaiClient.ts index dd447c7ad..35744a7dd 100644 --- a/backend/src/apis/openai/OpenaiClient.ts +++ b/backend/src/apis/openai/OpenaiClient.ts @@ -7,10 +7,10 @@ import { CONFIG } from '@/config' import { Message as MessageModel } from './model/Message' -import { LOG4JS_APIS_CATEGORY_NAME } from '@/apis' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { getLogger } from 'log4js' -const logger = getLogger(`${LOG4JS_APIS_CATEGORY_NAME}.openai.OpenaiClient`) +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. diff --git a/backend/src/emails/index.ts b/backend/src/emails/index.ts deleted file mode 100644 index d529ac28d..000000000 --- a/backend/src/emails/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' - -export const LOG4JS_EMAILS_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.emails` diff --git a/backend/src/emails/sendEmailTranslated.test.ts b/backend/src/emails/sendEmailTranslated.test.ts index b4da5fbfb..103296080 100644 --- a/backend/src/emails/sendEmailTranslated.test.ts +++ b/backend/src/emails/sendEmailTranslated.test.ts @@ -5,10 +5,10 @@ import { i18n } from '@test/testSetup' import { CONFIG } from '@/config' import { getLogger } from 'config-schema/test/testSetup' -import { LOG4JS_EMAILS_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { sendEmailTranslated } from './sendEmailTranslated' -const logger = getLogger(`${LOG4JS_EMAILS_CATEGORY_NAME}.sendEmailTranslated`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`) const testMailServerHost = 'localhost' const testMailServerPort = 1025 diff --git a/backend/src/emails/sendEmailTranslated.ts b/backend/src/emails/sendEmailTranslated.ts index 30050abf3..5b95cd7ad 100644 --- a/backend/src/emails/sendEmailTranslated.ts +++ b/backend/src/emails/sendEmailTranslated.ts @@ -5,10 +5,10 @@ import i18n from 'i18n' import { createTransport } from 'nodemailer' import { CONFIG } from '@/config' -import { LOG4JS_EMAILS_CATEGORY_NAME } from '@/emails' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { getLogger } from 'log4js' -const logger = getLogger(`${LOG4JS_EMAILS_CATEGORY_NAME}.sendEmailTranslated`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.emails.sendEmailTranslated`) export const sendEmailTranslated = async ({ receiver, diff --git a/backend/src/emails/sendEmailVariants.test.ts b/backend/src/emails/sendEmailVariants.test.ts index 300885472..74eb940ed 100644 --- a/backend/src/emails/sendEmailVariants.test.ts +++ b/backend/src/emails/sendEmailVariants.test.ts @@ -5,9 +5,6 @@ import { DataSource } from 'typeorm' import { testEnvironment } from '@test/helpers' import { i18n as localization } from '@test/testSetup' import { getLogger } from 'config-schema/test/testSetup' -import { LOG4JS_EMAILS_CATEGORY_NAME } from '.' - -const logger = getLogger(`${LOG4JS_EMAILS_CATEGORY_NAME}.sendEmailTranslated`) import { CONFIG } from '@/config' @@ -57,7 +54,7 @@ let testEnv: { } beforeAll(async () => { - testEnv = await testEnvironment(logger, localization) + testEnv = await testEnvironment(getLogger('apollo'), localization) con = testEnv.con }) diff --git a/backend/src/federation/authenticateCommunities.ts b/backend/src/federation/authenticateCommunities.ts index 2a1c6f51d..d2d7b691d 100644 --- a/backend/src/federation/authenticateCommunities.ts +++ b/backend/src/federation/authenticateCommunities.ts @@ -6,12 +6,12 @@ import { CONFIG } from '@/config' import { AuthenticationClient as V1_0_AuthenticationClient } from '@/federation/client/1_0/AuthenticationClient' import { ensureUrlEndsWithSlash } from '@/util/utilities' -import { LOG4JS_FEDERATION_CATEGORY_NAME } from '@/federation' 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_FEDERATION_CATEGORY_NAME}.authenticateCommunities`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.authenticateCommunities`) export async function startCommunityAuthentication( foreignFedCom: DbFederatedCommunity, diff --git a/backend/src/federation/client/1_0/AuthenticationClient.ts b/backend/src/federation/client/1_0/AuthenticationClient.ts index 38996433f..0d478a521 100644 --- a/backend/src/federation/client/1_0/AuthenticationClient.ts +++ b/backend/src/federation/client/1_0/AuthenticationClient.ts @@ -3,12 +3,12 @@ import { GraphQLClient } from 'graphql-request' import { ensureUrlEndsWithSlash } from '@/util/utilities' -import { LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME } from '@/federation/client/1_0' +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_FEDERATION_CLIENT1_0_CATEGORY_NAME}.AuthenticationClient`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.AuthenticationClient`) export class AuthenticationClient { dbCom: DbFederatedCommunity diff --git a/backend/src/federation/client/1_0/FederationClient.ts b/backend/src/federation/client/1_0/FederationClient.ts index b548a97e1..df1140f9c 100644 --- a/backend/src/federation/client/1_0/FederationClient.ts +++ b/backend/src/federation/client/1_0/FederationClient.ts @@ -1,7 +1,7 @@ import { FederatedCommunity as DbFederatedCommunity } from 'database' import { GraphQLClient } from 'graphql-request' -import { LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME } from '@/federation/client/1_0' +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 { ensureUrlEndsWithSlash } from '@/util/utilities' @@ -11,7 +11,7 @@ import { PublicCommunityInfoLoggingView } from './logging/PublicCommunityInfoLog import { GetPublicKeyResult } from './model/GetPublicKeyResult' import { PublicCommunityInfo } from './model/PublicCommunityInfo' -const logger = getLogger(`${LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME}.FederationClient`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.FederationClient`) export class FederationClient { dbCom: DbFederatedCommunity diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index cc0c74846..2bb0e099c 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -5,7 +5,7 @@ import { LogError } from '@/server/LogError' import { ensureUrlEndsWithSlash } from '@/util/utilities' import { getLogger } from 'log4js' -import { LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME } from '.' +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' @@ -15,7 +15,7 @@ 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_FEDERATION_CLIENT1_0_CATEGORY_NAME}.SendCoinsClient`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.SendCoinsClient`) export class SendCoinsClient { dbCom: DbFederatedCommunity diff --git a/backend/src/federation/client/1_0/index.ts b/backend/src/federation/client/1_0/index.ts deleted file mode 100644 index c21700126..000000000 --- a/backend/src/federation/client/1_0/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_FEDERATION_CATEGORY_NAME } from '@/federation' - -export const LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME = `${LOG4JS_FEDERATION_CATEGORY_NAME}.client.1_0` diff --git a/backend/src/federation/index.ts b/backend/src/federation/index.ts deleted file mode 100644 index c1684852b..000000000 --- a/backend/src/federation/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' - -export const LOG4JS_FEDERATION_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.federation` diff --git a/backend/src/federation/validateCommunities.test.ts b/backend/src/federation/validateCommunities.test.ts index 160068140..b4fde9893 100644 --- a/backend/src/federation/validateCommunities.test.ts +++ b/backend/src/federation/validateCommunities.test.ts @@ -4,16 +4,15 @@ import { GraphQLClient } from 'graphql-request' import { Response } from 'graphql-request/dist/types' import { DataSource } from 'typeorm' -import { LOG4JS_FEDERATION_CATEGORY_NAME } from '@/federation' -import { LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME } from '@/federation/client/1_0' import { cleanDB, testEnvironment } from '@test/helpers' -import { clearLogs, getLogger, printLogs } from 'config-schema/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_FEDERATION_CATEGORY_NAME}.validateCommunities`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.validateCommunities`) const federationClientLogger = getLogger( - `${LOG4JS_FEDERATION_CLIENT1_0_CATEGORY_NAME}.FederationClient`, + `${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.FederationClient`, ) let con: DataSource diff --git a/backend/src/federation/validateCommunities.ts b/backend/src/federation/validateCommunities.ts index dc4747352..bbdd5d4d2 100644 --- a/backend/src/federation/validateCommunities.ts +++ b/backend/src/federation/validateCommunities.ts @@ -5,7 +5,6 @@ import { } from 'database' import { IsNull } from 'typeorm' -import { LOG4JS_FEDERATION_CATEGORY_NAME } from '@/federation' import { FederationClient as V1_0_FederationClient } from '@/federation/client/1_0/FederationClient' import { PublicCommunityInfo } from '@/federation/client/1_0/model/PublicCommunityInfo' import { FederationClientFactory } from '@/federation/client/FederationClientFactory' @@ -14,8 +13,9 @@ 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_FEDERATION_CATEGORY_NAME}.validateCommunities`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.validateCommunities`) export async function startValidateCommunities(timerInterval: number): Promise { if (Number.isNaN(timerInterval) || timerInterval <= 0) { diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index f3c7d4709..7998e7c2e 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -12,7 +12,7 @@ import { Context, getUser } from '@/server/context' import { calculateDecay } from '@/util/decay' import { getLogger } from 'log4js' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { GdtResolver } from './GdtResolver' import { getLastTransaction } from './util/getLastTransaction' import { transactionLinkSummary } from './util/transactionLinkSummary' @@ -24,7 +24,7 @@ export class BalanceResolver { async balance(@Ctx() context: Context): Promise { const user = getUser(context) const now = new Date() - const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.BalanceResolver`) + const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.BalanceResolver`) logger.addContext('user', user.id) logger.info(`balance...`) diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index 428915765..c9c925a2e 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -19,12 +19,10 @@ import { import { peterLustig } from '@/seeds/users/peter-lustig' import { getLogger } from 'config-schema/test/testSetup' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' import { getCommunityByUuid } from './util/communities' jest.mock('@/password/EncryptorUtils') -const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.CommunityResolver`) // to do: We need a setup for the tests that closes the connection let mutate: ApolloServerTestClient['mutate'] @@ -44,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 diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index a9aa3fa91..962b77766 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -10,7 +10,6 @@ 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 { LOG4JS_INTERACTION_CATEGORY_NAME } from '@/interactions' import { userFactory } from '@/seeds/factory/user' import { adminCreateContributionMessage, @@ -22,13 +21,12 @@ 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 { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { getLogger} from 'config-schema/test/testSetup' -const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ContributionMessageResolver`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.ContributionMessageResolver`) const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`) const interactionLogger = getLogger( - `${LOG4JS_INTERACTION_CATEGORY_NAME}.updateUnconfirmedContribution`, + `${LOG4JS_BASE_CATEGORY_NAME}.interactions.updateUnconfirmedContribution`, ) jest.mock('@/password/EncryptorUtils') diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 505711b9e..8ddf782b0 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -23,13 +23,13 @@ import { UpdateUnconfirmedContributionContext } from '@/interactions/updateUncon import { LogError } from '@/server/LogError' import { Context, getUser } from '@/server/context' import { getLogger } from 'log4js' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +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_RESOLVER_CATEGORY_NAME}.ContributionMessageResolver`) +const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.ContributionMessageResolver`) @Resolver() export class ContributionMessageResolver { diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 8bdbd98a8..1bb3b08b7 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -51,7 +51,7 @@ 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 { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' +import { getLogger } from 'config-schema/test/testSetup' import { getLogger as originalGetLogger } from 'log4js' jest.mock('@/emails/sendEmailVariants') diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 687aa0196..f05e2ea7f 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -47,7 +47,7 @@ import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' import { calculateDecay } from '@/util/decay' import { fullName } from '@/util/utilities' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '@/graphql/resolver' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { ContributionMessageType } from '@enum/ContributionMessageType' import { AppDatabase } from 'database' import { getLogger } from 'log4js' @@ -63,7 +63,7 @@ import { getLastTransaction } from './util/getLastTransaction' import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' const db = AppDatabase.getInstance() -const createLogger = () => getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ContributionResolver`) +const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.ContributionResolver`) @Resolver(() => Contribution) export class ContributionResolver { diff --git a/backend/src/graphql/resolver/GdtResolver.ts b/backend/src/graphql/resolver/GdtResolver.ts index 9d7451ed8..c689cf9c2 100644 --- a/backend/src/graphql/resolver/GdtResolver.ts +++ b/backend/src/graphql/resolver/GdtResolver.ts @@ -11,9 +11,9 @@ import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' import { Context, getUser } from '@/server/context' import { getLogger } from 'log4js' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.GdtResolver`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.GdtResolver`) @Resolver() export class GdtResolver { diff --git a/backend/src/graphql/resolver/KlicktippResolver.test.ts b/backend/src/graphql/resolver/KlicktippResolver.test.ts index 10ef6a760..f3ac85ef4 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.test.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.test.ts @@ -9,11 +9,11 @@ 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_RESOLVER_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' jest.mock('@/password/EncryptorUtils') -const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.KlicktippResolver`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.KlicktippResolver`) let testEnv: any let mutate: any diff --git a/backend/src/graphql/resolver/ProjectBrandingResolver.ts b/backend/src/graphql/resolver/ProjectBrandingResolver.ts index a7ef1c8b8..9e59dbbaa 100644 --- a/backend/src/graphql/resolver/ProjectBrandingResolver.ts +++ b/backend/src/graphql/resolver/ProjectBrandingResolver.ts @@ -10,9 +10,9 @@ import { HumHubClient } from '@/apis/humhub/HumHubClient' import { RIGHTS } from '@/auth/RIGHTS' import { LogError } from '@/server/LogError' import { getLogger } from 'log4js' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.ProjectBrandingResolver`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.ProjectBrandingResolver`) @Resolver(() => ProjectBranding) export class ProjectBrandingResolver { diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 34c073e45..8c7a95cc9 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -44,7 +44,7 @@ import { calculateBalance } from '@/util/validate' import { DisburseJwtPayloadType } from '@/auth/jwt/payloadtypes/DisburseJwtPayloadType' import { Logger, getLogger } from 'log4js' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { executeTransaction } from './TransactionResolver' import { getAuthenticatedCommunities, @@ -56,7 +56,7 @@ import { getLastTransaction } from './util/getLastTransaction' import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' import { transactionLinkList } from './util/transactionLinkList' -const createLogger = () => getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.TransactionLinkResolver`) +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 => { diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 511c7167d..ac21f255b 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -36,7 +36,7 @@ import { calculateBalance } from '@/util/validate' import { virtualDecayTransaction, virtualLinkTransaction } from '@/util/virtualTransactions' import { Logger, getLogger } from 'log4js' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { BalanceResolver } from './BalanceResolver' import { GdtResolver } from './GdtResolver' import { getCommunityByIdentifier, getCommunityName, isHomeCommunity } from './util/communities' @@ -52,7 +52,7 @@ import { storeForeignUser } from './util/storeForeignUser' import { transactionLinkSummary } from './util/transactionLinkSummary' const db = AppDatabase.getInstance() -const createLogger = () => getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.TransactionResolver`) +const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.TransactionResolver`) export const executeTransaction = async ( amount: Decimal, diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 9a8c223f1..117212850 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -68,8 +68,7 @@ import { printTimeDuration } from '@/util/time' import { objectValuesToArray } from '@/util/utilities' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '.' +import { getLogger } from 'config-schema/test/testSetup' import { Location2Point } from './util/Location2Point' jest.mock('@/apis/humhub/HumHubClient') @@ -96,7 +95,7 @@ jest.mock('@/apis/KlicktippController', () => { } }) -const logger = getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.UserResolver`) +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 @@ -113,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 @@ -698,9 +697,6 @@ describe('UserResolver', () => { }) describe('no users in database', () => { - beforeAll(() => { - clearLogs() - }) it('throws an error', async () => { jest.clearAllMocks() const result = await mutate({ mutation: login, variables }) @@ -712,7 +708,6 @@ describe('UserResolver', () => { }) it('logs the error found', () => { - printLogs() expect(logger.warn).toBeCalledWith( `findUserByEmail failed, user with email=${variables.email} not found`, ) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 46c9c44ed..c6a2997d9 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -88,7 +88,7 @@ import { delay } from '@/util/utilities' import random from 'random-bigint' import { randombytes_random } from 'sodium-native' -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '@/graphql/resolver' +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' @@ -108,7 +108,7 @@ import { validateAlias } from './util/validateAlias' const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl'] const DEFAULT_LANGUAGE = 'de' const db = AppDatabase.getInstance() -const createLogger = () => getLogger(`${LOG4JS_RESOLVER_CATEGORY_NAME}.UserResolver`) +const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.UserResolver`) const isLanguage = (language: string): boolean => { return LANGUAGES.includes(language) } diff --git a/backend/src/graphql/resolver/index.ts b/backend/src/graphql/resolver/index.ts deleted file mode 100644 index 0ee59f7f6..000000000 --- a/backend/src/graphql/resolver/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' - -export const LOG4JS_RESOLVER_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver` diff --git a/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts b/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts index 01d423993..74593f1c7 100644 --- a/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts +++ b/backend/src/graphql/resolver/util/authenticateGmsUserPlayground.ts @@ -3,12 +3,12 @@ import { User as DbUser } from 'database' import { verifyAuthToken } from '@/apis/gms/GmsClient' import { CONFIG } from '@/config' import { GmsUserAuthenticationResult } from '@/graphql/model/GmsUserAuthenticationResult' -import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { ensureUrlEndsWithSlash } from '@/util/utilities' import { getLogger } from 'log4js' const logger = getLogger( - `${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.authenticateGmsUserPlayground`, + `${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.authenticateGmsUserPlayground`, ) export async function authenticateGmsUserPlayground( diff --git a/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts index 80a1e608a..1928374a4 100644 --- a/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts +++ b/backend/src/graphql/resolver/util/compareGmsRelevantUserSettings.ts @@ -7,11 +7,11 @@ import { PublishNameType } from '@/graphql/enum/PublishNameType' import { LogError } from '@/server/LogError' import { getLogger } from 'log4js' -import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { Point2Location } from './Location2Point' const logger = getLogger( - `${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.compareGmsRelevantUserSettings`, + `${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.compareGmsRelevantUserSettings`, ) export function compareGmsRelevantUserSettings( diff --git a/backend/src/graphql/resolver/util/creations.ts b/backend/src/graphql/resolver/util/creations.ts index 02439e10a..a0c930579 100644 --- a/backend/src/graphql/resolver/util/creations.ts +++ b/backend/src/graphql/resolver/util/creations.ts @@ -4,14 +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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { LogError } from '@/server/LogError' import { getFirstDayOfPreviousNMonth } from '@/util/utilities' import { AppDatabase } from 'database' import { getLogger } from 'log4js' const db = AppDatabase.getInstance() -const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.creations`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.creations`) interface CreationMap { id: number diff --git a/backend/src/graphql/resolver/util/getKlicktippState.ts b/backend/src/graphql/resolver/util/getKlicktippState.ts index 719286223..7cfdb6d51 100644 --- a/backend/src/graphql/resolver/util/getKlicktippState.ts +++ b/backend/src/graphql/resolver/util/getKlicktippState.ts @@ -1,10 +1,10 @@ import { KlickTipp } from '@model/KlickTipp' import { getKlickTippUser } from '@/apis/KlicktippController' -import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { getLogger } from 'log4js' -const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.getKlicktippState`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.getKlicktippState`) export const getKlicktippState = async (email: string): Promise => { try { diff --git a/backend/src/graphql/resolver/util/index.ts b/backend/src/graphql/resolver/util/index.ts deleted file mode 100644 index bf0fba6d1..000000000 --- a/backend/src/graphql/resolver/util/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_RESOLVER_CATEGORY_NAME } from '@/graphql/resolver' - -export const LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME = `${LOG4JS_RESOLVER_CATEGORY_NAME}.util` diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 572a50f97..f288ab3ff 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -14,7 +14,7 @@ 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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { LogError } from '@/server/LogError' import { calculateSenderBalance } from '@/util/calculateSenderBalance' import { fullName } from '@/util/utilities' @@ -22,7 +22,7 @@ import { getLogger } from 'log4js' import { settlePendingSenderTransaction } from './settlePendingSenderTransaction' -const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.processXComSendCoins`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.processXComSendCoins`) export async function processXComPendingSendCoins( receiverCom: DbCommunity, diff --git a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts index 4dd1672af..b5ec0a08a 100644 --- a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts +++ b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.test.ts @@ -21,14 +21,14 @@ 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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { sendTransactionsToDltConnector } from './sendTransactionsToDltConnector' jest.mock('@/password/EncryptorUtils') const logger = getLogger( - `${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.sendTransactionsToDltConnector`, + `${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.sendTransactionsToDltConnector`, ) /* diff --git a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts index 98e53456e..678fcd151 100644 --- a/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts +++ b/backend/src/graphql/resolver/util/sendTransactionsToDltConnector.ts @@ -3,12 +3,12 @@ import { IsNull } from 'typeorm' import { DltConnectorClient } from '@dltConnector/DltConnectorClient' -import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { Monitor, MonitorNames } from '@/util/Monitor' import { getLogger } from 'log4js' const logger = getLogger( - `${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.sendTransactionsToDltConnector`, + `${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.sendTransactionsToDltConnector`, ) export async function sendTransactionsToDltConnector(): Promise { diff --git a/backend/src/graphql/resolver/util/sendUserToGms.ts b/backend/src/graphql/resolver/util/sendUserToGms.ts index 835d36343..a0edfd35a 100644 --- a/backend/src/graphql/resolver/util/sendUserToGms.ts +++ b/backend/src/graphql/resolver/util/sendUserToGms.ts @@ -3,11 +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_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { LogError } from '@/server/LogError' import { getLogger } from 'log4js' -const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.sendUserToGms`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.sendUserToGms`) export async function sendUserToGms( user: DbUser, diff --git a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts index 81f1d5042..a10e982b3 100644 --- a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts +++ b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts @@ -8,7 +8,7 @@ import { import { Decimal } from 'decimal.js-light' import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState' -import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { LogError } from '@/server/LogError' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' import { calculateSenderBalance } from '@/util/calculateSenderBalance' @@ -17,7 +17,7 @@ import { getLastTransaction } from './getLastTransaction' const db = AppDatabase.getInstance() const logger = getLogger( - `${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.settlePendingSenderTransaction`, + `${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.settlePendingSenderTransaction`, ) export async function settlePendingSenderTransaction( diff --git a/backend/src/graphql/resolver/util/storeForeignUser.ts b/backend/src/graphql/resolver/util/storeForeignUser.ts index bd0eeb68f..adceb155e 100644 --- a/backend/src/graphql/resolver/util/storeForeignUser.ts +++ b/backend/src/graphql/resolver/util/storeForeignUser.ts @@ -1,10 +1,10 @@ import { Community as DbCommunity, User as DbUser } from 'database' import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult' -import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { getLogger } from 'log4js' -const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.storeForeignUser`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.storeForeignUser`) export async function storeForeignUser( recipCom: DbCommunity, diff --git a/backend/src/graphql/resolver/util/syncHumhub.test.ts b/backend/src/graphql/resolver/util/syncHumhub.test.ts index 2eba3be40..5d31f036b 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.test.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.test.ts @@ -5,7 +5,7 @@ import { GetUser } from '@/apis/humhub/model/GetUser' import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs' import { PublishNameType } from '@/graphql/enum/PublishNameType' -import { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { getLogger } from 'config-schema/test/testSetup' import { syncHumhub } from './syncHumhub' @@ -20,7 +20,7 @@ mockUser.humhubPublishName = PublishNameType.PUBLISH_NAME_FULL const mockUpdateUserInfosArg = new UpdateUserInfosArgs() const mockHumHubUser = new GetUser(mockUser, 1) -const logger = getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.syncHumhub`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.syncHumhub`) describe('syncHumhub', () => { beforeEach(() => { diff --git a/backend/src/graphql/resolver/util/syncHumhub.ts b/backend/src/graphql/resolver/util/syncHumhub.ts index c866daf5a..9cc53d1e9 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.ts @@ -7,10 +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 { LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME } from '@/graphql/resolver/util' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { getLogger } from 'log4js' -const createLogger = () => getLogger(`${LOG4JS_GRAPHQL_RESOLVER_UTIL_CATEGORY_NAME}.syncHumhub`) +const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.syncHumhub`) /** * Syncs the user with humhub diff --git a/backend/src/interactions/index.ts b/backend/src/interactions/index.ts deleted file mode 100644 index 3aad98ba5..000000000 --- a/backend/src/interactions/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' - -export const LOG4JS_INTERACTION_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.interaction` diff --git a/backend/src/interactions/updateUnconfirmedContribution/AbstractUnconfirmedContribution.role.ts b/backend/src/interactions/updateUnconfirmedContribution/AbstractUnconfirmedContribution.role.ts index b713aeefd..dd85e3fb6 100644 --- a/backend/src/interactions/updateUnconfirmedContribution/AbstractUnconfirmedContribution.role.ts +++ b/backend/src/interactions/updateUnconfirmedContribution/AbstractUnconfirmedContribution.role.ts @@ -5,10 +5,10 @@ import { Role } from '@/auth/Role' import { ContributionLogic } from '@/data/Contribution.logic' import { ContributionMessageBuilder } from '@/data/ContributionMessage.builder' import { ContributionStatus } from '@/graphql/enum/ContributionStatus' -import { LOG4JS_INTERACTION_CATEGORY_NAME } from '@/interactions' 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[] @@ -24,7 +24,7 @@ export abstract class AbstractUnconfirmedContributionRole { if (self.confirmedAt || self.deniedAt) { throw new LogError("this contribution isn't unconfirmed!") } - this.logger = getLogger(`${LOG4JS_INTERACTION_CATEGORY_NAME}.updateUnconfirmedContribution`) + this.logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.interactions.updateUnconfirmedContribution`) this.logger.addContext('contribution', this.self.id) } diff --git a/backend/src/seeds/factory/user.ts b/backend/src/seeds/factory/user.ts index 34d399b08..3cae22f71 100644 --- a/backend/src/seeds/factory/user.ts +++ b/backend/src/seeds/factory/user.ts @@ -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 { diff --git a/dht-node/src/dht_node/index.test.ts b/dht-node/src/dht_node/index.test.ts index 20cc52057..25ccd4638 100644 --- a/dht-node/src/dht_node/index.test.ts +++ b/dht-node/src/dht_node/index.test.ts @@ -3,11 +3,11 @@ import { Community as DbCommunity, FederatedCommunity as DbFederatedCommunity } import { validate as validateUUID, version as versionUUID } from 'uuid' import { cleanDB, testEnvironment } from '@test/helpers' -import { clearLogs, getLogger, printLogs } from 'config-schema/test/testSetup' - +import { getLogger } from 'config-schema/test/testSetup' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { CONFIG } from '@/config' -import { LOG_CATEGORY_DHT_NODE, startDHT } from './index' +import { startDHT } from './index' CONFIG.FEDERATION_DHT_SEED = '64ebcb0e3ad547848fef4197c6e2332f' CONFIG.FEDERATION_COMMUNITY_APIS = '1_0,1_1,2_0' @@ -21,7 +21,7 @@ const keyPairMock = { secretKey: Buffer.from('secretKey'), } -const logger = getLogger(LOG_CATEGORY_DHT_NODE) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.dht_node`) const serverListenSpy = jest.fn() @@ -358,7 +358,6 @@ describe('federation', () => { let jsonArray: any[] let result: DbFederatedCommunity[] = [] beforeAll(async () => { - clearLogs() jest.clearAllMocks() jsonArray = [ { @@ -374,7 +373,6 @@ describe('federation', () => { ] await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) result = await DbFederatedCommunity.find({ where: { foreign: true } }) - printLogs() }) afterAll(async () => { diff --git a/dht-node/src/dht_node/index.ts b/dht-node/src/dht_node/index.ts index dc45b15ed..4e9ca7b0f 100644 --- a/dht-node/src/dht_node/index.ts +++ b/dht-node/src/dht_node/index.ts @@ -18,8 +18,7 @@ const POLLTIME = 20000 const SUCCESSTIME = 120000 const ERRORTIME = 240000 const ANNOUNCETIME = 30000 -export const LOG_CATEGORY_DHT_NODE = `${LOG4JS_BASE_CATEGORY_NAME}.dht_node` -const logger = getLogger(LOG_CATEGORY_DHT_NODE) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.dht_node`) type CommunityApi = { api: string diff --git a/federation/src/client/1_0/AuthenticationClient.ts b/federation/src/client/1_0/AuthenticationClient.ts index 8535e06ab..85daa706f 100644 --- a/federation/src/client/1_0/AuthenticationClient.ts +++ b/federation/src/client/1_0/AuthenticationClient.ts @@ -1,14 +1,14 @@ import { FederatedCommunity as DbFederatedCommunity } from 'database' import { GraphQLClient } from 'graphql-request' import { getLogger } from 'log4js' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { AuthenticationArgs } from '@/graphql/api/1_0/model/AuthenticationArgs' import { OpenConnectionCallbackArgs } from '@/graphql/api/1_0/model/OpenConnectionCallbackArgs' -import { LOG4JS_CLIENT_1_0_CATEGORY_NAME } from '.' import { authenticate } from './query/authenticate' import { openConnectionCallback } from './query/openConnectionCallback' -const logger = getLogger(`${LOG4JS_CLIENT_1_0_CATEGORY_NAME}.AuthenticationClient`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.client.1_0.AuthenticationClient`) export class AuthenticationClient { dbCom: DbFederatedCommunity diff --git a/federation/src/client/1_0/index.ts b/federation/src/client/1_0/index.ts deleted file mode 100644 index 18e8aaeed..000000000 --- a/federation/src/client/1_0/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' - -export const LOG4JS_CLIENT_1_0_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.client.1_0` diff --git a/federation/src/graphql/api/1_0/index.ts b/federation/src/graphql/api/1_0/index.ts deleted file mode 100644 index 3a995fd28..000000000 --- a/federation/src/graphql/api/1_0/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_API_CATEGORY_NAME } from '@/graphql/api' - -export const LOG4JS_API_1_0_CATEGORY_NAME = `${LOG4JS_API_CATEGORY_NAME}.1_0` diff --git a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts index 89e891b34..73f0f2aec 100644 --- a/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/AuthenticationResolver.ts @@ -8,13 +8,13 @@ import { } from 'database' import { getLogger } from 'log4js' import { Arg, Mutation, Resolver } from 'type-graphql' -import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { AuthenticationArgs } from '../model/AuthenticationArgs' import { OpenConnectionArgs } from '../model/OpenConnectionArgs' import { OpenConnectionCallbackArgs } from '../model/OpenConnectionCallbackArgs' import { startAuthentication, startOpenConnectionCallback } from '../util/authenticateCommunity' -const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.AuthenticationResolver`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.resolver.AuthenticationResolver`) @Resolver() export class AuthenticationResolver { diff --git a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts index e7bf40bed..174c3735f 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.test.ts @@ -1,5 +1,4 @@ import { CONFIG } from '@/config' -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { createServer } from '@/server/createServer' import { createTestClient } from 'apollo-server-testing' import { Community as DbCommunity } from 'database' @@ -14,7 +13,7 @@ let con: DataSource CONFIG.FEDERATION_API = '1_0' beforeAll(async () => { - const server = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`)) + const server = await createServer(getLogger('apollo')) con = server.con query = createTestClient(server.apollo).query DbCommunity.clear() diff --git a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.ts b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.ts index 9bd2898f3..1cdb43a31 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicCommunityInfoResolver.ts @@ -1,11 +1,11 @@ import { Community as DbCommunity } from 'database' import { getLogger } from 'log4js' import { Query, Resolver } from 'type-graphql' -import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { GetPublicCommunityInfoResultLoggingView } from '../logger/GetPublicCommunityInfoResultLogging.view' import { GetPublicCommunityInfoResult } from '../model/GetPublicCommunityInfoResult' -const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.PublicCommunityInfoResolver`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.resolver.PublicCommunityInfoResolver`) @Resolver() export class PublicCommunityInfoResolver { diff --git a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts index 13ec75dfc..023a11a19 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts @@ -13,7 +13,7 @@ let con: any CONFIG.FEDERATION_API = '1_0' beforeAll(async () => { - const server = await createServer(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`)) + const server = await createServer(getLogger('apollo')) con = server.con query = createTestClient(server.apollo).query DbFederatedCommunity.clear() diff --git a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts index f849941e0..4df03096d 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts @@ -1,10 +1,10 @@ import { FederatedCommunity as DbFederatedCommunity } from 'database' import { getLogger } from 'log4js' import { Query, Resolver } from 'type-graphql' -import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { GetPublicKeyResult } from '../model/GetPublicKeyResult' -const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.PublicKeyResolver`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.resolver.PublicKeyResolver`) @Resolver() export class PublicKeyResolver { diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts index d41ea1936..5ba55f88a 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -29,7 +29,7 @@ let recipUser: DbUser let recipContact: DbUserContact beforeAll(async () => { - testEnv = await testEnvironment(getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apollo`)) + testEnv = await testEnvironment(getLogger('apollo')) mutate = testEnv.mutate // query = testEnv.query // con = testEnv.con diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index a6eeef7f8..84b79ea0e 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -9,7 +9,7 @@ import { import Decimal from 'decimal.js-light' import { getLogger } from 'log4js' import { Arg, Mutation, Resolver } from 'type-graphql' -import { LOG4JS_RESOLVER_1_0_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { PendingTransactionState } from '../enum/PendingTransactionState' import { TransactionTypeId } from '../enum/TransactionTypeId' import { SendCoinsArgsLoggingView } from '../logger/SendCoinsArgsLogging.view' @@ -21,7 +21,7 @@ import { revertSettledReceiveTransaction } from '../util/revertSettledReceiveTra import { settlePendingReceiveTransaction } from '../util/settlePendingReceiveTransaction' import { storeForeignUser } from '../util/storeForeignUser' -const logger = getLogger(`${LOG4JS_RESOLVER_1_0_CATEGORY_NAME}.SendCoinsResolver`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.resolver.SendCoinsResolver`) @Resolver() export class SendCoinsResolver { diff --git a/federation/src/graphql/api/1_0/resolver/index.ts b/federation/src/graphql/api/1_0/resolver/index.ts deleted file mode 100644 index adec2adb9..000000000 --- a/federation/src/graphql/api/1_0/resolver/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_API_1_0_CATEGORY_NAME } from '@/graphql/api/1_0' - -export const LOG4JS_RESOLVER_1_0_CATEGORY_NAME = `${LOG4JS_API_1_0_CATEGORY_NAME}.resolver` diff --git a/federation/src/graphql/api/1_0/util/authenticateCommunity.ts b/federation/src/graphql/api/1_0/util/authenticateCommunity.ts index e4d27e47c..3f89dd5c4 100644 --- a/federation/src/graphql/api/1_0/util/authenticateCommunity.ts +++ b/federation/src/graphql/api/1_0/util/authenticateCommunity.ts @@ -12,10 +12,10 @@ import { AuthenticationClientFactory } from '@/client/AuthenticationClientFactor import { randombytes_random } from 'sodium-native' import { AuthenticationClient as V1_0_AuthenticationClient } from '@/client/1_0/AuthenticationClient' -import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { AuthenticationArgs } from '../model/AuthenticationArgs' -const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.authenticateCommunity`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.util.authenticateCommunity`) export async function startOpenConnectionCallback( args: OpenConnectionArgs, diff --git a/federation/src/graphql/api/1_0/util/index.ts b/federation/src/graphql/api/1_0/util/index.ts deleted file mode 100644 index 109466d5c..000000000 --- a/federation/src/graphql/api/1_0/util/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_API_1_0_CATEGORY_NAME } from '@/graphql/api/1_0' - -export const LOG4JS_1_0_UTIL_CATEGORY_NAME = `${LOG4JS_API_1_0_CATEGORY_NAME}.util` diff --git a/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts index cdb8a4563..685a7b9ff 100644 --- a/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts @@ -14,13 +14,13 @@ import { PendingTransactionState } from '../enum/PendingTransactionState' import { LogError } from '@/server/LogError' import { getLogger } from 'log4js' -import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { TRANSACTIONS_LOCK } from '@/graphql/util/TRANSACTIONS_LOCK' import { getLastTransaction } from '@/graphql/util/getLastTransaction' const db = AppDatabase.getInstance() -const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.revertSettledReceiveTransaction`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.util.revertSettledReceiveTransaction`) export async function revertSettledReceiveTransaction( homeCom: DbCommunity, diff --git a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts index b10d40a03..49e8ca203 100644 --- a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts @@ -17,11 +17,11 @@ import { TRANSACTIONS_LOCK } from '@/graphql/util/TRANSACTIONS_LOCK' import { getLastTransaction } from '@/graphql/util/getLastTransaction' import Decimal from 'decimal.js-light' import { getLogger } from 'log4js' -import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { calculateRecipientBalance } from './calculateRecipientBalance' const db = AppDatabase.getInstance() -const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.settlePendingReceiveTransaction`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.util.settlePendingReceiveTransaction`) export async function settlePendingReceiveTransaction( homeCom: DbCommunity, diff --git a/federation/src/graphql/api/1_0/util/storeForeignUser.ts b/federation/src/graphql/api/1_0/util/storeForeignUser.ts index c7991f482..c45f94eb7 100644 --- a/federation/src/graphql/api/1_0/util/storeForeignUser.ts +++ b/federation/src/graphql/api/1_0/util/storeForeignUser.ts @@ -1,11 +1,11 @@ import { User as DbUser, UserLoggingView } from 'database' import { getLogger } from 'log4js' -import { LOG4JS_1_0_UTIL_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { SendCoinsArgsLoggingView } from '../logger/SendCoinsArgsLogging.view' import { SendCoinsArgs } from '../model/SendCoinsArgs' -const logger = getLogger(`${LOG4JS_1_0_UTIL_CATEGORY_NAME}.storeForeignUser`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.util.storeForeignUser`) export async function storeForeignUser(args: SendCoinsArgs): Promise { if (args.senderCommunityUuid !== null && args.senderUserUuid !== null) { diff --git a/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.ts b/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.ts index 181ccb78c..01ac673d5 100644 --- a/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.ts +++ b/federation/src/graphql/api/1_1/resolver/PublicKeyResolver.ts @@ -1,10 +1,10 @@ import { FederatedCommunity as DbFederatedCommunity } from 'database' import { getLogger } from 'log4js' import { Query, Resolver } from 'type-graphql' -import { LOG4JS_RESOLVER_1_1_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { GetPublicKeyResult } from '../../1_0/model/GetPublicKeyResult' -const logger = getLogger(`${LOG4JS_RESOLVER_1_1_CATEGORY_NAME}.PublicKeyResolver`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_1.resolver.PublicKeyResolver`) @Resolver() export class PublicKeyResolver { diff --git a/federation/src/graphql/api/1_1/resolver/index.ts b/federation/src/graphql/api/1_1/resolver/index.ts deleted file mode 100644 index f2b9283eb..000000000 --- a/federation/src/graphql/api/1_1/resolver/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_API_CATEGORY_NAME } from '@/graphql/api' - -export const LOG4JS_RESOLVER_1_1_CATEGORY_NAME = `${LOG4JS_API_CATEGORY_NAME}.1_1.resolver` diff --git a/federation/src/graphql/api/index.ts b/federation/src/graphql/api/index.ts deleted file mode 100644 index b6e810c61..000000000 --- a/federation/src/graphql/api/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_GRAPHQL_CATEGORY_NAME } from '@/graphql' - -export const LOG4JS_API_CATEGORY_NAME = `${LOG4JS_GRAPHQL_CATEGORY_NAME}.api` diff --git a/federation/src/graphql/api/schema.ts b/federation/src/graphql/api/schema.ts index 00ae50b21..1802d046e 100644 --- a/federation/src/graphql/api/schema.ts +++ b/federation/src/graphql/api/schema.ts @@ -1,13 +1,13 @@ import { getLogger } from 'log4js' import { NonEmptyArray } from 'type-graphql' -import { LOG4JS_API_CATEGORY_NAME } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' // config import { CONFIG } from '../../config' import { getApiResolvers as getApiResolvers_1_0 } from './1_0/schema' import { getApiResolvers as getApiResolvers_1_1 } from './1_1/schema' export const getApiResolvers = (): NonEmptyArray => { - getLogger(LOG4JS_API_CATEGORY_NAME).info(`getApiResolvers...${CONFIG.FEDERATION_API}`) + getLogger(LOG4JS_BASE_CATEGORY_NAME).info(`getApiResolvers...${CONFIG.FEDERATION_API}`) if (CONFIG.FEDERATION_API === '1_0') { return getApiResolvers_1_0() diff --git a/federation/src/graphql/index.ts b/federation/src/graphql/index.ts deleted file mode 100644 index c3b774479..000000000 --- a/federation/src/graphql/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' - -export const LOG4JS_GRAPHQL_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.graphql` diff --git a/federation/src/graphql/util/checkTradingLevel.ts b/federation/src/graphql/util/checkTradingLevel.ts index 458970c9f..2a96f34f9 100644 --- a/federation/src/graphql/util/checkTradingLevel.ts +++ b/federation/src/graphql/util/checkTradingLevel.ts @@ -1,11 +1,11 @@ import { CONFIG } from '@/config' -import { LOG4JS_GRAPHQL_UTIL_CATEGORY_NAME } from '@/graphql/util' import { Community as DbCommunity } from 'database' import { Decimal } from 'decimal.js-light' import { getLogger } from 'log4js' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' export async function checkTradingLevel(homeCom: DbCommunity, amount: Decimal): Promise { - const logger = getLogger(`${LOG4JS_GRAPHQL_UTIL_CATEGORY_NAME}.checkTradingLevel`) + const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.util.checkTradingLevel`) const tradingLevel = CONFIG.FEDERATION_TRADING_LEVEL if (homeCom.url !== tradingLevel.RECEIVER_COMMUNITY_URL) { diff --git a/federation/src/graphql/util/index.ts b/federation/src/graphql/util/index.ts deleted file mode 100644 index d7f06f692..000000000 --- a/federation/src/graphql/util/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_GRAPHQL_CATEGORY_NAME } from '@/graphql' - -export const LOG4JS_GRAPHQL_UTIL_CATEGORY_NAME = `${LOG4JS_GRAPHQL_CATEGORY_NAME}.util` From 54782d95c35a70eecc46d46b553b650499c70e7c Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 26 Jun 2025 08:00:25 +0200 Subject: [PATCH 40/46] use correct logging categories --- core/src/validation/index.ts | 3 --- core/src/validation/user.test.ts | 6 +++--- core/src/validation/user.ts | 4 ++-- shared/src/logic/decay.test.ts | 9 +++++++++ shared/src/logic/decay.ts | 6 +----- shared/src/logic/index.ts | 3 --- 6 files changed, 15 insertions(+), 16 deletions(-) delete mode 100644 core/src/validation/index.ts delete mode 100644 shared/src/logic/index.ts diff --git a/core/src/validation/index.ts b/core/src/validation/index.ts deleted file mode 100644 index b00a2f926..000000000 --- a/core/src/validation/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' - -export const LOG4JS_CATEGORY_SCHEMA_ALIAS = `${LOG4JS_BASE_CATEGORY_NAME}.schema` diff --git a/core/src/validation/user.test.ts b/core/src/validation/user.test.ts index 4cfc76a7c..cd3bd1925 100644 --- a/core/src/validation/user.test.ts +++ b/core/src/validation/user.test.ts @@ -1,10 +1,10 @@ -import { LOG4JS_CATEGORY_SCHEMA_ALIAS } from '.' import { validateAlias } from './user' -import { getLogger, printLogs } from '../../../config-schema/test/testSetup.bun' +import { getLogger } from '../../../config-schema/test/testSetup.bun' import { describe, it, expect, beforeEach, mock, jest } from 'bun:test' import { aliasExists } from 'database' +import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' -const logger = getLogger(`${LOG4JS_CATEGORY_SCHEMA_ALIAS}.alias`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.validation.user`) mock.module('database', () => ({ aliasExists: jest.fn(), diff --git a/core/src/validation/user.ts b/core/src/validation/user.ts index b188e5d54..eebc34442 100644 --- a/core/src/validation/user.ts +++ b/core/src/validation/user.ts @@ -1,10 +1,10 @@ import { ZodError } from 'zod' import { getLogger } from 'log4js' -import { LOG4JS_CATEGORY_SCHEMA_ALIAS } from '.' +import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' import { aliasExists } from 'database' import { aliasSchema } from 'shared' -const logger = getLogger(`${LOG4JS_CATEGORY_SCHEMA_ALIAS}.alias`) +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.validation.user`) export async function validateAlias(alias: string): Promise { try { diff --git a/shared/src/logic/decay.test.ts b/shared/src/logic/decay.test.ts index 1db94c4fe..7678c945b 100644 --- a/shared/src/logic/decay.test.ts +++ b/shared/src/logic/decay.test.ts @@ -41,4 +41,13 @@ describe('utils/decay', () => { const now = new Date() expect(calculateDecay(new Decimal(100.0), now, now).balance.toString()).toBe('100') }) + + describe('calculateDecay called with invalid dates', () => { + it('throws an error when to is before from', () => { + const now = new Date() + const oneSecondAgo = new Date(now.getTime()) + oneSecondAgo.setSeconds(0) + expect(() => calculateDecay(new Decimal(1.0), now, oneSecondAgo)).toThrowError() + }) + }) }) diff --git a/shared/src/logic/decay.ts b/shared/src/logic/decay.ts index f75146515..35ab99803 100644 --- a/shared/src/logic/decay.ts +++ b/shared/src/logic/decay.ts @@ -1,11 +1,7 @@ import { Decimal } from 'decimal.js-light' -import { getLogger } from 'log4js' -import { LOG4JS_LOGIC_CATEGORY } from '.' import { DECAY_START_TIME } from '../const' -const logger = getLogger(`${LOG4JS_LOGIC_CATEGORY}.DecayLogic`) - Decimal.set({ precision: 25, rounding: Decimal.ROUND_HALF_UP, @@ -40,7 +36,7 @@ export function calculateDecay( const startBlockMs = DECAY_START_TIME.getTime() if (toMs < fromMs) { - logger.error('calculateDecay: to < from, reverse decay calculation is invalid', from, to) + // TODO: refactor, use custom Error Classes which contain context variables throw new Error('calculateDecay: to < from, reverse decay calculation is invalid') } diff --git a/shared/src/logic/index.ts b/shared/src/logic/index.ts deleted file mode 100644 index fca61a4b1..000000000 --- a/shared/src/logic/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { LOG4JS_BASE_CATEGORY_NAME } from '../const' - -export const LOG4JS_LOGIC_CATEGORY = `${LOG4JS_BASE_CATEGORY_NAME}.logic` From 00d111bd9539d7c3b0204346a3675f2887244b1d Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 27 Jun 2025 09:59:45 +0200 Subject: [PATCH 41/46] Update backend/src/graphql/resolver/UserResolver.ts Co-authored-by: clauspeterhuebner <86960882+clauspeterhuebner@users.noreply.github.com> --- backend/src/graphql/resolver/UserResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index c6a2997d9..50a7ff298 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -176,7 +176,7 @@ export class UserResolver { try { dbUser = await findUserByEmail(email) - // add pubKey in logger-context for layout-pattern X{user} to print it in each logging message + // 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) { From c328b73a978ead7e594707cc488580649351ebee Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 27 Jun 2025 12:10:15 +0200 Subject: [PATCH 42/46] logging adjustments --- .../resolver/util/processXComSendCoins.ts | 109 +++++++++++------- backend/src/webhook/gms.ts | 3 +- 2 files changed, 68 insertions(+), 44 deletions(-) diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index f288ab3ff..4cbf9b570 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -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' @@ -21,6 +24,8 @@ 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`) @@ -35,15 +40,19 @@ export async function processXComPendingSendCoins( ): Promise { 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 }, @@ -66,7 +75,9 @@ export async function processXComPendingSendCoins( if (!senderBalance) { throw new LogError('User has not enough GDD or amount is < 0', senderBalance) } - logger.debug(`calculated senderBalance = `, senderBalance) + if(logger.isDebugEnabled()) { + logger.debug(`calculated senderBalance = ${JSON.stringify(senderBalance, null, 2)}`) + } const receiverFCom = await DbFederatedCommunity.findOneOrFail({ where: { @@ -91,11 +102,15 @@ export async function processXComPendingSendCoins( args.senderUserUuid = sender.gradidoID args.senderUserName = fullName(sender.firstName, sender.lastName) args.senderAlias = sender.alias - logger.debug(`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(`returned from voteForSendCoins:`, voteResult) + if(logger.isDebugEnabled()) { + logger.debug(`returned from voteForSendCoins: ${new SendCoinsResultLoggingView(voteResult)}`) + } if (voteResult.vote) { - logger.debug(`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() @@ -123,35 +138,37 @@ export async function processXComPendingSendCoins( pendingTx.userId = sender.id pendingTx.userGradidoID = sender.gradidoID pendingTx.userName = fullName(sender.firstName, sender.lastName) - logger.debug(`initialized sender pendingTX=`, pendingTx) + if(logger.isDebugEnabled()) { + logger.debug(`initialized sender pendingTX=${new PendingTransactionLoggingView(pendingTx)}`) + } await DbPendingTransaction.insert(pendingTx) - logger.debug(`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(`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) } } 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(`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 { const sendCoinsResult = new SendCoinsResult() try { - logger.debug( - `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,7 +211,9 @@ export async function processXComCommittingSendCoins( memo, }) if (pendingTx) { - logger.debug('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: Buffer.from(receiverCom.publicKey), @@ -218,9 +240,11 @@ export async function processXComCommittingSendCoins( args.senderUserName = pendingTx.userName } args.senderAlias = sender.alias - logger.debug('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('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('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) } } 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 diff --git a/backend/src/webhook/gms.ts b/backend/src/webhook/gms.ts index 06f48024d..038804366 100644 --- a/backend/src/webhook/gms.ts +++ b/backend/src/webhook/gms.ts @@ -2,8 +2,9 @@ import { User as DbUser } from 'database' import { decode } from '@/auth/JWT' import { getLogger } from 'log4js' +import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -const logger = getLogger('gms.GmsWebhook') +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.webhook.gms`) export const gmsWebhook = async (req: any, res: any): Promise => { logger.info('GMS Hook received') From b487b4ca72d23a5a0b58ded173f68beb37e4edbd Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 27 Jun 2025 14:12:45 +0200 Subject: [PATCH 43/46] move code for checking pending transactions into own query + test --- .../graphql/enum/PendingTransactionState.ts | 8 +- .../graphql/resolver/TransactionResolver.ts | 17 +-- .../resolver/util/processXComSendCoins.ts | 17 +-- .../util/settlePendingSenderTransaction.ts | 2 +- .../logging/PendingTransactionLogging.view.ts | 9 +- database/src/queries/index.ts | 1 + .../src/queries/pendingTransactions.test.ts | 124 ++++++++++++++++++ database/src/queries/pendingTransactions.ts | 18 +++ database/src/queries/user.test.ts | 3 +- .../src/seeds/factory/pendingTransaction.ts | 23 ++++ database/src/seeds/factory/user.ts | 4 +- .../api/1_0/enum/PendingTransactionState.ts | 8 +- .../api/1_0/resolver/SendCoinsResolver.ts | 19 +-- .../util/revertSettledReceiveTransaction.ts | 2 +- .../util/settlePendingReceiveTransaction.ts | 2 +- shared/src/enum/PendingTransactionState.ts | 6 + shared/src/enum/index.ts | 1 + 17 files changed, 193 insertions(+), 71 deletions(-) create mode 100644 database/src/queries/pendingTransactions.test.ts create mode 100644 database/src/queries/pendingTransactions.ts create mode 100644 database/src/seeds/factory/pendingTransaction.ts create mode 100644 shared/src/enum/PendingTransactionState.ts diff --git a/backend/src/graphql/enum/PendingTransactionState.ts b/backend/src/graphql/enum/PendingTransactionState.ts index d89b0b0eb..e59f3fd7d 100644 --- a/backend/src/graphql/enum/PendingTransactionState.ts +++ b/backend/src/graphql/enum/PendingTransactionState.ts @@ -1,11 +1,5 @@ import { registerEnumType } from 'type-graphql' - -export enum PendingTransactionState { - NEW = 1, - PENDING = 2, - SETTLED = 3, - REVERTED = 4, -} +import { PendingTransactionState } from 'shared' registerEnumType(PendingTransactionState, { name: 'PendingTransactionState', // this one is mandatory diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 9dedb395f..67dcc4432 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -1,5 +1,6 @@ import { AppDatabase, + countOpenPendingTransactions, Community as DbCommunity, PendingTransaction as DbPendingTransaction, Transaction as dbTransaction, @@ -14,7 +15,7 @@ import { In, IsNull } from 'typeorm' import { Paginated } from '@arg/Paginated' import { TransactionSendArgs } from '@arg/TransactionSendArgs' import { Order } from '@enum/Order' -import { PendingTransactionState } from '@enum/PendingTransactionState' +import { PendingTransactionState } from 'shared' import { TransactionTypeId } from '@enum/TransactionTypeId' import { Transaction } from '@model/Transaction' import { TransactionList } from '@model/TransactionList' @@ -68,19 +69,7 @@ export const executeTransaction = async ( try { logger.info('executeTransaction', amount, memo, sender, recipient) - const openSenderPendingTx = await DbPendingTransaction.count({ - where: [ - { userGradidoID: sender.gradidoID, state: PendingTransactionState.NEW }, - { linkedUserGradidoID: sender.gradidoID, state: PendingTransactionState.NEW }, - ], - }) - const openReceiverPendingTx = await DbPendingTransaction.count({ - where: [ - { userGradidoID: recipient.gradidoID, state: PendingTransactionState.NEW }, - { linkedUserGradidoID: recipient.gradidoID, state: PendingTransactionState.NEW }, - ], - }) - if (openSenderPendingTx > 0 || openReceiverPendingTx > 0) { + if (await countOpenPendingTransactions([sender.gradidoID, recipient.gradidoID]) > 0) { throw new LogError( `There exist still ongoing 'Pending-Transactions' for the involved users on sender-side!`, ) diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 4cbf9b570..2d85a6c1a 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -6,6 +6,7 @@ import { PendingTransactionLoggingView, CommunityLoggingView, UserLoggingView, + countOpenPendingTransactions, } from 'database' import { Decimal } from 'decimal.js-light' @@ -15,7 +16,7 @@ import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0 import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs' import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult' import { SendCoinsClientFactory } from '@/federation/client/SendCoinsClientFactory' -import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState' +import { PendingTransactionState } from 'shared' import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { LogError } from '@/server/LogError' @@ -53,19 +54,7 @@ export async function processXComPendingSendCoins( } ) } - const openSenderPendingTx = await DbPendingTransaction.count({ - where: [ - { userGradidoID: sender.gradidoID, state: PendingTransactionState.NEW }, - { linkedUserGradidoID: sender.gradidoID, state: PendingTransactionState.NEW }, - ], - }) - const openReceiverPendingTx = await DbPendingTransaction.count({ - where: [ - { userGradidoID: recipientIdentifier, state: PendingTransactionState.NEW }, - { linkedUserGradidoID: recipientIdentifier, state: PendingTransactionState.NEW }, - ], - }) - if (openSenderPendingTx > 0 || openReceiverPendingTx > 0) { + if (await countOpenPendingTransactions([sender.gradidoID, recipientIdentifier]) > 0) { throw new LogError( `There exist still ongoing 'Pending-Transactions' for the involved users on sender-side!`, ) diff --git a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts index a10e982b3..2d532113c 100644 --- a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts +++ b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts @@ -7,7 +7,7 @@ import { } from 'database' import { Decimal } from 'decimal.js-light' -import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState' +import { PendingTransactionState } from 'shared' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' import { LogError } from '@/server/LogError' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' diff --git a/database/src/logging/PendingTransactionLogging.view.ts b/database/src/logging/PendingTransactionLogging.view.ts index 9cf27be88..fad2d8f56 100644 --- a/database/src/logging/PendingTransactionLogging.view.ts +++ b/database/src/logging/PendingTransactionLogging.view.ts @@ -1,14 +1,7 @@ import { PendingTransaction, Transaction } from '../entity' import { AbstractLoggingView } from './AbstractLogging.view' import { TransactionLoggingView } from './TransactionLogging.view' - -// TODO: move enum into database, maybe rename database -enum PendingTransactionState { - NEW = 1, - PENDING = 2, - SETTLED = 3, - REVERTED = 4, -} +import { PendingTransactionState } from 'shared' export class PendingTransactionLoggingView extends AbstractLoggingView { public constructor(private self: PendingTransaction) { diff --git a/database/src/queries/index.ts b/database/src/queries/index.ts index c244a4af0..9acf80871 100644 --- a/database/src/queries/index.ts +++ b/database/src/queries/index.ts @@ -2,5 +2,6 @@ import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const' export * from './user' export * from './communities' +export * from './pendingTransactions' export const LOG4JS_QUERIES_CATEGORY_NAME = `${LOG4JS_BASE_CATEGORY_NAME}.queries` diff --git a/database/src/queries/pendingTransactions.test.ts b/database/src/queries/pendingTransactions.test.ts new file mode 100644 index 000000000..72deaac71 --- /dev/null +++ b/database/src/queries/pendingTransactions.test.ts @@ -0,0 +1,124 @@ +import { + PendingTransaction as DbPendingTransaction, + User as DbUser, + UserContact as DbUserContact, + Community as DbCommunity +} from '..' +import { countOpenPendingTransactions } from './pendingTransactions' +import { PendingTransactionState } from 'shared' +import { AppDatabase } from '../AppDatabase' +import { userFactory } from '../seeds/factory/user' +import { pendingTransactionFactory } from '../seeds/factory/pendingTransaction' +import { bibiBloxberg } from '../seeds/users/bibi-bloxberg' +import { peterLustig } from '../seeds/users/peter-lustig' +import { bobBaumeister } from '../seeds/users/bob-baumeister' +import { garrickOllivander } from '../seeds/users/garrick-ollivander' +import { describe, expect, it, beforeAll, afterAll } from 'vitest' +import { createCommunity } from '../seeds/homeCommunity' +import { v4 as uuidv4 } from 'uuid' +import Decimal from 'decimal.js-light' + +const db = AppDatabase.getInstance() + +beforeAll(async () => { + await db.init() +}) +afterAll(async () => { + await db.destroy() +}) + + +describe('countOpenPendingTransactions', () => { + let bibi: DbUser + let peter: DbUser + let bob: DbUser + let garrick: DbUser + beforeAll(async () => { + await DbPendingTransaction.clear() + await DbUser.clear() + await DbUserContact.clear() + await DbCommunity.clear() + + await createCommunity(false) + + bibi = await userFactory(bibiBloxberg) + peter = await userFactory(peterLustig) + bob = await userFactory(bobBaumeister) + garrick = await userFactory(garrickOllivander) + + // Bibi -> Peter + await pendingTransactionFactory( + bibi, + peter, + new Decimal(10), + 'Bibi -> Peter new', + PendingTransactionState.NEW + ) + await pendingTransactionFactory( + bibi, + peter, + new Decimal(100.01), + 'Bibi -> Peter settled', + PendingTransactionState.SETTLED + ) + + // Peter -> Bibi + await pendingTransactionFactory( + peter, + bibi, + new Decimal(12), + 'Peter -> Bibi new', + PendingTransactionState.NEW + ) + + // Bob -> Peter + await pendingTransactionFactory( + bob, + peter, + new Decimal(17.1), + 'Bob -> Peter new', + PendingTransactionState.NEW + ) + + }) + it('should return 0 if called with empty array', async () => { + const count = await countOpenPendingTransactions([]) + expect(count).toBe(0) + }) + + it('should return 0 if called with unknown gradido id', async () => { + const count = await countOpenPendingTransactions([uuidv4()]) + expect(count).toBe(0) + }) + + it('should return 0 if there are no pending transactions for the given user', async () => { + const count = await countOpenPendingTransactions([garrick.gradidoID]) + expect(count).toBe(0) + }) + + it('bibi and peter have two transactions together and peter one additional, should return 3', async () => { + const count = await countOpenPendingTransactions([bibi.gradidoID, peter.gradidoID]) + expect(count).toBe(3) + }) + + it('peter and bob have one transaction together, peter two additional, should return 3', async () => { + const count = await countOpenPendingTransactions([peter.gradidoID, bob.gradidoID]) + expect(count).toBe(3) + }) + + it('peter has three transactions, should return 3', async () => { + const count = await countOpenPendingTransactions([peter.gradidoID]) + expect(count).toBe(3) + }) + + + it('bibi has two transactions, should return 2', async () => { + const count = await countOpenPendingTransactions([bibi.gradidoID]) + expect(count).toBe(2) + }) + + it('bob has one transaction, should return 1', async () => { + const count = await countOpenPendingTransactions([bob.gradidoID]) + expect(count).toBe(1) + }) +}) diff --git a/database/src/queries/pendingTransactions.ts b/database/src/queries/pendingTransactions.ts new file mode 100644 index 000000000..44bf63f82 --- /dev/null +++ b/database/src/queries/pendingTransactions.ts @@ -0,0 +1,18 @@ +import { PendingTransaction as DbPendingTransaction } from '../entity' +import { In } from 'typeorm' +import { PendingTransactionState } from 'shared' + +/** + * Counts the number of open pending transactions for the given users. + * @param users The users gradidoID to count the pending transactions for + * @returns The number of open pending transactions + */ +export async function countOpenPendingTransactions(users: string[]): Promise { + const count = await DbPendingTransaction.count({ + where: [ + { userGradidoID: In(users), state: PendingTransactionState.NEW }, + { linkedUserGradidoID: In(users), state: PendingTransactionState.NEW }, + ], + }) + return count +} \ No newline at end of file diff --git a/database/src/queries/user.test.ts b/database/src/queries/user.test.ts index 1b0008c94..8cb95e0ac 100644 --- a/database/src/queries/user.test.ts +++ b/database/src/queries/user.test.ts @@ -3,13 +3,12 @@ import { AppDatabase } from '../AppDatabase' import { aliasExists, findUserByIdentifier } from './user' import { userFactory } from '../seeds/factory/user' import { bibiBloxberg } from '../seeds/users/bibi-bloxberg' -import { describe, expect, it, beforeAll, afterAll } from 'vitest' +import { describe, expect, it, beforeAll, afterAll, beforeEach, } from 'vitest' import { createCommunity } from '../seeds/homeCommunity' import { peterLustig } from '../seeds/users/peter-lustig' import { bobBaumeister } from '../seeds/users/bob-baumeister' import { getLogger, printLogs, clearLogs } from '../../../config-schema/test/testSetup.vitest' import { LOG4JS_QUERIES_CATEGORY_NAME } from '.' -import { beforeEach } from 'node:test' const db = AppDatabase.getInstance() const userIdentifierLoggerName = `${LOG4JS_QUERIES_CATEGORY_NAME}.user.findUserByIdentifier` diff --git a/database/src/seeds/factory/pendingTransaction.ts b/database/src/seeds/factory/pendingTransaction.ts new file mode 100644 index 000000000..2e8c7d256 --- /dev/null +++ b/database/src/seeds/factory/pendingTransaction.ts @@ -0,0 +1,23 @@ +import { User as DbUser, PendingTransaction as DbPendingTransaction } from '../..' +import { PendingTransactionState } from 'shared' +import { Decimal } from 'decimal.js-light' + +export async function pendingTransactionFactory( + sender: DbUser, + receiver: DbUser, + amount: Decimal, + memo: string, + state: PendingTransactionState, +) { + const pendingTransaction = new DbPendingTransaction() + pendingTransaction.state = state + pendingTransaction.memo = memo + pendingTransaction.amount = amount + pendingTransaction.userId = sender.id + pendingTransaction.userGradidoID = sender.gradidoID + pendingTransaction.userCommunityUuid = sender.communityUuid! + pendingTransaction.linkedUserId = receiver.id + pendingTransaction.linkedUserGradidoID = receiver.gradidoID + pendingTransaction.linkedUserCommunityUuid = receiver.communityUuid! + await pendingTransaction.save() +} diff --git a/database/src/seeds/factory/user.ts b/database/src/seeds/factory/user.ts index 293ee4bb3..3772fe66d 100644 --- a/database/src/seeds/factory/user.ts +++ b/database/src/seeds/factory/user.ts @@ -14,7 +14,9 @@ export const userFactory = async (user: UserInterface): Promise => { let dbUser = new User() dbUser.firstName = user.firstName ?? '' dbUser.lastName = user.lastName ?? '' - dbUser.alias = user.alias ?? '' + if (user.alias) { + dbUser.alias = user.alias + } dbUser.language = user.language ?? 'en' dbUser.createdAt = user.createdAt ?? new Date() dbUser.deletedAt = user.deletedAt ?? null diff --git a/federation/src/graphql/api/1_0/enum/PendingTransactionState.ts b/federation/src/graphql/api/1_0/enum/PendingTransactionState.ts index d89b0b0eb..e59f3fd7d 100644 --- a/federation/src/graphql/api/1_0/enum/PendingTransactionState.ts +++ b/federation/src/graphql/api/1_0/enum/PendingTransactionState.ts @@ -1,11 +1,5 @@ import { registerEnumType } from 'type-graphql' - -export enum PendingTransactionState { - NEW = 1, - PENDING = 2, - SETTLED = 3, - REVERTED = 4, -} +import { PendingTransactionState } from 'shared' registerEnumType(PendingTransactionState, { name: 'PendingTransactionState', // this one is mandatory diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 933c9d1eb..35850fc3e 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -10,7 +10,7 @@ import Decimal from 'decimal.js-light' import { getLogger } from 'log4js' import { Arg, Mutation, Resolver } from 'type-graphql' import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const' -import { PendingTransactionState } from '../enum/PendingTransactionState' +import { PendingTransactionState } from 'shared' import { TransactionTypeId } from '../enum/TransactionTypeId' import { SendCoinsArgsLoggingView } from '../logger/SendCoinsArgsLogging.view' import { SendCoinsArgs } from '../model/SendCoinsArgs' @@ -20,7 +20,7 @@ import { calculateRecipientBalance } from '../util/calculateRecipientBalance' import { revertSettledReceiveTransaction } from '../util/revertSettledReceiveTransaction' import { settlePendingReceiveTransaction } from '../util/settlePendingReceiveTransaction' import { storeForeignUser } from '../util/storeForeignUser' - +import { countOpenPendingTransactions } from 'database' const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.api.1_0.resolver.SendCoinsResolver`) @Resolver() @@ -56,19 +56,8 @@ export class SendCoinsResolver { homeCom.name, ) } - const openSenderPendingTx = await DbPendingTransaction.count({ - where: [ - { userGradidoID: args.senderUserUuid, state: PendingTransactionState.NEW }, - { linkedUserGradidoID: args.senderUserUuid, state: PendingTransactionState.NEW }, - ], - }) - const openReceiverPendingTx = await DbPendingTransaction.count({ - where: [ - { userGradidoID: receiverUser.gradidoID, state: PendingTransactionState.NEW }, - { linkedUserGradidoID: receiverUser.gradidoID, state: PendingTransactionState.NEW }, - ], - }) - if (openSenderPendingTx > 0 || openReceiverPendingTx > 0) { + + if (await countOpenPendingTransactions([args.senderUserUuid, receiverUser.gradidoID]) > 0) { throw new LogError( `There exist still ongoing 'Pending-Transactions' for the involved users on receiver-side!`, ) diff --git a/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts index 685a7b9ff..9b311be30 100644 --- a/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts @@ -10,7 +10,7 @@ import { Transaction as dbTransaction, } from 'database' -import { PendingTransactionState } from '../enum/PendingTransactionState' +import { PendingTransactionState } from 'shared' import { LogError } from '@/server/LogError' import { getLogger } from 'log4js' diff --git a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts index 49e8ca203..6396174bc 100644 --- a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts @@ -9,7 +9,7 @@ import { UserLoggingView, Transaction as dbTransaction, } from 'database' -import { PendingTransactionState } from '../enum/PendingTransactionState' +import { PendingTransactionState } from 'shared' import { LogError } from '@/server/LogError' diff --git a/shared/src/enum/PendingTransactionState.ts b/shared/src/enum/PendingTransactionState.ts new file mode 100644 index 000000000..644fdc8dc --- /dev/null +++ b/shared/src/enum/PendingTransactionState.ts @@ -0,0 +1,6 @@ +export enum PendingTransactionState { + NEW = 1, + PENDING = 2, + SETTLED = 3, + REVERTED = 4, +} diff --git a/shared/src/enum/index.ts b/shared/src/enum/index.ts index efd722624..adadf38ce 100644 --- a/shared/src/enum/index.ts +++ b/shared/src/enum/index.ts @@ -2,3 +2,4 @@ export * from './RoleNames' export * from './UserContactType' export * from './PasswordEncryptionType' export * from './OptInType' +export * from './PendingTransactionState' From 7bd3d7022f2246974a66472201eed60276fbff8a Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 27 Jun 2025 14:20:26 +0200 Subject: [PATCH 44/46] add missing test logger function mock --- config-schema/test/testSetup.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config-schema/test/testSetup.ts b/config-schema/test/testSetup.ts index ef471cc03..530b5cbc8 100644 --- a/config-schema/test/testSetup.ts +++ b/config-schema/test/testSetup.ts @@ -83,6 +83,9 @@ const getLoggerMocked = jest.fn().mockImplementation((param: any) => { }), clearContext: jest.fn(() => { fakeLogger.context.clear() + }), + isDebugEnabled: jest.fn(() => { + return true }) } loggers[param] = fakeLogger From 5683f686d3c2a08df6d2412139d6e52544012278 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 27 Jun 2025 14:25:03 +0200 Subject: [PATCH 45/46] add isDebugEnabled to all logger mocks --- config-schema/test/testSetup.bun.ts | 3 +++ config-schema/test/testSetup.vitest.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/config-schema/test/testSetup.bun.ts b/config-schema/test/testSetup.bun.ts index d2a4002bb..f64b81279 100644 --- a/config-schema/test/testSetup.bun.ts +++ b/config-schema/test/testSetup.bun.ts @@ -81,6 +81,9 @@ const getLoggerMocked = mock().mockImplementation((param: any) => { }), clearContext: jest.fn(() => { fakeLogger.context.clear() + }), + isDebugEnabled: jest.fn(() => { + return true }) } loggers[param] = fakeLogger diff --git a/config-schema/test/testSetup.vitest.ts b/config-schema/test/testSetup.vitest.ts index 5d404c806..dac4b0292 100644 --- a/config-schema/test/testSetup.vitest.ts +++ b/config-schema/test/testSetup.vitest.ts @@ -83,6 +83,9 @@ const getLoggerMocked = vi.fn().mockImplementation((param: any) => { }), clearContext: vi.fn(() => { fakeLogger.context.clear() + }), + isDebugEnabled: vi.fn(() => { + return true }) } loggers[param] = fakeLogger From e381c4ac209365f9ec693c25aa24382fe46326ed Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 27 Jun 2025 14:29:43 +0200 Subject: [PATCH 46/46] fix naming --- .github/workflows/test_database.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_database.yml b/.github/workflows/test_database.yml index d9a2d3acc..2c1a7ef94 100644 --- a/.github/workflows/test_database.yml +++ b/.github/workflows/test_database.yml @@ -36,7 +36,7 @@ jobs: database_migration_test: if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' || needs.files-changed.outputs.shared == 'true' - name: Database Migration Test - Up, Test + Reset + name: Database Migration Test - Up + Reset needs: files-changed runs-on: ubuntu-latest steps: