mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 2602-Text-changes-on-Community
This commit is contained in:
commit
858ebcf559
98
.github/workflows/test_dht-node.yml
vendored
Normal file
98
.github/workflows/test_dht-node.yml
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
name: gradido test_dht-node CI
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST #####################################################
|
||||
##############################################################################
|
||||
build:
|
||||
name: Docker Build Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build `test` image
|
||||
run: |
|
||||
docker build --target test -t "gradido/dht-node:test" -f dht-node/Dockerfile .
|
||||
docker save "gradido/dht-node:test" > /tmp/dht-node.tar
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: docker-dht-node-test
|
||||
path: /tmp/dht-node.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT ##################################################################
|
||||
##############################################################################
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Docker Image
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-dht-node-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/dht-node.tar
|
||||
|
||||
- name: Lint
|
||||
run: docker run --rm gradido/dht-node:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: UNIT TEST #############################################################
|
||||
##############################################################################
|
||||
unit_test:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Docker Image
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docker-dht-node-test
|
||||
path: /tmp
|
||||
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/dht-node.tar
|
||||
|
||||
- name: docker-compose mariadb
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
|
||||
|
||||
- name: Sleep for 30 seconds
|
||||
run: sleep 30s
|
||||
shell: bash
|
||||
|
||||
- name: docker-compose database
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
|
||||
|
||||
- name: Sleep for 30 seconds
|
||||
run: sleep 30s
|
||||
shell: bash
|
||||
|
||||
#- name: Unit tests
|
||||
# run: cd database && yarn && yarn build && cd ../dht-node && yarn && yarn test
|
||||
- name: Unit tests
|
||||
run: |
|
||||
docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net -v ~/coverage:/app/coverage --rm gradido/dht-node:test yarn run test
|
||||
cp -r ~/coverage ./coverage
|
||||
|
||||
- name: Coverage check
|
||||
uses: webcraftmedia/coverage-check-action@master
|
||||
with:
|
||||
report_name: Coverage dht-node
|
||||
type: lcov
|
||||
#result_path: ./dht-node/coverage/lcov.info
|
||||
result_path: ./coverage/lcov.info
|
||||
min_coverage: 79
|
||||
token: ${{ github.token }}
|
||||
22
dht-node/.env.dist
Normal file
22
dht-node/.env.dist
Normal file
@ -0,0 +1,22 @@
|
||||
CONFIG_VERSION=v1.2023-01-01
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=gradido_community
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=typeorm.dht-node.log
|
||||
|
||||
# EventProtocol
|
||||
EVENT_PROTOCOL_DISABLED=false
|
||||
|
||||
# SET LOG LEVEL AS NEEDED IN YOUR .ENV
|
||||
# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal
|
||||
# LOG_LEVEL=info
|
||||
|
||||
# Federation
|
||||
# if you set the value of FEDERATION_DHT_TOPIC, the DHT hyperswarm will start to announce and listen
|
||||
# on an hash created from this topic
|
||||
FEDERATION_DHT_TOPIC=GRADIDO_HUB
|
||||
# FEDERATION_DHT_SEED=64ebcb0e3ad547848fef4197c6e2332f
|
||||
17
dht-node/.env.template
Normal file
17
dht-node/.env.template
Normal file
@ -0,0 +1,17 @@
|
||||
CONFIG_VERSION=$BACKEND_CONFIG_VERSION
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=$DB_USER
|
||||
DB_PASSWORD=$DB_PASSWORD
|
||||
DB_DATABASE=gradido_community
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=$TYPEORM_LOGGING_RELATIVE_PATH
|
||||
|
||||
# EventProtocol
|
||||
EVENT_PROTOCOL_DISABLED=$EVENT_PROTOCOL_DISABLED
|
||||
|
||||
# Federation
|
||||
FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC
|
||||
FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED
|
||||
FEDERATION_COMMUNITY_URL=$FEDERATION_COMMUNITY_URL
|
||||
3
dht-node/.eslintignore
Normal file
3
dht-node/.eslintignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
**/*.min.js
|
||||
build
|
||||
26
dht-node/.eslintrc.js
Normal file
26
dht-node/.eslintrc.js
Normal file
@ -0,0 +1,26 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
// jest: true,
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['prettier', '@typescript-eslint' /*, 'jest' */],
|
||||
extends: [
|
||||
'standard',
|
||||
'eslint:recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
'no-console': ['error'],
|
||||
'no-debugger': 'error',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
htmlWhitespaceSensitivity: 'ignore',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
8
dht-node/.gitignore
vendored
Normal file
8
dht-node/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/node_modules/
|
||||
/.env
|
||||
/.env.bak
|
||||
/build/
|
||||
package-json.lock
|
||||
coverage
|
||||
# emacs
|
||||
*~
|
||||
9
dht-node/.prettierrc.js
Normal file
9
dht-node/.prettierrc.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
printWidth: 100,
|
||||
singleQuote: true,
|
||||
trailingComma: "all",
|
||||
tabWidth: 2,
|
||||
bracketSpacing: true,
|
||||
endOfLine: "auto",
|
||||
};
|
||||
119
dht-node/Dockerfile
Normal file
119
dht-node/Dockerfile
Normal file
@ -0,0 +1,119 @@
|
||||
##################################################################################
|
||||
# BASE ###########################################################################
|
||||
##################################################################################
|
||||
FROM node:19.5.0-alpine3.17 as base
|
||||
#FROM ubuntu:latest as base
|
||||
|
||||
# ENVs (available in production aswell, can be overwritten by commandline or env file)
|
||||
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
|
||||
ENV DOCKER_WORKDIR="/app"
|
||||
## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0
|
||||
ENV BUILD_DATE="1970-01-01T00:00:00.00Z"
|
||||
## We cannot do $(npm run version).${BUILD_NUMBER} here so we default to 0.0.0.0
|
||||
ENV BUILD_VERSION="0.0.0.0"
|
||||
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
|
||||
ENV BUILD_COMMIT="0000000"
|
||||
## SET NODE_ENV
|
||||
ENV NODE_ENV="production"
|
||||
## App relevant Envs
|
||||
#ENV PORT="5000"
|
||||
|
||||
# Labels
|
||||
LABEL org.label-schema.build-date="${BUILD_DATE}"
|
||||
LABEL org.label-schema.name="gradido:dht-node"
|
||||
LABEL org.label-schema.description="Gradido dht-node"
|
||||
LABEL org.label-schema.usage="https://github.com/gradido/gradido/blob/master/README.md"
|
||||
LABEL org.label-schema.url="https://gradido.net"
|
||||
LABEL org.label-schema.vcs-url="https://github.com/gradido/gradido/tree/master/dht-node"
|
||||
LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}"
|
||||
LABEL org.label-schema.vendor="Gradido Community"
|
||||
LABEL org.label-schema.version="${BUILD_VERSION}"
|
||||
LABEL org.label-schema.schema-version="1.0"
|
||||
LABEL maintainer="support@gradido.net"
|
||||
|
||||
# Install Additional Software
|
||||
## install: sodium requirements
|
||||
RUN apk add --no-cache --virtual build-deps python3 alpine-sdk autoconf libtool automake && \
|
||||
mkdir -p /prebuilds && cd /prebuilds && npm init -y && npm install sodium-native@3.1.1 && \
|
||||
apk del build-deps
|
||||
ENV SODIUM_NATIVE_PREBUILD=/prebuilds/node_modules/sodium-native/
|
||||
|
||||
# Settings
|
||||
## Expose Container Port
|
||||
#EXPOSE ${PORT}
|
||||
|
||||
## Workdir
|
||||
RUN mkdir -p ${DOCKER_WORKDIR}
|
||||
WORKDIR ${DOCKER_WORKDIR}
|
||||
|
||||
RUN mkdir -p /database
|
||||
|
||||
##################################################################################
|
||||
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
|
||||
##################################################################################
|
||||
FROM base as development
|
||||
|
||||
# We don't need to copy or build anything since we gonna bind to the
|
||||
# local filesystem which will need a rebuild anyway
|
||||
|
||||
# Run command
|
||||
# (for development we need to execute yarn install since the
|
||||
# node_modules are on another volume and need updating)
|
||||
CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /app && yarn install && yarn run dev"
|
||||
|
||||
##################################################################################
|
||||
# BUILD (Does contain all files and is therefore bloated) ########################
|
||||
##################################################################################
|
||||
FROM base as build
|
||||
|
||||
# Copy everything from dht-node
|
||||
COPY ./dht-node/ ./
|
||||
# Copy everything from database
|
||||
COPY ./database/ ../database/
|
||||
|
||||
# yarn install dht-node
|
||||
RUN yarn install --production=false --frozen-lockfile --non-interactive
|
||||
|
||||
# yarn install database
|
||||
RUN cd ../database && yarn install --production=false --frozen-lockfile --non-interactive
|
||||
|
||||
# yarn build
|
||||
RUN yarn run build
|
||||
|
||||
# yarn build database
|
||||
RUN cd ../database && yarn run build
|
||||
|
||||
##################################################################################
|
||||
# TEST ###########################################################################
|
||||
##################################################################################
|
||||
FROM build as test
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn run start"
|
||||
|
||||
##################################################################################
|
||||
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #
|
||||
##################################################################################
|
||||
FROM base as production
|
||||
|
||||
# Copy "binary"-files from build image
|
||||
COPY --from=build ${DOCKER_WORKDIR}/build ./build
|
||||
COPY --from=build ${DOCKER_WORKDIR}/../database/build ../database/build
|
||||
# We also copy the node_modules express and serve-static for the run script
|
||||
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
|
||||
COPY --from=build ${DOCKER_WORKDIR}/../database/node_modules ../database/node_modules
|
||||
|
||||
# Copy static files
|
||||
# COPY --from=build ${DOCKER_WORKDIR}/public ./public
|
||||
# Copy package.json for script definitions (lock file should not be needed)
|
||||
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
|
||||
# Copy tsconfig.json to provide alias path definitions
|
||||
COPY --from=build ${DOCKER_WORKDIR}/tsconfig.json ./tsconfig.json
|
||||
# Copy log4js-config.json to provide log configuration
|
||||
COPY --from=build ${DOCKER_WORKDIR}/log4js-config.json ./log4js-config.json
|
||||
|
||||
# Copy run scripts run/
|
||||
# COPY --from=build ${DOCKER_WORKDIR}/run ./run
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn run start"
|
||||
22
dht-node/jest.config.js
Normal file
22
dht-node/jest.config.js
Normal file
@ -0,0 +1,22 @@
|
||||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
preset: 'ts-jest',
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
|
||||
setupFiles: ['<rootDir>/test/testSetup.ts'],
|
||||
setupFilesAfterEnv: [],
|
||||
modulePathIgnorePatterns: ['<rootDir>/build/'],
|
||||
moduleNameMapper: {
|
||||
'@/(.*)': '<rootDir>/src/$1',
|
||||
'@test/(.*)': '<rootDir>/test/$1',
|
||||
'@entity/(.*)':
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/entity/$1'
|
||||
: '<rootDir>/../database/build/entity/$1',
|
||||
'@dbTools/(.*)':
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/src/$1'
|
||||
: '<rootDir>/../database/build/src/$1',
|
||||
},
|
||||
}
|
||||
69
dht-node/log4js-config.json
Normal file
69
dht-node/log4js-config.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"appenders":
|
||||
{
|
||||
"dht":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/dht-node/apiversion-%v.log",
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %c [%X{user}] [%f : %l] - %m"
|
||||
},
|
||||
"keepFileExt" : true,
|
||||
"fileNameSep" : "_",
|
||||
"numBackups" : 30
|
||||
},
|
||||
"errorFile":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/dht-node/errors.log",
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %c [%X{user}] [%f : %l] - %m"
|
||||
},
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
45
dht-node/package.json
Normal file
45
dht-node/package.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "gradido-dht-node",
|
||||
"version": "1.0.0",
|
||||
"description": "Gradido dht-node module",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/",
|
||||
"author": "Claus-Peter Huebner",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"clean": "tsc --build --clean",
|
||||
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
|
||||
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r dotenv/config -r tsconfig-paths/register src/index.ts",
|
||||
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
|
||||
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hyperswarm/dht": "^6.4.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "10.0.0",
|
||||
"log4js": "^6.7.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.1.2",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/dotenv": "^8.2.0",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/node": "^18.11.18",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"eslint": "^8.31.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-plugin-import": "^2.23.4",
|
||||
"eslint-plugin-n": "^15.6.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"jest": "^27.2.4",
|
||||
"prettier": "^2.3.1",
|
||||
"ts-jest": "^27.0.5"
|
||||
}
|
||||
}
|
||||
62
dht-node/src/config/index.ts
Normal file
62
dht-node/src/config/index.ts
Normal file
@ -0,0 +1,62 @@
|
||||
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env)
|
||||
import dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
const constants = {
|
||||
DB_VERSION: '0059-add_hide_amount_to_users',
|
||||
LOG4JS_CONFIG: 'log4js-config.json',
|
||||
// default log level on production should be info
|
||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||
CONFIG_VERSION: {
|
||||
DEFAULT: 'DEFAULT',
|
||||
EXPECTED: 'v1.2023-01-01',
|
||||
CURRENT: '',
|
||||
},
|
||||
}
|
||||
|
||||
const server = {
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' || false,
|
||||
}
|
||||
|
||||
const database = {
|
||||
DB_HOST: process.env.DB_HOST || 'localhost',
|
||||
DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306,
|
||||
DB_USER: process.env.DB_USER || 'root',
|
||||
DB_PASSWORD: process.env.DB_PASSWORD || '',
|
||||
DB_DATABASE: process.env.DB_DATABASE || 'gradido_community',
|
||||
TYPEORM_LOGGING_RELATIVE_PATH:
|
||||
process.env.TYPEORM_LOGGING_RELATIVE_PATH || 'typeorm.dht-node.log',
|
||||
}
|
||||
|
||||
const eventProtocol = {
|
||||
// global switch to enable writing of EventProtocol-Entries
|
||||
EVENT_PROTOCOL_DISABLED: process.env.EVENT_PROTOCOL_DISABLED === 'true' || false,
|
||||
}
|
||||
|
||||
const federation = {
|
||||
FEDERATION_DHT_TOPIC: process.env.FEDERATION_DHT_TOPIC || 'GRADIDO_HUB',
|
||||
FEDERATION_DHT_SEED: process.env.FEDERATION_DHT_SEED || null,
|
||||
FEDERATION_COMMUNITY_URL: process.env.FEDERATION_COMMUNITY_URL || null,
|
||||
}
|
||||
|
||||
// Check config version
|
||||
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
|
||||
if (
|
||||
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
|
||||
constants.CONFIG_VERSION.CURRENT,
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
|
||||
)
|
||||
}
|
||||
|
||||
const CONFIG = {
|
||||
...constants,
|
||||
...server,
|
||||
...database,
|
||||
...eventProtocol,
|
||||
...federation,
|
||||
}
|
||||
|
||||
export default CONFIG
|
||||
1
dht-node/src/dht_node/@types/@hyperswarm__dht/index.d.ts
vendored
Normal file
1
dht-node/src/dht_node/@types/@hyperswarm__dht/index.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
declare module '@hyperswarm/dht'
|
||||
798
dht-node/src/dht_node/index.test.ts
Normal file
798
dht-node/src/dht_node/index.test.ts
Normal file
@ -0,0 +1,798 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import { startDHT } from './index'
|
||||
import DHT from '@hyperswarm/dht'
|
||||
import CONFIG from '@/config'
|
||||
import { logger } from '@test/testSetup'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { testEnvironment, cleanDB } from '@test/helpers'
|
||||
|
||||
CONFIG.FEDERATION_DHT_SEED = '64ebcb0e3ad547848fef4197c6e2332f'
|
||||
|
||||
jest.mock('@hyperswarm/dht')
|
||||
|
||||
const TEST_TOPIC = 'gradido_test_topic'
|
||||
|
||||
const keyPairMock = {
|
||||
publicKey: Buffer.from('publicKey'),
|
||||
secretKey: Buffer.from('secretKey'),
|
||||
}
|
||||
|
||||
const serverListenSpy = jest.fn()
|
||||
|
||||
const serverEventMocks: { [key: string]: any } = {}
|
||||
|
||||
const serverOnMock = jest.fn().mockImplementation((key: string, callback) => {
|
||||
serverEventMocks[key] = callback
|
||||
})
|
||||
|
||||
const nodeCreateServerMock = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
on: serverOnMock,
|
||||
listen: serverListenSpy,
|
||||
}
|
||||
})
|
||||
|
||||
const nodeAnnounceMock = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
finished: jest.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
const lookupResultMock = {
|
||||
token: Buffer.from(TEST_TOPIC),
|
||||
from: {
|
||||
id: Buffer.from('somone'),
|
||||
host: '188.95.53.5',
|
||||
port: 63561,
|
||||
},
|
||||
to: { id: null, host: '83.53.31.27', port: 55723 },
|
||||
peers: [
|
||||
{
|
||||
publicKey: Buffer.from('some-public-key'),
|
||||
relayAddresses: [],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const nodeLookupMock = jest.fn().mockResolvedValue([lookupResultMock])
|
||||
|
||||
const socketEventMocks: { [key: string]: any } = {}
|
||||
|
||||
const socketOnMock = jest.fn().mockImplementation((key: string, callback) => {
|
||||
socketEventMocks[key] = callback
|
||||
})
|
||||
|
||||
const socketWriteMock = jest.fn()
|
||||
|
||||
const nodeConnectMock = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
on: socketOnMock,
|
||||
once: socketOnMock,
|
||||
write: socketWriteMock,
|
||||
}
|
||||
})
|
||||
|
||||
DHT.hash.mockImplementation(() => {
|
||||
return Buffer.from(TEST_TOPIC)
|
||||
})
|
||||
|
||||
DHT.keyPair.mockImplementation(() => {
|
||||
return keyPairMock
|
||||
})
|
||||
|
||||
DHT.mockImplementation(() => {
|
||||
return {
|
||||
createServer: nodeCreateServerMock,
|
||||
announce: nodeAnnounceMock,
|
||||
lookup: nodeLookupMock,
|
||||
connect: nodeConnectMock,
|
||||
}
|
||||
})
|
||||
|
||||
let con: any
|
||||
let testEnv: any
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment()
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
})
|
||||
|
||||
describe('federation', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers()
|
||||
})
|
||||
|
||||
describe('call startDHT', () => {
|
||||
const hashSpy = jest.spyOn(DHT, 'hash')
|
||||
const keyPairSpy = jest.spyOn(DHT, 'keyPair')
|
||||
beforeEach(async () => {
|
||||
DHT.mockClear()
|
||||
jest.clearAllMocks()
|
||||
await startDHT(TEST_TOPIC)
|
||||
})
|
||||
|
||||
it('calls DHT.hash', () => {
|
||||
expect(hashSpy).toBeCalledWith(Buffer.from(TEST_TOPIC))
|
||||
})
|
||||
|
||||
it('creates a key pair', () => {
|
||||
expect(keyPairSpy).toBeCalledWith(expect.any(Buffer))
|
||||
})
|
||||
|
||||
it('initializes a new DHT object', () => {
|
||||
expect(DHT).toBeCalledWith({ keyPair: keyPairMock })
|
||||
})
|
||||
|
||||
describe('DHT node', () => {
|
||||
it('creates a server', () => {
|
||||
expect(nodeCreateServerMock).toBeCalled()
|
||||
})
|
||||
|
||||
it('listens on the server', () => {
|
||||
expect(serverListenSpy).toBeCalled()
|
||||
})
|
||||
|
||||
describe('timers', () => {
|
||||
beforeEach(() => {
|
||||
jest.runOnlyPendingTimers()
|
||||
})
|
||||
|
||||
it('announces on topic', () => {
|
||||
expect(nodeAnnounceMock).toBeCalledWith(Buffer.from(TEST_TOPIC), keyPairMock)
|
||||
})
|
||||
|
||||
it('looks up on topic', () => {
|
||||
expect(nodeLookupMock).toBeCalledWith(Buffer.from(TEST_TOPIC))
|
||||
})
|
||||
})
|
||||
|
||||
describe('server connection event', () => {
|
||||
beforeEach(() => {
|
||||
serverEventMocks.connection({
|
||||
remotePublicKey: Buffer.from('another-public-key'),
|
||||
on: socketOnMock,
|
||||
})
|
||||
})
|
||||
|
||||
it('can be triggered', () => {
|
||||
expect(socketOnMock).toBeCalled()
|
||||
})
|
||||
|
||||
describe('socket events', () => {
|
||||
describe('on data', () => {
|
||||
describe('with receiving simply a string', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
socketEventMocks.data(Buffer.from('no-json string'))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith('data: no-json string')
|
||||
})
|
||||
|
||||
it('logs an error of unexpected data format and structure', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Error on receiving data from socket:',
|
||||
new SyntaxError('Unexpected token \'o\', "no-json string" is not valid JSON'),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving array of strings', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
const strArray: string[] = ['invalid type test', 'api', 'url']
|
||||
socketEventMocks.data(Buffer.from(strArray.toString()))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith('data: invalid type test,api,url')
|
||||
})
|
||||
|
||||
it('logs an error of unexpected data format and structure', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Error on receiving data from socket:',
|
||||
new SyntaxError('Unexpected token \'i\', "invalid ty"... is not valid JSON'),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving array of string-arrays', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
const strArray: string[][] = [
|
||||
[`api`, `url`, `invalid type in array test`],
|
||||
[`wrong`, `api`, `url`],
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(strArray.toString()))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'data: api,url,invalid type in array test,wrong,api,url',
|
||||
)
|
||||
})
|
||||
|
||||
it('logs an error of unexpected data format and structure', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Error on receiving data from socket:',
|
||||
new SyntaxError('Unexpected token \'a\', "api,url,in"... is not valid JSON'),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving JSON-Array with too much entries', () => {
|
||||
let jsonArray: { api: string; url: string }[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{ api: 'v1_0', url: 'too much versions at the same time test' },
|
||||
{ api: 'v1_0', url: 'url2' },
|
||||
{ api: 'v1_0', url: 'url3' },
|
||||
{ api: 'v1_0', url: 'url4' },
|
||||
{ api: 'v1_0', url: 'url5' },
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'data: [{"api":"v1_0","url":"too much versions at the same time test"},{"api":"v1_0","url":"url2"},{"api":"v1_0","url":"url3"},{"api":"v1_0","url":"url4"},{"api":"v1_0","url":"url5"}]',
|
||||
)
|
||||
})
|
||||
|
||||
it('logs a warning of too much apiVersion-Definitions', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`received totaly wrong or too much apiVersions-Definition JSON-String: ${JSON.stringify(
|
||||
jsonArray,
|
||||
)}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving wrong but tolerated property data', () => {
|
||||
let jsonArray: any[]
|
||||
let result: DbCommunity[] = []
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{
|
||||
wrong: 'wrong but tolerated property test',
|
||||
api: 'v1_0',
|
||||
url: 'url1',
|
||||
},
|
||||
{
|
||||
api: 'v2_0',
|
||||
url: 'url2',
|
||||
wrong: 'wrong but tolerated property test',
|
||||
},
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
result = await DbCommunity.find()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
it('has two Communty entries in database', () => {
|
||||
expect(result).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('has an entry for api version v1_0', () => {
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
publicKey: expect.any(Buffer),
|
||||
apiVersion: 'v1_0',
|
||||
endPoint: 'url1',
|
||||
lastAnnouncedAt: expect.any(Date),
|
||||
createdAt: expect.any(Date),
|
||||
updatedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
it('has an entry for api version v2_0', () => {
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
publicKey: expect.any(Buffer),
|
||||
apiVersion: 'v2_0',
|
||||
endPoint: 'url2',
|
||||
lastAnnouncedAt: expect.any(Date),
|
||||
createdAt: expect.any(Date),
|
||||
updatedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data but missing api property', () => {
|
||||
let jsonArray: any[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{ test1: 'missing api proterty test', url: 'any url definition as string' },
|
||||
{ api: 'some api', test2: 'missing url property test' },
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`)
|
||||
})
|
||||
|
||||
it('logs a warning of invalid apiVersion-Definition', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data but missing url property', () => {
|
||||
let jsonArray: any[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{ api: 'some api', test2: 'missing url property test' },
|
||||
{ test1: 'missing api proterty test', url: 'any url definition as string' },
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`)
|
||||
})
|
||||
|
||||
it('logs a warning of invalid apiVersion-Definition', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data but wrong type of api property', () => {
|
||||
let jsonArray: any[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{ api: 1, url: 'wrong property type tests' },
|
||||
{ api: 'urltyptest', url: 2 },
|
||||
{ api: 1, url: 2 },
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`)
|
||||
})
|
||||
|
||||
it('logs a warning of invalid apiVersion-Definition', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data but wrong type of url property', () => {
|
||||
let jsonArray: any[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{ api: 'urltyptest', url: 2 },
|
||||
{ api: 1, url: 'wrong property type tests' },
|
||||
{ api: 1, url: 2 },
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`)
|
||||
})
|
||||
|
||||
it('logs a warning of invalid apiVersion-Definition', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data but wrong type of both properties', () => {
|
||||
let jsonArray: any[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{ api: 1, url: 2 },
|
||||
{ api: 'urltyptest', url: 2 },
|
||||
{ api: 1, url: 'wrong property type tests' },
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`)
|
||||
})
|
||||
|
||||
it('logs a warning of invalid apiVersion-Definition', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data but too long api string', () => {
|
||||
let jsonArray: any[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{ api: 'toolong api', url: 'some valid url' },
|
||||
{
|
||||
api: 'valid api',
|
||||
url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic',
|
||||
},
|
||||
{
|
||||
api: 'toolong api',
|
||||
url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic',
|
||||
},
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`)
|
||||
})
|
||||
|
||||
it('logs a warning of invalid apiVersion-Definition', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`received apiVersion with content longer than max length: ${JSON.stringify(
|
||||
jsonArray[0],
|
||||
)}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data but too long url string', () => {
|
||||
let jsonArray: any[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{
|
||||
api: 'api',
|
||||
url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic',
|
||||
},
|
||||
{ api: 'toolong api', url: 'some valid url' },
|
||||
{
|
||||
api: 'toolong api',
|
||||
url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic',
|
||||
},
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`)
|
||||
})
|
||||
|
||||
it('logs a warning of invalid apiVersion-Definition', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`received apiVersion with content longer than max length: ${JSON.stringify(
|
||||
jsonArray[0],
|
||||
)}`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data but both properties with too long strings', () => {
|
||||
let jsonArray: any[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{
|
||||
api: 'toolong api',
|
||||
url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic',
|
||||
},
|
||||
{
|
||||
api: 'api',
|
||||
url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic',
|
||||
},
|
||||
{ api: 'toolong api', url: 'some valid url' },
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data of exact max allowed properties length', () => {
|
||||
let jsonArray: any[]
|
||||
let result: DbCommunity[] = []
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{
|
||||
api: 'valid api',
|
||||
url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
},
|
||||
{
|
||||
api: 'api',
|
||||
url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic',
|
||||
},
|
||||
{ api: 'toolong api', url: 'some valid url' },
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
result = await DbCommunity.find()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
it('has one Communty entry in database', () => {
|
||||
expect(result).toHaveLength(1)
|
||||
})
|
||||
|
||||
it(`has an entry with max content length for api and url`, () => {
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
publicKey: expect.any(Buffer),
|
||||
apiVersion: 'valid api',
|
||||
endPoint:
|
||||
'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
lastAnnouncedAt: expect.any(Date),
|
||||
createdAt: expect.any(Date),
|
||||
updatedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data of exact max allowed buffer length', () => {
|
||||
let jsonArray: any[]
|
||||
let result: DbCommunity[] = []
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{
|
||||
api: 'valid api1',
|
||||
url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
},
|
||||
{
|
||||
api: 'valid api2',
|
||||
url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
},
|
||||
{
|
||||
api: 'valid api3',
|
||||
url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
},
|
||||
{
|
||||
api: 'valid api4',
|
||||
url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
},
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
result = await DbCommunity.find()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
it('has five Communty entries in database', () => {
|
||||
expect(result).toHaveLength(4)
|
||||
})
|
||||
|
||||
it(`has an entry 'valid api1' with max content length for api and url`, () => {
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
publicKey: expect.any(Buffer),
|
||||
apiVersion: 'valid api1',
|
||||
endPoint:
|
||||
'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
lastAnnouncedAt: expect.any(Date),
|
||||
createdAt: expect.any(Date),
|
||||
updatedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
it(`has an entry 'valid api2' with max content length for api and url`, () => {
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
publicKey: expect.any(Buffer),
|
||||
apiVersion: 'valid api2',
|
||||
endPoint:
|
||||
'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
lastAnnouncedAt: expect.any(Date),
|
||||
createdAt: expect.any(Date),
|
||||
updatedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
it(`has an entry 'valid api3' with max content length for api and url`, () => {
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
publicKey: expect.any(Buffer),
|
||||
apiVersion: 'valid api3',
|
||||
endPoint:
|
||||
'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
lastAnnouncedAt: expect.any(Date),
|
||||
createdAt: expect.any(Date),
|
||||
updatedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
it(`has an entry 'valid api4' with max content length for api and url`, () => {
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
publicKey: expect.any(Buffer),
|
||||
apiVersion: 'valid api4',
|
||||
endPoint:
|
||||
'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
lastAnnouncedAt: expect.any(Date),
|
||||
createdAt: expect.any(Date),
|
||||
updatedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with receiving data longer than max allowed buffer length', () => {
|
||||
let jsonArray: any[]
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
jsonArray = [
|
||||
{
|
||||
api: 'Xvalid api1',
|
||||
url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
},
|
||||
{
|
||||
api: 'valid api2',
|
||||
url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
},
|
||||
{
|
||||
api: 'valid api3',
|
||||
url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
},
|
||||
{
|
||||
api: 'valid api4',
|
||||
url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich',
|
||||
},
|
||||
]
|
||||
await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray)))
|
||||
})
|
||||
|
||||
it('logs the received data', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
`received more than max allowed length of data buffer: ${
|
||||
JSON.stringify(jsonArray).length
|
||||
} against 1141 max allowed`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with proper data', () => {
|
||||
let result: DbCommunity[] = []
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
await socketEventMocks.data(
|
||||
Buffer.from(
|
||||
JSON.stringify([
|
||||
{
|
||||
api: 'v1_0',
|
||||
url: 'http://localhost:4000/api/v1_0',
|
||||
},
|
||||
{
|
||||
api: 'v2_0',
|
||||
url: 'http://localhost:4000/api/v2_0',
|
||||
},
|
||||
]),
|
||||
),
|
||||
)
|
||||
result = await DbCommunity.find()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
it('has two Communty entries in database', () => {
|
||||
expect(result).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('has an entry for api version v1_0', () => {
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
publicKey: expect.any(Buffer),
|
||||
apiVersion: 'v1_0',
|
||||
endPoint: 'http://localhost:4000/api/v1_0',
|
||||
lastAnnouncedAt: expect.any(Date),
|
||||
createdAt: expect.any(Date),
|
||||
updatedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
it('has an entry for api version v2_0', () => {
|
||||
expect(result).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
publicKey: expect.any(Buffer),
|
||||
apiVersion: 'v2_0',
|
||||
endPoint: 'http://localhost:4000/api/v2_0',
|
||||
lastAnnouncedAt: expect.any(Date),
|
||||
createdAt: expect.any(Date),
|
||||
updatedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('on open', () => {
|
||||
beforeEach(() => {
|
||||
socketEventMocks.open()
|
||||
})
|
||||
|
||||
it.skip('calls socket write with own api versions', () => {
|
||||
expect(socketWriteMock).toBeCalledWith(
|
||||
Buffer.from(
|
||||
JSON.stringify([
|
||||
{
|
||||
api: 'v1_0',
|
||||
url: 'http://localhost:4000/api/v1_0',
|
||||
},
|
||||
{
|
||||
api: 'v1_1',
|
||||
url: 'http://localhost:4000/api/v1_1',
|
||||
},
|
||||
{
|
||||
api: 'v2_0',
|
||||
url: 'http://localhost:4000/api/v2_0',
|
||||
},
|
||||
]),
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
186
dht-node/src/dht_node/index.ts
Normal file
186
dht-node/src/dht_node/index.ts
Normal file
@ -0,0 +1,186 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import DHT from '@hyperswarm/dht'
|
||||
import { logger } from '@/server/logger'
|
||||
import CONFIG from '@/config'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
|
||||
const KEY_SECRET_SEEDBYTES = 32
|
||||
const getSeed = (): Buffer | null =>
|
||||
CONFIG.FEDERATION_DHT_SEED ? Buffer.alloc(KEY_SECRET_SEEDBYTES, CONFIG.FEDERATION_DHT_SEED) : null
|
||||
|
||||
const POLLTIME = 20000
|
||||
const SUCCESSTIME = 120000
|
||||
const ERRORTIME = 240000
|
||||
const ANNOUNCETIME = 30000
|
||||
|
||||
enum ApiVersionType {
|
||||
V1_0 = 'v1_0',
|
||||
V1_1 = 'v1_1',
|
||||
V2_0 = 'v2_0',
|
||||
}
|
||||
type CommunityApi = {
|
||||
api: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export const startDHT = async (topic: string): Promise<void> => {
|
||||
try {
|
||||
const TOPIC = DHT.hash(Buffer.from(topic))
|
||||
const keyPair = DHT.keyPair(getSeed())
|
||||
logger.info(`keyPairDHT: publicKey=${keyPair.publicKey.toString('hex')}`)
|
||||
logger.debug(`keyPairDHT: secretKey=${keyPair.secretKey.toString('hex')}`)
|
||||
|
||||
const ownApiVersions = Object.values(ApiVersionType).map(function (apiEnum) {
|
||||
const comApi: CommunityApi = {
|
||||
api: apiEnum,
|
||||
url: CONFIG.FEDERATION_COMMUNITY_URL + apiEnum,
|
||||
}
|
||||
return comApi
|
||||
})
|
||||
logger.debug(`ApiList: ${JSON.stringify(ownApiVersions)}`)
|
||||
|
||||
const node = new DHT({ keyPair })
|
||||
|
||||
const server = node.createServer()
|
||||
|
||||
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) {
|
||||
logger.warn(
|
||||
`received more than max allowed length of data buffer: ${data.length} against 1141 max allowed`,
|
||||
)
|
||||
return
|
||||
}
|
||||
logger.info(`data: ${data.toString('ascii')}`)
|
||||
const recApiVersions: CommunityApi[] = JSON.parse(data.toString('ascii'))
|
||||
|
||||
// TODO better to introduce the validation by https://github.com/typestack/class-validator
|
||||
if (!recApiVersions || !Array.isArray(recApiVersions) || recApiVersions.length >= 5) {
|
||||
logger.warn(
|
||||
`received totaly wrong or too much apiVersions-Definition JSON-String: ${JSON.stringify(
|
||||
recApiVersions,
|
||||
)}`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
for (const recApiVersion of recApiVersions) {
|
||||
if (
|
||||
!recApiVersion.api ||
|
||||
typeof recApiVersion.api !== 'string' ||
|
||||
!recApiVersion.url ||
|
||||
typeof recApiVersion.url !== 'string'
|
||||
) {
|
||||
logger.warn(
|
||||
`received invalid apiVersion-Definition: ${JSON.stringify(recApiVersion)}`,
|
||||
)
|
||||
return
|
||||
}
|
||||
// TODO better to introduce the validation on entity-Level by https://github.com/typestack/class-validator
|
||||
if (recApiVersion.api.length > 10 || recApiVersion.url.length > 255) {
|
||||
logger.warn(
|
||||
`received apiVersion with content longer than max length: ${JSON.stringify(
|
||||
recApiVersion,
|
||||
)}`,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const variables = {
|
||||
apiVersion: recApiVersion.api,
|
||||
endPoint: recApiVersion.url,
|
||||
publicKey: socket.remotePublicKey.toString('hex'),
|
||||
lastAnnouncedAt: new Date(),
|
||||
}
|
||||
logger.debug(`upsert with variables=${JSON.stringify(variables)}`)
|
||||
// this will NOT update the updatedAt column, to distingue between a normal update and the last announcement
|
||||
await DbCommunity.createQueryBuilder()
|
||||
.insert()
|
||||
.into(DbCommunity)
|
||||
.values(variables)
|
||||
.orUpdate({
|
||||
conflict_target: ['id', 'publicKey', 'apiVersion'],
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
logger.info(`federation community upserted successfully...`)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('Error on receiving data from socket:', e)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
await server.listen()
|
||||
|
||||
setInterval(async () => {
|
||||
logger.info(`Announcing on topic: ${TOPIC.toString('hex')}`)
|
||||
await node.announce(TOPIC, keyPair).finished()
|
||||
}, ANNOUNCETIME)
|
||||
|
||||
let successfulRequests: string[] = []
|
||||
let errorfulRequests: string[] = []
|
||||
|
||||
setInterval(async () => {
|
||||
logger.info('Refreshing successful nodes')
|
||||
successfulRequests = []
|
||||
}, SUCCESSTIME)
|
||||
|
||||
setInterval(async () => {
|
||||
logger.info('Refreshing errorful nodes')
|
||||
errorfulRequests = []
|
||||
}, ERRORTIME)
|
||||
|
||||
setInterval(async () => {
|
||||
const result = await node.lookup(TOPIC)
|
||||
|
||||
const collectedPubKeys: string[] = []
|
||||
|
||||
for await (const data of result) {
|
||||
data.peers.forEach((peer: any) => {
|
||||
const pubKey = peer.publicKey.toString('hex')
|
||||
if (
|
||||
pubKey !== keyPair.publicKey.toString('hex') &&
|
||||
!successfulRequests.includes(pubKey) &&
|
||||
!errorfulRequests.includes(pubKey) &&
|
||||
!collectedPubKeys.includes(pubKey)
|
||||
) {
|
||||
collectedPubKeys.push(peer.publicKey.toString('hex'))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (collectedPubKeys.length) {
|
||||
logger.info(`Found new peers: ${collectedPubKeys}`)
|
||||
}
|
||||
|
||||
collectedPubKeys.forEach((remotePubKey) => {
|
||||
const socket = node.connect(Buffer.from(remotePubKey, 'hex'))
|
||||
|
||||
// socket.once("connect", function () {
|
||||
// console.log("client side emitted connect");
|
||||
// });
|
||||
|
||||
// socket.once("end", function () {
|
||||
// console.log("client side ended");
|
||||
// });
|
||||
|
||||
socket.once('error', (err: any) => {
|
||||
errorfulRequests.push(remotePubKey)
|
||||
logger.error(`error on peer ${remotePubKey}: ${err.message}`)
|
||||
})
|
||||
|
||||
socket.on('open', function () {
|
||||
socket.write(Buffer.from(JSON.stringify(ownApiVersions)))
|
||||
successfulRequests.push(remotePubKey)
|
||||
})
|
||||
})
|
||||
}, POLLTIME)
|
||||
} catch (err) {
|
||||
logger.error('DHT unexpected error:', err)
|
||||
}
|
||||
}
|
||||
38
dht-node/src/index.ts
Normal file
38
dht-node/src/index.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { startDHT } from '@/dht_node/index'
|
||||
|
||||
// config
|
||||
import CONFIG from './config'
|
||||
import { logger } from './server/logger'
|
||||
import connection from './typeorm/connection'
|
||||
import { checkDBVersion } from './typeorm/DBVersion'
|
||||
|
||||
async function main() {
|
||||
// open mysql connection
|
||||
const con = await connection()
|
||||
if (!con || !con.isConnected) {
|
||||
logger.fatal(`Couldn't open connection to database!`)
|
||||
throw new Error(`Fatal: Couldn't open connection to database`)
|
||||
}
|
||||
|
||||
// check for correct database version
|
||||
const dbVersion = await checkDBVersion(CONFIG.DB_VERSION)
|
||||
if (!dbVersion) {
|
||||
logger.fatal('Fatal: Database Version incorrect')
|
||||
throw new Error('Fatal: Database Version incorrect')
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`starting Federation on ${CONFIG.FEDERATION_DHT_TOPIC} ${
|
||||
CONFIG.FEDERATION_DHT_SEED ? 'with seed...' : 'without seed...'
|
||||
}`,
|
||||
)
|
||||
await startDHT(CONFIG.FEDERATION_DHT_TOPIC)
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
20
dht-node/src/server/logger.ts
Normal file
20
dht-node/src/server/logger.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import log4js from 'log4js'
|
||||
import CONFIG from '@/config'
|
||||
|
||||
import { readFileSync } from 'fs'
|
||||
|
||||
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
|
||||
|
||||
log4js.configure(options)
|
||||
|
||||
const logger = log4js.getLogger('dht')
|
||||
|
||||
export { logger }
|
||||
27
dht-node/src/typeorm/DBVersion.ts
Normal file
27
dht-node/src/typeorm/DBVersion.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Migration } from '@entity/Migration'
|
||||
import { logger } from '@/server/logger'
|
||||
|
||||
const getDBVersion = async (): Promise<string | null> => {
|
||||
try {
|
||||
const dbVersion = await Migration.findOne({ order: { version: 'DESC' } })
|
||||
return dbVersion ? dbVersion.fileName : null
|
||||
} catch (error) {
|
||||
logger.error(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const checkDBVersion = async (DB_VERSION: string): Promise<boolean> => {
|
||||
const dbVersion = await getDBVersion()
|
||||
if (!dbVersion || dbVersion.indexOf(DB_VERSION) === -1) {
|
||||
logger.error(
|
||||
`Wrong database version detected - the dht-node requires '${DB_VERSION}' but found '${
|
||||
dbVersion || 'None'
|
||||
}`,
|
||||
)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export { checkDBVersion, getDBVersion }
|
||||
34
dht-node/src/typeorm/connection.ts
Normal file
34
dht-node/src/typeorm/connection.ts
Normal file
@ -0,0 +1,34 @@
|
||||
// TODO This is super weird - since the entities are defined in another project they have their own globals.
|
||||
// We cannot use our connection here, but must use the external typeorm installation
|
||||
import { Connection, createConnection, FileLogger } from '@dbTools/typeorm'
|
||||
import CONFIG from '@/config'
|
||||
import { entities } from '@entity/index'
|
||||
|
||||
const connection = async (): Promise<Connection | null> => {
|
||||
try {
|
||||
return createConnection({
|
||||
name: 'default',
|
||||
type: 'mysql',
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
username: CONFIG.DB_USER,
|
||||
password: CONFIG.DB_PASSWORD,
|
||||
database: CONFIG.DB_DATABASE,
|
||||
entities,
|
||||
synchronize: false,
|
||||
logging: true,
|
||||
logger: new FileLogger('all', {
|
||||
logPath: CONFIG.TYPEORM_LOGGING_RELATIVE_PATH,
|
||||
}),
|
||||
extra: {
|
||||
charset: 'utf8mb4_unicode_ci',
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export default connection
|
||||
7
dht-node/test/helpers.test.ts
Normal file
7
dht-node/test/helpers.test.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { contributionDateFormatter } from '@test/helpers'
|
||||
|
||||
describe('contributionDateFormatter', () => {
|
||||
it('formats the date correctly', () => {
|
||||
expect(contributionDateFormatter(new Date('Thu Feb 29 2024 13:12:11'))).toEqual('2/29/2024')
|
||||
})
|
||||
})
|
||||
68
dht-node/test/helpers.ts
Normal file
68
dht-node/test/helpers.ts
Normal file
@ -0,0 +1,68 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import CONFIG from '@/config'
|
||||
import connection from '@/typeorm/connection'
|
||||
import { checkDBVersion } from '@/typeorm/DBVersion'
|
||||
import { initialize } from '@dbTools/helpers'
|
||||
import { entities } from '@entity/index'
|
||||
import { logger } from './testSetup'
|
||||
|
||||
export const headerPushMock = jest.fn((t) => {
|
||||
context.token = t.value
|
||||
})
|
||||
|
||||
const context = {
|
||||
token: '',
|
||||
setHeaders: {
|
||||
push: headerPushMock,
|
||||
forEach: jest.fn(),
|
||||
},
|
||||
clientTimezoneOffset: 0,
|
||||
}
|
||||
|
||||
export const cleanDB = async () => {
|
||||
// this only works as long we do not have foreign key constraints
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
await resetEntity(entities[i])
|
||||
}
|
||||
}
|
||||
|
||||
export const testEnvironment = async () => {
|
||||
// open mysql connection
|
||||
const con = await connection()
|
||||
if (!con || !con.isConnected) {
|
||||
logger.fatal(`Couldn't open connection to database!`)
|
||||
throw new Error(`Fatal: Couldn't open connection to database`)
|
||||
}
|
||||
|
||||
// check for correct database version
|
||||
const dbVersion = await checkDBVersion(CONFIG.DB_VERSION)
|
||||
if (!dbVersion) {
|
||||
logger.fatal('Fatal: Database Version incorrect')
|
||||
throw new Error('Fatal: Database Version incorrect')
|
||||
}
|
||||
await initialize()
|
||||
return { con }
|
||||
}
|
||||
|
||||
export const resetEntity = async (entity: any) => {
|
||||
const items = await entity.find({ withDeleted: true })
|
||||
if (items.length > 0) {
|
||||
const ids = items.map((i: any) => i.id)
|
||||
await entity.delete(ids)
|
||||
}
|
||||
}
|
||||
|
||||
export const resetToken = () => {
|
||||
context.token = ''
|
||||
}
|
||||
|
||||
// format date string as it comes from the frontend for the contribution date
|
||||
export const contributionDateFormatter = (date: Date): string => {
|
||||
return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}`
|
||||
}
|
||||
|
||||
export const setClientTimezoneOffset = (offset: number): void => {
|
||||
context.clientTimezoneOffset = offset
|
||||
}
|
||||
22
dht-node/test/testSetup.ts
Normal file
22
dht-node/test/testSetup.ts
Normal file
@ -0,0 +1,22 @@
|
||||
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 }
|
||||
86
dht-node/tsconfig.json
Normal file
86
dht-node/tsconfig.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"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": "./", /* 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'. */
|
||||
"@/*": ["src/*"],
|
||||
"@test/*": ["test/*"],
|
||||
/* external */
|
||||
"@typeorm/*": ["../backend/src/typeorm/*", "../../backend/src/typeorm/*"],
|
||||
"@dbTools/*": ["../database/src/*", "../../database/build/src/*"],
|
||||
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"]
|
||||
},
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
"typeRoots": ["src/dht_node/@types", "node_modules/@types"], /* List of folders to include type definitions from. */
|
||||
// "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": [
|
||||
{
|
||||
"path": "../database/tsconfig.json",
|
||||
// add 'prepend' if you want to include the referenced project in your output file
|
||||
// "prepend": true
|
||||
}
|
||||
]
|
||||
}
|
||||
4320
dht-node/yarn.lock
Normal file
4320
dht-node/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -61,6 +61,29 @@ services:
|
||||
- ./backend:/app
|
||||
- ./database:/database
|
||||
|
||||
########################################################
|
||||
# DHT-NODE #############################################
|
||||
########################################################
|
||||
dht-node:
|
||||
# name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there
|
||||
image: gradido/dht-node:local-development
|
||||
build:
|
||||
target: development
|
||||
networks:
|
||||
- external-net
|
||||
- internal-net
|
||||
environment:
|
||||
- NODE_ENV="development"
|
||||
volumes:
|
||||
# This makes sure the docker container has its own node modules.
|
||||
# Therefore it is possible to have a different node version on the host machine
|
||||
- dht_node_modules:/app/node_modules
|
||||
- dht_database_node_modules:/database/node_modules
|
||||
- dht_database_build:/database/build
|
||||
# bind the local folder to the docker to allow live reload
|
||||
- ./dht-node:/app
|
||||
- ./database:/database
|
||||
|
||||
########################################################
|
||||
# DATABASE ##############################################
|
||||
########################################################
|
||||
@ -129,5 +152,8 @@ volumes:
|
||||
backend_node_modules:
|
||||
backend_database_node_modules:
|
||||
backend_database_build:
|
||||
dht_node_modules:
|
||||
dht_database_node_modules:
|
||||
dht_database_build:
|
||||
database_node_modules:
|
||||
database_build:
|
||||
database_build:
|
||||
@ -111,6 +111,42 @@ services:
|
||||
# <host_machine_directory>:<container_directory> – mirror bidirectional path in local context with path in Docker container
|
||||
- ./logs/backend:/logs/backend
|
||||
|
||||
########################################################
|
||||
# DHT-NODE #############################################
|
||||
########################################################
|
||||
dht-node:
|
||||
# name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there
|
||||
image: gradido/dht-node:local-production
|
||||
build:
|
||||
# since we have to include the entities from ./database we cannot define the context as ./backend
|
||||
# this might blow build image size to the moon ?!
|
||||
context: ./
|
||||
dockerfile: ./dht-node/Dockerfile
|
||||
target: production
|
||||
networks:
|
||||
- internal-net
|
||||
- external-net
|
||||
#ports:
|
||||
# - 5000:5000
|
||||
depends_on:
|
||||
- mariadb
|
||||
restart: always
|
||||
environment:
|
||||
# Envs used in Dockerfile
|
||||
# - DOCKER_WORKDIR="/app"
|
||||
# - PORT=5000
|
||||
- BUILD_DATE
|
||||
- BUILD_VERSION
|
||||
- BUILD_COMMIT
|
||||
- NODE_ENV="production"
|
||||
- DB_HOST=mariadb
|
||||
# Application only envs
|
||||
#env_file:
|
||||
# - ./frontend/.env
|
||||
volumes:
|
||||
# <host_machine_directory>:<container_directory> – mirror bidirectional path in local context with path in Docker container
|
||||
- ./logs/dht-node:/logs/dht-node
|
||||
|
||||
########################################################
|
||||
# DATABASE #############################################
|
||||
########################################################
|
||||
|
||||
@ -296,8 +296,8 @@ export default {
|
||||
},
|
||||
created() {
|
||||
this.updateTransactions(0)
|
||||
this.tabIndex = 1
|
||||
this.$router.push({ path: '/community#my' })
|
||||
this.tabIndex = 0
|
||||
this.$router.push({ path: '/community#edit' })
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user