mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge remote-tracking branch 'origin/3237-feature-connect-gms-api-with-a-new-rest-client' into 3264-feature-gms-publish-user-backend-gradido-communities-dialog
This commit is contained in:
commit
59dfe5cfc3
@ -131,7 +131,7 @@ Each component (frontend, admin, backend and database) has its own `.env` file.
|
||||
|
||||
Each component has a `.env.dist` file. This file contains all environment variables used by the component and can be used as pattern. If you want to use a local `.env`, copy the `.env.dist` and adjust the variables accordingly.
|
||||
|
||||
Each component has a `.env.template` file. These files are very important on deploy.
|
||||
Each component has a `.env.template` file. These files are very important on deploy. They use COMMUNITY_HOST instead of different urls for different modules because in deploy using nginx is expected for routing incoming request to the correct module
|
||||
|
||||
There is one `.env.dist` in the `deployment/bare_metal/` folder. This `.env.dist` contains all variables used by the components, e.g. unites all `.env.dist` from the components. On deploy, we copy this `.env.dist` to `.env` and set all variables in this new file. The deploy script loads this variables and provides them by the `.env.templates` of each component, creating an `.env` for each component (see in `deployment/bare_metal/start.sh` the `envsubst`).
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
GRAPHQL_URI=http://localhost:4000/graphql
|
||||
WALLET_AUTH_URL=http://localhost/authenticate?token={token}
|
||||
WALLET_URL=http://localhost/login
|
||||
GRAPHQL_URL=http://localhost:4000
|
||||
GRAPHQL_PATH=/graphql
|
||||
WALLET_URL=http://localhost
|
||||
WALLET_AUTH_PATH=/authenticate?token={token}
|
||||
WALLET_LOGIN_PATH=/login
|
||||
DEBUG_DISABLE_AUTH=false
|
||||
@ -1,6 +1,8 @@
|
||||
CONFIG_VERSION=$ADMIN_CONFIG_VERSION
|
||||
|
||||
GRAPHQL_URI=$GRAPHQL_URI
|
||||
WALLET_AUTH_URL=$WALLET_AUTH_URL
|
||||
WALLET_URL=$WALLET_URL
|
||||
DEBUG_DISABLE_AUTH=false
|
||||
COMMUNITY_HOST=$COMMUNITY_HOST
|
||||
URL_PROTOCOL=$URL_PROTOCOL
|
||||
WALLET_AUTH_PATH=$WALLET_AUTH_PATH
|
||||
WALLET_LOGIN_PATH=$WALLET_LOGIN_PATH
|
||||
GRAPHQL_PATH=$GRAPHQL_PATH
|
||||
DEBUG_DISABLE_AUTH=false
|
||||
|
||||
@ -38,8 +38,8 @@ export default {
|
||||
name: 'navbar',
|
||||
methods: {
|
||||
async logout() {
|
||||
window.location.assign(CONFIG.WALLET_URL)
|
||||
// window.location = CONFIG.WALLET_URL
|
||||
window.location.assign(CONFIG.WALLET_LOGIN_URL)
|
||||
// window.location = CONFIG.WALLET_LOGIN_URL
|
||||
this.$store.dispatch('logout')
|
||||
await this.$apollo.mutate({
|
||||
mutation: logout,
|
||||
|
||||
@ -7,37 +7,45 @@ const pkg = require('../../package')
|
||||
const constants = {
|
||||
CONFIG_VERSION: {
|
||||
DEFAULT: 'DEFAULT',
|
||||
EXPECTED: 'v1.2022-03-18',
|
||||
EXPECTED: 'v2.2024-01-04',
|
||||
CURRENT: '',
|
||||
},
|
||||
}
|
||||
|
||||
const version = {
|
||||
APP_VERSION: pkg.version,
|
||||
BUILD_COMMIT: process.env.BUILD_COMMIT || null,
|
||||
BUILD_COMMIT: process.env.BUILD_COMMIT ?? null,
|
||||
// self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code
|
||||
BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT || '0000000').slice(0, 7),
|
||||
PORT: process.env.PORT || 8080,
|
||||
BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT ?? '0000000').slice(0, 7),
|
||||
PORT: process.env.PORT ?? 8080,
|
||||
}
|
||||
|
||||
const environment = {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
DEBUG: process.env.NODE_ENV !== 'production' || false,
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' || false,
|
||||
DEBUG: process.env.NODE_ENV !== 'production' ?? false,
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' ?? false,
|
||||
}
|
||||
|
||||
const COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? undefined
|
||||
const URL_PROTOCOL = process.env.URL_PROTOCOL ?? 'http'
|
||||
const COMMUNITY_URL =
|
||||
COMMUNITY_HOST && URL_PROTOCOL ? URL_PROTOCOL + '://' + COMMUNITY_HOST : undefined
|
||||
const WALLET_URL = process.env.WALLET_URL ?? COMMUNITY_URL ?? 'http://localhost'
|
||||
|
||||
const endpoints = {
|
||||
GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql',
|
||||
WALLET_AUTH_URL: process.env.WALLET_AUTH_URL || 'http://localhost/authenticate?token={token}',
|
||||
WALLET_URL: process.env.WALLET_URL || 'http://localhost/login',
|
||||
GRAPHQL_URL:
|
||||
(process.env.GRAPHQL_URL ?? COMMUNITY_URL ?? 'http://localhost:4000') +
|
||||
process.env.GRAPHQL_PATH ?? '/graphql',
|
||||
WALLET_AUTH_URL: WALLET_URL + (process.env.WALLET_AUTH_PATH ?? '/authenticate?token={token}'),
|
||||
WALLET_LOGIN_URL: WALLET_URL + (process.env.WALLET_LOGIN_PATH ?? '/login'),
|
||||
}
|
||||
|
||||
const debug = {
|
||||
DEBUG_DISABLE_AUTH: process.env.DEBUG_DISABLE_AUTH === 'true' || false,
|
||||
DEBUG_DISABLE_AUTH: process.env.DEBUG_DISABLE_AUTH === 'true' ?? false,
|
||||
}
|
||||
|
||||
// Check config version
|
||||
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
|
||||
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,
|
||||
|
||||
@ -16,7 +16,7 @@ const authLink = new ApolloLink((operation, forward) => {
|
||||
return forward(operation).map((response) => {
|
||||
if (response.errors && response.errors[0].message === '403.13 - Client certificate revoked') {
|
||||
store.dispatch('logout', null)
|
||||
window.location.assign(CONFIG.WALLET_URL)
|
||||
window.location.assign(CONFIG.WALLET_LOGIN_URL)
|
||||
return response
|
||||
}
|
||||
const newToken = operation.getContext().response.headers.get('token')
|
||||
|
||||
@ -27,10 +27,10 @@ DLT_CONNECTOR_URL=http://localhost:6010
|
||||
|
||||
# Community
|
||||
COMMUNITY_NAME=Gradido Entwicklung
|
||||
COMMUNITY_URL=http://localhost/
|
||||
COMMUNITY_REGISTER_URL=http://localhost/register
|
||||
COMMUNITY_REDEEM_URL=http://localhost/redeem/{code}
|
||||
COMMUNITY_REDEEM_CONTRIBUTION_URL=http://localhost/redeem/CL-{code}
|
||||
COMMUNITY_URL=http://localhost
|
||||
COMMUNITY_REGISTER_PATH=/register
|
||||
COMMUNITY_REDEEM_PATH=/redeem/{code}
|
||||
COMMUNITY_REDEEM_CONTRIBUTION_PATH=/redeem/CL-{code}
|
||||
COMMUNITY_DESCRIPTION=Die lokale Entwicklungsumgebung von Gradido.
|
||||
COMMUNITY_SUPPORT_MAIL=support@supportmail.com
|
||||
|
||||
@ -47,10 +47,10 @@ EMAIL_SENDER=info@gradido.net
|
||||
EMAIL_PASSWORD=xxx
|
||||
EMAIL_SMTP_URL=gmail.com
|
||||
EMAIL_SMTP_PORT=587
|
||||
EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{optin}{code}
|
||||
EMAIL_LINK_SETPASSWORD=http://localhost/reset-password/{optin}
|
||||
EMAIL_LINK_FORGOTPASSWORD=http://localhost/forgot-password
|
||||
EMAIL_LINK_OVERVIEW=http://localhost/overview
|
||||
EMAIL_LINK_VERIFICATION_PATH=/checkEmail/{optin}{code}
|
||||
EMAIL_LINK_SETPASSWORD_PATH=/reset-password/{optin}
|
||||
EMAIL_LINK_FORGOTPASSWORD_PATH=/forgot-password
|
||||
EMAIL_LINK_OVERVIEW_PATH=/overview
|
||||
EMAIL_CODE_VALID_TIME=1440
|
||||
EMAIL_CODE_REQUEST_TIME=10
|
||||
|
||||
@ -63,6 +63,7 @@ WEBHOOK_ELOPAGE_SECRET=secret
|
||||
|
||||
# Federation
|
||||
FEDERATION_VALIDATE_COMMUNITY_TIMER=60000
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED=false
|
||||
|
||||
# GMS
|
||||
# GMS_ACTIVE=true
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# must match the CONFIG_VERSION.EXPECTED definition in scr/config/index.ts
|
||||
CONFIG_VERSION=v21.2023-11-15
|
||||
CONFIG_VERSION=$BACKEND_CONFIG_VERSION
|
||||
|
||||
# Server
|
||||
JWT_SECRET=$JWT_SECRET
|
||||
@ -25,14 +25,15 @@ KLICKTIPP_APIKEY_EN=$KLICKTIPP_APIKEY_EN
|
||||
|
||||
# DltConnector
|
||||
DLT_CONNECTOR=$DLT_CONNECTOR
|
||||
DLT_CONNECTOR_URL=$DLT_CONNECTOR_URL
|
||||
DLT_CONNECTOR_PORT=$DLT_CONNECTOR_PORT
|
||||
|
||||
# Community
|
||||
COMMUNITY_HOST=$COMMUNITY_HOST
|
||||
URL_PROTOCOL=$URL_PROTOCOL
|
||||
COMMUNITY_NAME=$COMMUNITY_NAME
|
||||
COMMUNITY_URL=$COMMUNITY_URL
|
||||
COMMUNITY_REGISTER_URL=$COMMUNITY_REGISTER_URL
|
||||
COMMUNITY_REDEEM_URL=$COMMUNITY_REDEEM_URL
|
||||
COMMUNITY_REDEEM_CONTRIBUTION_URL=$COMMUNITY_REDEEM_CONTRIBUTION_URL
|
||||
COMMUNITY_REGISTER_PATH=$COMMUNITY_REGISTER_PATH
|
||||
COMMUNITY_REDEEM_PATH=$COMMUNITY_REDEEM_PATH
|
||||
COMMUNITY_REDEEM_CONTRIBUTION_PATH=$COMMUNITY_REDEEM_CONTRIBUTION_PATH
|
||||
COMMUNITY_DESCRIPTION=$COMMUNITY_DESCRIPTION
|
||||
COMMUNITY_SUPPORT_MAIL=$COMMUNITY_SUPPORT_MAIL
|
||||
|
||||
@ -48,12 +49,12 @@ EMAIL_USERNAME=$EMAIL_USERNAME
|
||||
EMAIL_SENDER=$EMAIL_SENDER
|
||||
EMAIL_PASSWORD=$EMAIL_PASSWORD
|
||||
EMAIL_SMTP_URL=$EMAIL_SMTP_URL
|
||||
EMAIL_SMTP_PORT=587
|
||||
EMAIL_LINK_VERIFICATION=$EMAIL_LINK_VERIFICATION
|
||||
EMAIL_LINK_SETPASSWORD=$EMAIL_LINK_SETPASSWORD
|
||||
EMAIL_LINK_FORGOTPASSWORD=$EMAIL_LINK_FORGOTPASSWORD
|
||||
EMAIL_LINK_OVERVIEW=$EMAIL_LINK_OVERVIEW
|
||||
EMAIL_CODE_VALID_TIME=$EMAIL_CODE_VALID_TIME
|
||||
EMAIL_SMTP_PORT=$EMAIL_SMTP_PORT
|
||||
EMAIL_LINK_VERIFICATION_PATH=$EMAIL_LINK_VERIFICATION_PATH
|
||||
EMAIL_LINK_SETPASSWORD_PATH=$EMAIL_LINK_SETPASSWORD_PATH
|
||||
EMAIL_LINK_FORGOTPASSWORD_PATH=$EMAIL_LINK_FORGOTPASSWORD_PATH
|
||||
EMAIL_LINK_OVERVIEW_PATH=$EMAIL_LINK_OVERVIEW_PATH
|
||||
EMAIL_CODE_VALID_TIME=$EMAIL_CODE_VALID_TIME_PATH
|
||||
EMAIL_CODE_REQUEST_TIME=$EMAIL_CODE_REQUEST_TIME
|
||||
|
||||
# Webhook
|
||||
@ -61,6 +62,7 @@ WEBHOOK_ELOPAGE_SECRET=$WEBHOOK_ELOPAGE_SECRET
|
||||
|
||||
# Federation
|
||||
FEDERATION_VALIDATE_COMMUNITY_TIMER=$FEDERATION_VALIDATE_COMMUNITY_TIMER
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED=$FEDERATION_XCOM_SENDCOINS_ENABLED
|
||||
|
||||
# GMS
|
||||
GMS_ACTIVE=$GMS_ACTIVE
|
||||
|
||||
@ -16,6 +16,7 @@ module.exports = {
|
||||
moduleNameMapper: {
|
||||
'@/(.*)': '<rootDir>/src/$1',
|
||||
'@arg/(.*)': '<rootDir>/src/graphql/arg/$1',
|
||||
'@dltConnector/(.*)': '<rootDir>/src/apis/dltConnector/$1',
|
||||
'@enum/(.*)': '<rootDir>/src/graphql/enum/$1',
|
||||
'@model/(.*)': '<rootDir>/src/graphql/model/$1',
|
||||
'@union/(.*)': '<rootDir>/src/graphql/union/$1',
|
||||
@ -27,6 +28,11 @@ module.exports = {
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/entity/$1'
|
||||
: '<rootDir>/../database/build/entity/$1',
|
||||
'@logging/(.*)':
|
||||
// eslint-disable-next-line n/no-process-env
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/logging/$1'
|
||||
: '<rootDir>/../database/build/logging/$1',
|
||||
'@dbTools/(.*)':
|
||||
// eslint-disable-next-line n/no-process-env
|
||||
process.env.NODE_ENV === 'development'
|
||||
|
||||
@ -31,9 +31,11 @@
|
||||
"dotenv": "^10.0.0",
|
||||
"email-templates": "^10.0.1",
|
||||
"express": "^4.17.1",
|
||||
"express-slow-down": "^2.0.1",
|
||||
"gradido-database": "file:../database",
|
||||
"graphql": "^15.5.1",
|
||||
"graphql-request": "5.0.0",
|
||||
"helmet": "^5.1.1",
|
||||
"i18n": "^0.15.1",
|
||||
"jose": "^4.14.4",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
|
||||
@ -25,8 +25,6 @@ let testEnv: {
|
||||
jest.mock('graphql-request', () => {
|
||||
const originalModule = jest.requireActual('graphql-request')
|
||||
|
||||
let testCursor = 0
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
@ -38,30 +36,11 @@ jest.mock('graphql-request', () => {
|
||||
// why not using mockResolvedValueOnce or mockReturnValueOnce?
|
||||
// I have tried, but it didn't work and return every time the first value
|
||||
request: jest.fn().mockImplementation(() => {
|
||||
testCursor++
|
||||
if (testCursor === 4) {
|
||||
return Promise.resolve(
|
||||
// invalid, is 33 Bytes long as binary
|
||||
{
|
||||
transmitTransaction: {
|
||||
dltTransactionIdHex:
|
||||
'723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516212A',
|
||||
},
|
||||
},
|
||||
)
|
||||
} else if (testCursor === 5) {
|
||||
throw Error('Connection error')
|
||||
} else {
|
||||
return Promise.resolve(
|
||||
// valid, is 32 Bytes long as binary
|
||||
{
|
||||
transmitTransaction: {
|
||||
dltTransactionIdHex:
|
||||
'723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
return Promise.resolve({
|
||||
transmitTransaction: {
|
||||
succeed: true,
|
||||
},
|
||||
})
|
||||
}),
|
||||
}
|
||||
}),
|
||||
@ -6,6 +6,9 @@ import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { TransactionResult } from './model/TransactionResult'
|
||||
import { UserIdentifier } from './model/UserIdentifier'
|
||||
|
||||
const sendTransaction = gql`
|
||||
mutation ($input: TransactionInput!) {
|
||||
sendTransaction(data: $input) {
|
||||
@ -78,32 +81,42 @@ export class DltConnectorClient {
|
||||
* transmit transaction via dlt-connector to iota
|
||||
* and update dltTransactionId of transaction in db with iota message id
|
||||
*/
|
||||
public async transmitTransaction(
|
||||
transaction: DbTransaction,
|
||||
senderCommunityUuid?: string,
|
||||
recipientCommunityUuid?: string,
|
||||
): Promise<string> {
|
||||
public async transmitTransaction(transaction: DbTransaction): Promise<boolean> {
|
||||
const typeString = getTransactionTypeString(transaction.typeId)
|
||||
const amountString = transaction.amount.toString()
|
||||
// no negative values in dlt connector, gradido concept don't use negative values so the code don't use it too
|
||||
const amountString = transaction.amount.abs().toString()
|
||||
const params = {
|
||||
input: {
|
||||
user: {
|
||||
uuid: transaction.userGradidoID,
|
||||
communityUuid: transaction.userCommunityUuid,
|
||||
} as UserIdentifier,
|
||||
linkedUser: {
|
||||
uuid: transaction.linkedUserGradidoID,
|
||||
communityUuid: transaction.linkedUserCommunityUuid,
|
||||
} as UserIdentifier,
|
||||
amount: amountString,
|
||||
type: typeString,
|
||||
createdAt: transaction.balanceDate.toISOString(),
|
||||
backendTransactionId: transaction.id,
|
||||
targetDate: transaction.creationDate?.toISOString(),
|
||||
},
|
||||
}
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(sendTransaction, {
|
||||
input: {
|
||||
senderUser: {
|
||||
uuid: transaction.userGradidoID,
|
||||
communityUuid: senderCommunityUuid,
|
||||
},
|
||||
recipientUser: {
|
||||
uuid: transaction.linkedUserGradidoID,
|
||||
communityUuid: recipientCommunityUuid,
|
||||
},
|
||||
amount: amountString,
|
||||
type: typeString,
|
||||
createdAt: transaction.balanceDate.toString(),
|
||||
// TODO: add account nr for user after they have also more than one account in backend
|
||||
logger.debug('transmit transaction to dlt connector', params)
|
||||
const {
|
||||
data: {
|
||||
sendTransaction: { error, succeed },
|
||||
},
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
||||
return data.sendTransaction.dltTransactionIdHex
|
||||
} = await this.client.rawRequest<{ sendTransaction: TransactionResult }>(
|
||||
sendTransaction,
|
||||
params,
|
||||
)
|
||||
if (error) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
return succeed
|
||||
} catch (e) {
|
||||
throw new LogError('Error send sending transaction to dlt-connector: ', e)
|
||||
}
|
||||
14
backend/src/apis/dltConnector/enum/TransactionErrorType.ts
Normal file
14
backend/src/apis/dltConnector/enum/TransactionErrorType.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Error Types for dlt-connector graphql responses
|
||||
*/
|
||||
export enum TransactionErrorType {
|
||||
NOT_IMPLEMENTED_YET = 'Not Implemented yet',
|
||||
MISSING_PARAMETER = 'Missing parameter',
|
||||
ALREADY_EXIST = 'Already exist',
|
||||
DB_ERROR = 'DB Error',
|
||||
PROTO_DECODE_ERROR = 'Proto Decode Error',
|
||||
PROTO_ENCODE_ERROR = 'Proto Encode Error',
|
||||
INVALID_SIGNATURE = 'Invalid Signature',
|
||||
LOGIC_ERROR = 'Logic Error',
|
||||
NOT_FOUND = 'Not found',
|
||||
}
|
||||
11
backend/src/apis/dltConnector/enum/TransactionType.ts
Normal file
11
backend/src/apis/dltConnector/enum/TransactionType.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Transaction Types on Blockchain
|
||||
*/
|
||||
export enum TransactionType {
|
||||
GRADIDO_TRANSFER = 1,
|
||||
GRADIDO_CREATION = 2,
|
||||
GROUP_FRIENDS_UPDATE = 3,
|
||||
REGISTER_ADDRESS = 4,
|
||||
GRADIDO_DEFERRED_TRANSFER = 5,
|
||||
COMMUNITY_ROOT = 6,
|
||||
}
|
||||
7
backend/src/apis/dltConnector/model/TransactionError.ts
Normal file
7
backend/src/apis/dltConnector/model/TransactionError.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { TransactionErrorType } from '@dltConnector/enum/TransactionErrorType'
|
||||
|
||||
export interface TransactionError {
|
||||
type: TransactionErrorType
|
||||
message: string
|
||||
name: string
|
||||
}
|
||||
8
backend/src/apis/dltConnector/model/TransactionRecipe.ts
Normal file
8
backend/src/apis/dltConnector/model/TransactionRecipe.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { TransactionType } from '@dltConnector/enum/TransactionType'
|
||||
|
||||
export interface TransactionRecipe {
|
||||
id: number
|
||||
createdAt: string
|
||||
type: TransactionType
|
||||
topic: string
|
||||
}
|
||||
8
backend/src/apis/dltConnector/model/TransactionResult.ts
Normal file
8
backend/src/apis/dltConnector/model/TransactionResult.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { TransactionError } from './TransactionError'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
|
||||
export interface TransactionResult {
|
||||
error?: TransactionError
|
||||
recipe?: TransactionRecipe
|
||||
succeed: boolean
|
||||
}
|
||||
5
backend/src/apis/dltConnector/model/UserIdentifier.ts
Normal file
5
backend/src/apis/dltConnector/model/UserIdentifier.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface UserIdentifier {
|
||||
uuid: string
|
||||
communityUuid: string
|
||||
accountNr?: number
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum GmsPublishNameType {
|
||||
GMS_PUBLISH_NAME_ALIAS_OR_INITALS = 0,
|
||||
GMS_PUBLISH_NAME_INITIALS = 1,
|
||||
GMS_PUBLISH_NAME_FIRST = 2,
|
||||
GMS_PUBLISH_NAME_FIRST_INITIAL = 3,
|
||||
GMS_PUBLISH_NAME_FULL = 4,
|
||||
}
|
||||
|
||||
registerEnumType(GmsPublishNameType, {
|
||||
name: 'GmsPublishNameType', // this one is mandatory
|
||||
description: 'Type of name publishing', // this one is optional
|
||||
})
|
||||
|
||||
export enum GmsPublishPhoneType {
|
||||
GMS_PUBLISH_PHONE_NOTHING = 0,
|
||||
GMS_PUBLISH_PHONE_COUNTRY = 1,
|
||||
GMS_PUBLISH_PHONE_FULL = 2,
|
||||
}
|
||||
|
||||
registerEnumType(GmsPublishPhoneType, {
|
||||
name: 'GmsPublishPhoneType', // this one is mandatory
|
||||
description: 'Type of Phone publishing', // this one is optional
|
||||
})
|
||||
|
||||
export enum GmsLocationType {
|
||||
GMS_LOCATION_TYPE_EXACT = 0,
|
||||
GMS_LOCATION_TYPE_APPROXIMATE = 1,
|
||||
GMS_LOCATION_TYPE_RANDOM = 2,
|
||||
}
|
||||
|
||||
registerEnumType(GmsLocationType, {
|
||||
name: 'GmsLocationType', // this one is mandatory
|
||||
description: 'Type of location treatment in GMS', // this one is optional
|
||||
})
|
||||
@ -1,6 +1,8 @@
|
||||
import { User as dbUser } from '@entity/User'
|
||||
|
||||
import { GmsLocationType, GmsPublishNameType, GmsPublishPhoneType } from './GmsEnums'
|
||||
import { GmsPublishLocationType } from '@/graphql/enum/GmsPublishLocationType'
|
||||
import { GmsPublishNameType } from '@/graphql/enum/GmsPublishNameType'
|
||||
import { GmsPublishPhoneType } from '@/graphql/enum/GmsPublishPhoneType'
|
||||
|
||||
export class GmsUser {
|
||||
constructor(user: dbUser) {
|
||||
@ -12,7 +14,7 @@ export class GmsUser {
|
||||
this.firstName = this.getGmsFirstName(user)
|
||||
this.lastName = this.getGmsLastName(user)
|
||||
this.alias = this.getGmsAlias(user)
|
||||
this.type = GmsLocationType.GMS_LOCATION_TYPE_RANDOM
|
||||
this.type = GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM
|
||||
this.location = null
|
||||
}
|
||||
|
||||
|
||||
@ -12,14 +12,14 @@ Decimal.set({
|
||||
})
|
||||
|
||||
const constants = {
|
||||
DB_VERSION: '0079-introduce_gms_registration',
|
||||
DB_VERSION: '0082-introduce_gms_registration',
|
||||
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
|
||||
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: 'v21.2023-11-15',
|
||||
EXPECTED: 'v21.2024-01-06',
|
||||
CURRENT: '',
|
||||
},
|
||||
}
|
||||
@ -51,18 +51,23 @@ const klicktipp = {
|
||||
KLICKTIPP_APIKEY_EN: process.env.KLICKTIPP_APIKEY_EN ?? 'SomeFakeKeyEN',
|
||||
}
|
||||
|
||||
const COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? 'localhost'
|
||||
const URL_PROTOCOL = process.env.URL_PROTOCOL ?? 'http'
|
||||
const COMMUNITY_URL = process.env.COMMUNITY_URL ?? `${URL_PROTOCOL}://${COMMUNITY_HOST}`
|
||||
const DLT_CONNECTOR_PORT = process.env.DLT_CONNECTOR_PORT ?? 6010
|
||||
|
||||
const dltConnector = {
|
||||
DLT_CONNECTOR: process.env.DLT_CONNECTOR === 'true' || false,
|
||||
DLT_CONNECTOR_URL: process.env.DLT_CONNECTOR_URL ?? 'http://localhost:6010',
|
||||
DLT_CONNECTOR_URL: process.env.DLT_CONNECTOR_URL ?? `${COMMUNITY_URL}:${DLT_CONNECTOR_PORT}`,
|
||||
}
|
||||
|
||||
const community = {
|
||||
COMMUNITY_NAME: process.env.COMMUNITY_NAME ?? 'Gradido Entwicklung',
|
||||
COMMUNITY_URL: process.env.COMMUNITY_URL ?? 'http://localhost/',
|
||||
COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL ?? 'http://localhost/register',
|
||||
COMMUNITY_REDEEM_URL: process.env.COMMUNITY_REDEEM_URL ?? 'http://localhost/redeem/{code}',
|
||||
COMMUNITY_URL,
|
||||
COMMUNITY_REGISTER_URL: COMMUNITY_URL + (process.env.COMMUNITY_REGISTER_PATH ?? '/register'),
|
||||
COMMUNITY_REDEEM_URL: COMMUNITY_URL + (process.env.COMMUNITY_REDEEM_PATH ?? '/redeem/{code}'),
|
||||
COMMUNITY_REDEEM_CONTRIBUTION_URL:
|
||||
process.env.COMMUNITY_REDEEM_CONTRIBUTION_URL ?? 'http://localhost/redeem/CL-{code}',
|
||||
COMMUNITY_URL + (process.env.COMMUNITY_REDEEM_CONTRIBUTION_PATH ?? '/redeem/CL-{code}'),
|
||||
COMMUNITY_DESCRIPTION:
|
||||
process.env.COMMUNITY_DESCRIPTION ?? 'Die lokale Entwicklungsumgebung von Gradido.',
|
||||
COMMUNITY_SUPPORT_MAIL: process.env.COMMUNITY_SUPPORT_MAIL ?? 'support@supportmail.com',
|
||||
@ -74,8 +79,8 @@ const loginServer = {
|
||||
}
|
||||
|
||||
const email = {
|
||||
EMAIL: process.env.EMAIL === 'true' || false,
|
||||
EMAIL_TEST_MODUS: process.env.EMAIL_TEST_MODUS === 'true' || false,
|
||||
EMAIL: process.env.EMAIL === 'true' ?? false,
|
||||
EMAIL_TEST_MODUS: process.env.EMAIL_TEST_MODUS === 'true' ?? false,
|
||||
EMAIL_TEST_RECEIVER: process.env.EMAIL_TEST_RECEIVER ?? 'stage1@gradido.net',
|
||||
EMAIL_USERNAME: process.env.EMAIL_USERNAME ?? '',
|
||||
EMAIL_SENDER: process.env.EMAIL_SENDER ?? 'info@gradido.net',
|
||||
@ -85,19 +90,19 @@ const email = {
|
||||
// eslint-disable-next-line no-unneeded-ternary
|
||||
EMAIL_TLS: process.env.EMAIL_TLS === 'false' ? false : true,
|
||||
EMAIL_LINK_VERIFICATION:
|
||||
process.env.EMAIL_LINK_VERIFICATION ?? 'http://localhost/checkEmail/{optin}{code}',
|
||||
COMMUNITY_URL + (process.env.EMAIL_LINK_VERIFICATION_PATH ?? '/checkEmail/{optin}{code}'),
|
||||
EMAIL_LINK_SETPASSWORD:
|
||||
process.env.EMAIL_LINK_SETPASSWORD ?? 'http://localhost/reset-password/{optin}',
|
||||
COMMUNITY_URL + (process.env.EMAIL_LINK_SETPASSWORD_PATH ?? '/reset-password/{optin}'),
|
||||
EMAIL_LINK_FORGOTPASSWORD:
|
||||
process.env.EMAIL_LINK_FORGOTPASSWORD ?? 'http://localhost/forgot-password',
|
||||
EMAIL_LINK_OVERVIEW: process.env.EMAIL_LINK_OVERVIEW ?? 'http://localhost/overview',
|
||||
COMMUNITY_URL + (process.env.EMAIL_LINK_FORGOTPASSWORD_PATH ?? '/forgot-password'),
|
||||
EMAIL_LINK_OVERVIEW: COMMUNITY_URL + (process.env.EMAIL_LINK_OVERVIEW_PATH ?? '/overview'),
|
||||
// time in minutes a optin code is valid
|
||||
EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME
|
||||
? parseInt(process.env.EMAIL_CODE_VALID_TIME) || 1440
|
||||
? parseInt(process.env.EMAIL_CODE_VALID_TIME) ?? 1440
|
||||
: 1440,
|
||||
// time in minutes that must pass to request a new optin code
|
||||
EMAIL_CODE_REQUEST_TIME: process.env.EMAIL_CODE_REQUEST_TIME
|
||||
? parseInt(process.env.EMAIL_CODE_REQUEST_TIME) || 10
|
||||
? parseInt(process.env.EMAIL_CODE_REQUEST_TIME) ?? 10
|
||||
: 10,
|
||||
}
|
||||
|
||||
@ -124,9 +129,9 @@ if (
|
||||
const federation = {
|
||||
FEDERATION_BACKEND_SEND_ON_API: process.env.FEDERATION_BACKEND_SEND_ON_API ?? '1_0',
|
||||
FEDERATION_VALIDATE_COMMUNITY_TIMER:
|
||||
Number(process.env.FEDERATION_VALIDATE_COMMUNITY_TIMER) || 60000,
|
||||
Number(process.env.FEDERATION_VALIDATE_COMMUNITY_TIMER) ?? 60000,
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED:
|
||||
process.env.FEDERATION_XCOM_SENDCOINS_ENABLED === 'true' || false,
|
||||
process.env.FEDERATION_XCOM_SENDCOINS_ENABLED === 'true' ?? false,
|
||||
// default value for community-uuid is equal uuid of stage-3
|
||||
FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID:
|
||||
process.env.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID ?? '56a55482-909e-46a4-bfa2-cd025e894ebc',
|
||||
|
||||
@ -9,6 +9,6 @@ block content
|
||||
h2= t('emails.addedContributionMessage.readMessage')
|
||||
div(class="p_content")= t('emails.addedContributionMessage.toSeeAndAnswerMessage')
|
||||
|
||||
a.button-3(href=`${communityURL}community/contributions`) #{t('emails.general.toAccount')}
|
||||
a.button-3(href=`${communityURL}/community/contributions`) #{t('emails.general.toAccount')}
|
||||
|
||||
include ../includes/doNotReply.pug
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//-
|
||||
h2= t('emails.general.contributionDetails')
|
||||
div(class="p_content")= t('emails.contribution.toSeeContributionsAndMessages')
|
||||
a.button-3(href=`${communityURL}community/contributions`) #{t('emails.general.toAccount')}
|
||||
a.button-3(href=`${communityURL}/community/contributions`) #{t('emails.general.toAccount')}
|
||||
div(class="p_content")= t('emails.general.orCopyLink')
|
||||
|
||||
a.clink(href=`${communityURL}community/contributions`) #{`${communityURL}community/contributions`}
|
||||
a.clink(href=`${communityURL}/community/contributions`) #{`${communityURL}/community/contributions`}
|
||||
@ -13,6 +13,6 @@ block content
|
||||
br
|
||||
= t('emails.general.detailsYouFindOnLinkToYourAccount')
|
||||
|
||||
a.button-3(href=`${communityURL}transactions`) #{t('emails.general.toAccount')}
|
||||
a.button-3(href=`${communityURL}/transactions`) #{t('emails.general.toAccount')}
|
||||
|
||||
include ../includes/doNotReply.pug
|
||||
|
||||
@ -9,7 +9,7 @@ block content
|
||||
h2= t('emails.general.transactionDetails')
|
||||
div(class="p_content")= t('emails.general.detailsYouFindOnLinkToYourAccount')
|
||||
|
||||
a.button-3(href=`${communityURL}transactions`) #{t('emails.general.toAccount')}
|
||||
a.button-3(href=`${communityURL}/transactions`) #{t('emails.general.toAccount')}
|
||||
|
||||
include ../includes/doNotReply.pug
|
||||
|
||||
|
||||
@ -28,9 +28,9 @@ export class AuthenticationClient {
|
||||
async openConnection(args: OpenConnectionArgs): Promise<boolean | undefined> {
|
||||
logger.debug(`Authentication: openConnection at ${this.endpoint} for args:`, args)
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(openConnection, { args })
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
const { data } = await this.client.rawRequest<{ openConnection: boolean }>(openConnection, {
|
||||
args,
|
||||
})
|
||||
if (!data?.openConnection) {
|
||||
logger.warn(
|
||||
'Authentication: openConnection without response data from endpoint',
|
||||
|
||||
@ -5,9 +5,10 @@ import { getPublicCommunityInfo } from '@/federation/client/1_0/query/getPublicC
|
||||
import { getPublicKey } from '@/federation/client/1_0/query/getPublicKey'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { PublicCommunityInfoLoggingView } from './logging/PublicCommunityInfoLogging.view'
|
||||
import { GetPublicKeyResult } from './model/GetPublicKeyResult'
|
||||
import { PublicCommunityInfo } from './model/PublicCommunityInfo'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export class FederationClient {
|
||||
dbCom: DbFederatedCommunity
|
||||
endpoint: string
|
||||
@ -27,12 +28,17 @@ export class FederationClient {
|
||||
})
|
||||
}
|
||||
|
||||
getEndpoint = () => {
|
||||
return this.endpoint
|
||||
}
|
||||
|
||||
getPublicKey = async (): Promise<string | undefined> => {
|
||||
logger.debug('Federation: getPublicKey from endpoint', this.endpoint)
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(getPublicKey, {})
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
const { data } = await this.client.rawRequest<{ getPublicKey: GetPublicKeyResult }>(
|
||||
getPublicKey,
|
||||
{},
|
||||
)
|
||||
if (!data?.getPublicKey?.publicKey) {
|
||||
logger.warn('Federation: getPublicKey without response data from endpoint', this.endpoint)
|
||||
return
|
||||
@ -40,22 +46,25 @@ export class FederationClient {
|
||||
logger.debug(
|
||||
'Federation: getPublicKey successful from endpoint',
|
||||
this.endpoint,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
data.getPublicKey.publicKey,
|
||||
)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
||||
return data.getPublicKey.publicKey
|
||||
} catch (err) {
|
||||
logger.warn('Federation: getPublicKey failed for endpoint', this.endpoint)
|
||||
const errorString = JSON.stringify(err)
|
||||
logger.warn('Federation: getPublicKey failed for endpoint', {
|
||||
endpoint: this.endpoint,
|
||||
err: errorString.length <= 200 ? errorString : errorString.substring(0, 200) + '...',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getPublicCommunityInfo = async (): Promise<PublicCommunityInfo | undefined> => {
|
||||
logger.debug(`Federation: getPublicCommunityInfo with endpoint='${this.endpoint}'...`)
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(getPublicCommunityInfo, {})
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
const { data } = await this.client.rawRequest<{
|
||||
getPublicCommunityInfo: PublicCommunityInfo
|
||||
}>(getPublicCommunityInfo, {})
|
||||
|
||||
if (!data?.getPublicCommunityInfo?.name) {
|
||||
logger.warn(
|
||||
'Federation: getPublicCommunityInfo without response data from endpoint',
|
||||
@ -64,12 +73,17 @@ export class FederationClient {
|
||||
return
|
||||
}
|
||||
logger.debug(`Federation: getPublicCommunityInfo successful from endpoint=${this.endpoint}`)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
logger.debug(`publicCommunityInfo:`, data.getPublicCommunityInfo)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
||||
logger.debug(
|
||||
`publicCommunityInfo:`,
|
||||
new PublicCommunityInfoLoggingView(data.getPublicCommunityInfo),
|
||||
)
|
||||
return data.getPublicCommunityInfo
|
||||
} catch (err) {
|
||||
logger.warn('Federation: getPublicCommunityInfo failed for endpoint', this.endpoint)
|
||||
const errorString = JSON.stringify(err)
|
||||
logger.warn('Federation: getPublicCommunityInfo failed for endpoint', {
|
||||
endpoint: this.endpoint,
|
||||
err: errorString.length <= 200 ? errorString : errorString.substring(0, 200) + '...',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ import { GraphQLClient } from 'graphql-request'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { SendCoinsArgsLoggingView } from './logging/SendCoinsArgsLogging.view'
|
||||
import { SendCoinsResultLoggingView } from './logging/SendCoinsResultLogging.view'
|
||||
import { SendCoinsArgs } from './model/SendCoinsArgs'
|
||||
import { SendCoinsResult } from './model/SendCoinsResult'
|
||||
import { revertSendCoins as revertSendCoinsQuery } from './query/revertSendCoins'
|
||||
@ -11,7 +13,6 @@ import { revertSettledSendCoins as revertSettledSendCoinsQuery } from './query/r
|
||||
import { settleSendCoins as settleSendCoinsQuery } from './query/settleSendCoins'
|
||||
import { voteForSendCoins as voteForSendCoinsQuery } from './query/voteForSendCoins'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
export class SendCoinsClient {
|
||||
dbCom: DbFederatedCommunity
|
||||
endpoint: string
|
||||
@ -34,26 +35,26 @@ export class SendCoinsClient {
|
||||
async voteForSendCoins(args: SendCoinsArgs): Promise<SendCoinsResult> {
|
||||
logger.debug('X-Com: voteForSendCoins against endpoint=', this.endpoint)
|
||||
try {
|
||||
logger.debug(`X-Com: SendCoinsClient: voteForSendCoins with args=`, args)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(voteForSendCoinsQuery, { args })
|
||||
logger.debug(`X-Com: SendCoinsClient: after rawRequest...data:`, data)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: voteForSendCoins with args=`,
|
||||
new SendCoinsArgsLoggingView(args),
|
||||
)
|
||||
const { data } = await this.client.rawRequest<{ voteForSendCoins: SendCoinsResult }>(
|
||||
voteForSendCoinsQuery,
|
||||
{ args },
|
||||
)
|
||||
const result = data.voteForSendCoins
|
||||
if (!data?.voteForSendCoins?.vote) {
|
||||
logger.debug('X-Com: voteForSendCoins failed with: ', data)
|
||||
logger.debug(
|
||||
'X-Com: voteForSendCoins failed with: ',
|
||||
new SendCoinsResultLoggingView(result),
|
||||
)
|
||||
return new SendCoinsResult()
|
||||
}
|
||||
const result = new SendCoinsResult()
|
||||
result.vote = true
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
|
||||
result.recipGradidoID = data.voteForSendCoins.recipGradidoID
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
|
||||
result.recipFirstName = data.voteForSendCoins.recipFirstName
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
|
||||
result.recipLastName = data.voteForSendCoins.recipLastName
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
|
||||
result.recipAlias = data.voteForSendCoins.recipAlias
|
||||
logger.debug('X-Com: voteForSendCoins successful with result=', result)
|
||||
logger.debug(
|
||||
'X-Com: voteForSendCoins successful with result=',
|
||||
new SendCoinsResultLoggingView(result),
|
||||
)
|
||||
return result
|
||||
} catch (err) {
|
||||
throw new LogError(`X-Com: voteForSendCoins failed for endpoint=${this.endpoint}:`, err)
|
||||
@ -63,11 +64,15 @@ export class SendCoinsClient {
|
||||
async revertSendCoins(args: SendCoinsArgs): Promise<boolean> {
|
||||
logger.debug('X-Com: revertSendCoins against endpoint=', this.endpoint)
|
||||
try {
|
||||
logger.debug(`X-Com: SendCoinsClient: revertSendCoins with args=`, args)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(revertSendCoinsQuery, { args })
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: revertSendCoins with args=`,
|
||||
new SendCoinsArgsLoggingView(args),
|
||||
)
|
||||
const { data } = await this.client.rawRequest<{ revertSendCoins: boolean }>(
|
||||
revertSendCoinsQuery,
|
||||
{ args },
|
||||
)
|
||||
logger.debug(`X-Com: SendCoinsClient: after revertSendCoins: data=`, data)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (!data?.revertSendCoins) {
|
||||
logger.warn('X-Com: revertSendCoins without response data from endpoint', this.endpoint)
|
||||
return false
|
||||
@ -88,11 +93,15 @@ export class SendCoinsClient {
|
||||
async settleSendCoins(args: SendCoinsArgs): Promise<boolean> {
|
||||
logger.debug(`X-Com: settleSendCoins against endpoint='${this.endpoint}'...`)
|
||||
try {
|
||||
logger.debug(`X-Com: SendCoinsClient: settleSendCoins with args=`, args)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(settleSendCoinsQuery, { args })
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: settleSendCoins with args=`,
|
||||
new SendCoinsArgsLoggingView(args),
|
||||
)
|
||||
const { data } = await this.client.rawRequest<{ settleSendCoins: boolean }>(
|
||||
settleSendCoinsQuery,
|
||||
{ args },
|
||||
)
|
||||
logger.debug(`X-Com: SendCoinsClient: after settleSendCoins: data=`, data)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (!data?.settleSendCoins) {
|
||||
logger.warn(
|
||||
'X-Com: SendCoinsClient: settleSendCoins without response data from endpoint',
|
||||
@ -115,9 +124,14 @@ export class SendCoinsClient {
|
||||
async revertSettledSendCoins(args: SendCoinsArgs): Promise<boolean> {
|
||||
logger.debug(`X-Com: revertSettledSendCoins against endpoint='${this.endpoint}'...`)
|
||||
try {
|
||||
logger.debug(`X-Com: SendCoinsClient: revertSettledSendCoins with args=`, args)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const { data } = await this.client.rawRequest(revertSettledSendCoinsQuery, { args })
|
||||
logger.debug(
|
||||
`X-Com: SendCoinsClient: revertSettledSendCoins with args=`,
|
||||
new SendCoinsArgsLoggingView(args),
|
||||
)
|
||||
const { data } = await this.client.rawRequest<{ revertSettledSendCoins: boolean }>(
|
||||
revertSettledSendCoinsQuery,
|
||||
{ args },
|
||||
)
|
||||
logger.debug(`X-Com: SendCoinsClient: after revertSettledSendCoins: data=`, data)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (!data?.revertSettledSendCoins) {
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
import { AbstractLoggingView } from '@logging/AbstractLogging.view'
|
||||
|
||||
import { PublicCommunityInfo } from '@/federation/client/1_0/model/PublicCommunityInfo'
|
||||
|
||||
export class PublicCommunityInfoLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: PublicCommunityInfo) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
name: this.self.name,
|
||||
description: this.self.description,
|
||||
creationDate: this.dateToString(this.self.creationDate),
|
||||
publicKey: this.self.publicKey,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import { AbstractLoggingView } from '@logging/AbstractLogging.view'
|
||||
|
||||
import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs'
|
||||
|
||||
export class SendCoinsArgsLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: SendCoinsArgs) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
recipientCommunityUuid: this.self.recipientCommunityUuid,
|
||||
recipientUserIdentifier: this.self.recipientUserIdentifier,
|
||||
creationDate: this.self.creationDate,
|
||||
amount: this.decimalToString(this.self.amount),
|
||||
memoLength: this.self.memo.length,
|
||||
senderCommunityUuid: this.self.senderCommunityUuid,
|
||||
senderUserUuid: this.self.senderUserUuid,
|
||||
senderUserName: this.self.senderUserName.substring(0, 3),
|
||||
senderAlias: this.self.senderAlias?.substring(0, 3),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
import { AbstractLoggingView } from '@logging/AbstractLogging.view'
|
||||
|
||||
import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult'
|
||||
|
||||
export class SendCoinsResultLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: SendCoinsResult) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
vote: this.self.vote,
|
||||
recipGradidoID: this.self.recipGradidoID,
|
||||
recipFirstName: this.self.recipFirstName?.substring(0, 3),
|
||||
recipLastName: this.self.recipLastName?.substring(0, 3),
|
||||
recipAlias: this.self.recipAlias?.substring(0, 3),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { Field, ObjectType } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export class GetPublicKeyResult {
|
||||
constructor(pubKey: string) {
|
||||
this.publicKey = pubKey
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
publicKey: string
|
||||
}
|
||||
@ -47,15 +47,25 @@ export class FederationClientFactory {
|
||||
const instance = FederationClientFactory.instanceArray.find(
|
||||
(instance) => instance.id === dbCom.id,
|
||||
)
|
||||
if (instance) {
|
||||
// TODO: found a way to prevent double code with FederationClient::constructor
|
||||
const endpoint = `${dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/'}${
|
||||
dbCom.apiVersion
|
||||
}/`
|
||||
// check if endpoint is still the same and not changed meanwhile
|
||||
if (instance && instance.client.getEndpoint() === endpoint) {
|
||||
return instance.client
|
||||
}
|
||||
const client = FederationClientFactory.createFederationClient(dbCom)
|
||||
if (client) {
|
||||
FederationClientFactory.instanceArray.push({
|
||||
id: dbCom.id,
|
||||
client,
|
||||
} as FederationClientInstance)
|
||||
// only update instance if we already have one
|
||||
if (instance) {
|
||||
instance.client = client
|
||||
} else {
|
||||
FederationClientFactory.instanceArray.push({
|
||||
id: dbCom.id,
|
||||
client,
|
||||
} as FederationClientInstance)
|
||||
}
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ describe('validate Communities', () => {
|
||||
return { data: {} } as Response<unknown>
|
||||
})
|
||||
const variables1 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
publicKey: Buffer.from('11111111111111111111111111111111', 'hex'),
|
||||
apiVersion: '1_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
@ -113,7 +113,7 @@ describe('validate Communities', () => {
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables1 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
publicKey: Buffer.from('11111111111111111111111111111111', 'hex'),
|
||||
apiVersion: '1_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
@ -195,7 +195,7 @@ describe('validate Communities', () => {
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables1 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
publicKey: Buffer.from('11111111111111111111111111111111', 'hex'),
|
||||
apiVersion: '1_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
@ -315,7 +315,7 @@ describe('validate Communities', () => {
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables3 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
publicKey: Buffer.from('11111111111111111111111111111111', 'hex'),
|
||||
apiVersion: '2_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
|
||||
@ -3,14 +3,15 @@
|
||||
import { IsNull } from '@dbTools/typeorm'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
|
||||
import { FederatedCommunityLoggingView } from '@logging/FederatedCommunityLogging.view'
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
import { FederationClient as V1_0_FederationClient } from '@/federation/client/1_0/FederationClient'
|
||||
import { PublicCommunityInfo } from '@/federation/client/1_0/model/PublicCommunityInfo'
|
||||
import { FederationClientFactory } from '@/federation/client/FederationClientFactory'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { startCommunityAuthentication } from './authenticateCommunities'
|
||||
import { PublicCommunityInfoLoggingView } from './client/1_0/logging/PublicCommunityInfoLogging.view'
|
||||
import { ApiVersionType } from './enum/apiVersionType'
|
||||
|
||||
export async function startValidateCommunities(timerInterval: number): Promise<void> {
|
||||
@ -37,7 +38,7 @@ export async function validateCommunities(): Promise<void> {
|
||||
|
||||
logger.debug(`Federation: found ${dbFederatedCommunities.length} dbCommunities`)
|
||||
for (const dbCom of dbFederatedCommunities) {
|
||||
logger.debug('Federation: dbCom', dbCom)
|
||||
logger.debug('Federation: dbCom', new FederatedCommunityLoggingView(dbCom))
|
||||
const apiValueStrings: string[] = Object.values(ApiVersionType)
|
||||
logger.debug(`suppported ApiVersions=`, apiValueStrings)
|
||||
if (!apiValueStrings.includes(dbCom.apiVersion)) {
|
||||
@ -53,7 +54,7 @@ export async function validateCommunities(): Promise<void> {
|
||||
// eslint-disable-next-line camelcase
|
||||
if (client instanceof V1_0_FederationClient) {
|
||||
const pubKey = await client.getPublicKey()
|
||||
if (pubKey && pubKey === dbCom.publicKey.toString()) {
|
||||
if (pubKey && pubKey === dbCom.publicKey.toString('hex')) {
|
||||
await DbFederatedCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() })
|
||||
logger.debug(`Federation: verified community with:`, dbCom.endPoint)
|
||||
const pubComInfo = await client.getPublicCommunityInfo()
|
||||
@ -68,7 +69,7 @@ export async function validateCommunities(): Promise<void> {
|
||||
logger.debug(
|
||||
'Federation: received not matching publicKey:',
|
||||
pubKey,
|
||||
dbCom.publicKey.toString(),
|
||||
dbCom.publicKey.toString('hex'),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -82,10 +83,11 @@ async function writeForeignCommunity(
|
||||
dbCom: DbFederatedCommunity,
|
||||
pubInfo: PublicCommunityInfo,
|
||||
): Promise<void> {
|
||||
if (!dbCom || !pubInfo || !(dbCom.publicKey.toString() === pubInfo.publicKey)) {
|
||||
if (!dbCom || !pubInfo || !(dbCom.publicKey.toString('hex') === pubInfo.publicKey)) {
|
||||
const pubInfoView = new PublicCommunityInfoLoggingView(pubInfo)
|
||||
logger.error(
|
||||
`Error in writeForeignCommunity: missmatching parameters or publicKey. pubInfo:${JSON.stringify(
|
||||
pubInfo,
|
||||
`Error in writeForeignCommunity: missmatching parameters or publicKey. pubInfo:${pubInfoView.toString(
|
||||
true,
|
||||
)}`,
|
||||
)
|
||||
} else {
|
||||
|
||||
@ -3,11 +3,11 @@ import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class UserArgs {
|
||||
@Field({ nullable: false })
|
||||
@Field()
|
||||
@IsString()
|
||||
identifier: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Field()
|
||||
@IsString()
|
||||
communityIdentifier?: string
|
||||
communityIdentifier: string
|
||||
}
|
||||
|
||||
12
backend/src/graphql/enum/GmsPublishLocationType.ts
Normal file
12
backend/src/graphql/enum/GmsPublishLocationType.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum GmsPublishLocationType {
|
||||
GMS_LOCATION_TYPE_EXACT = 0,
|
||||
GMS_LOCATION_TYPE_APPROXIMATE = 1,
|
||||
GMS_LOCATION_TYPE_RANDOM = 2,
|
||||
}
|
||||
|
||||
registerEnumType(GmsPublishLocationType, {
|
||||
name: 'GmsPublishLocationType', // this one is mandatory
|
||||
description: 'Type of location treatment in GMS', // this one is optional
|
||||
})
|
||||
14
backend/src/graphql/enum/GmsPublishNameType.ts
Normal file
14
backend/src/graphql/enum/GmsPublishNameType.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum GmsPublishNameType {
|
||||
GMS_PUBLISH_NAME_ALIAS_OR_INITALS = 0,
|
||||
GMS_PUBLISH_NAME_INITIALS = 1,
|
||||
GMS_PUBLISH_NAME_FIRST = 2,
|
||||
GMS_PUBLISH_NAME_FIRST_INITIAL = 3,
|
||||
GMS_PUBLISH_NAME_FULL = 4,
|
||||
}
|
||||
|
||||
registerEnumType(GmsPublishNameType, {
|
||||
name: 'GmsPublishNameType', // this one is mandatory
|
||||
description: 'Type of name publishing', // this one is optional
|
||||
})
|
||||
12
backend/src/graphql/enum/GmsPublishPhoneType.ts
Normal file
12
backend/src/graphql/enum/GmsPublishPhoneType.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum GmsPublishPhoneType {
|
||||
GMS_PUBLISH_PHONE_NOTHING = 0,
|
||||
GMS_PUBLISH_PHONE_COUNTRY = 1,
|
||||
GMS_PUBLISH_PHONE_FULL = 2,
|
||||
}
|
||||
|
||||
registerEnumType(GmsPublishPhoneType, {
|
||||
name: 'GmsPublishPhoneType', // this one is mandatory
|
||||
description: 'Type of Phone publishing', // this one is optional
|
||||
})
|
||||
@ -6,7 +6,7 @@ export class FederatedCommunity {
|
||||
constructor(dbCom: DbFederatedCommunity) {
|
||||
this.id = dbCom.id
|
||||
this.foreign = dbCom.foreign
|
||||
this.publicKey = dbCom.publicKey.toString()
|
||||
this.publicKey = dbCom.publicKey.toString('hex')
|
||||
this.url =
|
||||
(dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/') + dbCom.apiVersion
|
||||
this.lastAnnouncedAt = dbCom.lastAnnouncedAt
|
||||
|
||||
@ -10,6 +10,9 @@ export class User {
|
||||
this.id = user.id
|
||||
this.foreign = user.foreign
|
||||
this.communityUuid = user.communityUuid
|
||||
if (user.community) {
|
||||
this.communityName = user.community.name
|
||||
}
|
||||
this.gradidoID = user.gradidoID
|
||||
this.alias = user.alias
|
||||
if (user.emailContact) {
|
||||
|
||||
@ -51,6 +51,60 @@ afterAll(async () => {
|
||||
await con.close()
|
||||
})
|
||||
|
||||
// real valid ed25519 key pairs
|
||||
const ed25519KeyPairStaticHex = [
|
||||
{
|
||||
public: '264c1e88914d18166cc31e8d6c2111c03ac83f5910398eb45cd425c6c3836367',
|
||||
private:
|
||||
'0ddcafd5e2da92e171ccc974af22fee3ad8407475e330586c8f259837d4fedc6264c1e88914d18166cc31e8d6c2111c03ac83f5910398eb45cd425c6c3836367',
|
||||
},
|
||||
{
|
||||
public: 'ac18a8754f725079f93d27b9054f2eff536109a2fd439f9755941abdd639baf0',
|
||||
private:
|
||||
'45325a0d0f22655095321d9d05999c65245da02130318ff51da1ee423b836117ac18a8754f725079f93d27b9054f2eff536109a2fd439f9755941abdd639baf0',
|
||||
},
|
||||
{
|
||||
public: '6f7d4ccde610db1e1a33fabbb444d5400013c168296b03fd50bc686d4c1ad0ed',
|
||||
private:
|
||||
'8ab6d5da8b666ef5b3d754559c028806a1e2f8142a3e7ada411a8b6a3fe70eeb6f7d4ccde610db1e1a33fabbb444d5400013c168296b03fd50bc686d4c1ad0ed',
|
||||
},
|
||||
{
|
||||
public: '85fbbce0763db24677cf7cb579a743013557a4fea0a9a624245f3ae8cd785e1d',
|
||||
private:
|
||||
'0369ea7c80c3134c2872c3cf77a68f12d57de57359145b550e3a0c4c8170a31785fbbce0763db24677cf7cb579a743013557a4fea0a9a624245f3ae8cd785e1d',
|
||||
},
|
||||
{
|
||||
public: 'b099d023476ece01f231c269cbe496139ca73b3b4eb705816a511a1ca09661d0',
|
||||
private:
|
||||
'015ac650157b9e9bdbe718940606242daa318a251e8417b49440495e5afe3750b099d023476ece01f231c269cbe496139ca73b3b4eb705816a511a1ca09661d0',
|
||||
},
|
||||
{
|
||||
public: '9f8dc17f1af9f71e9b9a1cd49ca295b89049863515a487578ad4f90b307abf39',
|
||||
private:
|
||||
'0c13e71c55a3c03bd5df05c92bbccde88ad4a47f3bac6bdc5383ef1ec946cfdc9f8dc17f1af9f71e9b9a1cd49ca295b89049863515a487578ad4f90b307abf39',
|
||||
},
|
||||
{
|
||||
public: '34218b2f570d341370dd2db111d0ef2415c03a110c3bf3127c6b2337af71753a',
|
||||
private:
|
||||
'60f3479bba44d035886ac21c362bceece9f9ec81859c9b37f734b6442a06c93b34218b2f570d341370dd2db111d0ef2415c03a110c3bf3127c6b2337af71753a',
|
||||
},
|
||||
{
|
||||
public: 'a447404f5e04ed4896ed64d0f704574ed780b52e90868d4b83e1afb8ea687ff6',
|
||||
private:
|
||||
'ea85ebb4332a52d87fe6f322dcd23ad4afc5eafb93dfff2216f3ffa9f0730e8aa447404f5e04ed4896ed64d0f704574ed780b52e90868d4b83e1afb8ea687ff6',
|
||||
},
|
||||
{
|
||||
public: 'b8b987c55da62b30d929672520551033eb37abdd88f9ea104db5d107c19680b4',
|
||||
private:
|
||||
'29475dbbc96d694b3c653a1e143caf084f6daf2d35267522c4096c55b47e2b76b8b987c55da62b30d929672520551033eb37abdd88f9ea104db5d107c19680b4',
|
||||
},
|
||||
{
|
||||
public: '40203d18a6ff8fb3c4c62d78e4807036fc9207782ce97a9bcf3be0755c236c37',
|
||||
private:
|
||||
'0b5c4d536d222e88b561ea495e15918fb8cba61a3f8c261ec9e587cca560804040203d18a6ff8fb3c4c62d78e4807036fc9207782ce97a9bcf3be0755c236c37',
|
||||
},
|
||||
]
|
||||
|
||||
describe('CommunityResolver', () => {
|
||||
describe('getCommunities', () => {
|
||||
let homeCom1: DbFederatedCommunity
|
||||
@ -79,7 +133,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
homeCom1 = DbFederatedCommunity.create()
|
||||
homeCom1.foreign = false
|
||||
homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
|
||||
homeCom1.apiVersion = '1_0'
|
||||
homeCom1.endPoint = 'http://localhost/api'
|
||||
homeCom1.createdAt = new Date()
|
||||
@ -87,7 +141,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
homeCom2 = DbFederatedCommunity.create()
|
||||
homeCom2.foreign = false
|
||||
homeCom2.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||
homeCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[1].public, 'hex')
|
||||
homeCom2.apiVersion = '1_1'
|
||||
homeCom2.endPoint = 'http://localhost/api'
|
||||
homeCom2.createdAt = new Date()
|
||||
@ -95,7 +149,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
homeCom3 = DbFederatedCommunity.create()
|
||||
homeCom3.foreign = false
|
||||
homeCom3.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||
homeCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[2].public, 'hex')
|
||||
homeCom3.apiVersion = '2_0'
|
||||
homeCom3.endPoint = 'http://localhost/api'
|
||||
homeCom3.createdAt = new Date()
|
||||
@ -109,7 +163,7 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 3,
|
||||
foreign: homeCom3.foreign,
|
||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[2].public),
|
||||
url: expect.stringMatching('http://localhost/api/2_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
@ -120,7 +174,7 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 2,
|
||||
foreign: homeCom2.foreign,
|
||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[1].public),
|
||||
url: expect.stringMatching('http://localhost/api/1_1'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
@ -131,7 +185,7 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 1,
|
||||
foreign: homeCom1.foreign,
|
||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
|
||||
url: expect.stringMatching('http://localhost/api/1_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
@ -151,7 +205,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
foreignCom1 = DbFederatedCommunity.create()
|
||||
foreignCom1.foreign = true
|
||||
foreignCom1.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
||||
foreignCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[3].public, 'hex')
|
||||
foreignCom1.apiVersion = '1_0'
|
||||
foreignCom1.endPoint = 'http://remotehost/api'
|
||||
foreignCom1.createdAt = new Date()
|
||||
@ -159,7 +213,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
foreignCom2 = DbFederatedCommunity.create()
|
||||
foreignCom2.foreign = true
|
||||
foreignCom2.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
||||
foreignCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[4].public, 'hex')
|
||||
foreignCom2.apiVersion = '1_1'
|
||||
foreignCom2.endPoint = 'http://remotehost/api'
|
||||
foreignCom2.createdAt = new Date()
|
||||
@ -167,7 +221,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
foreignCom3 = DbFederatedCommunity.create()
|
||||
foreignCom3.foreign = true
|
||||
foreignCom3.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
||||
foreignCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[5].public, 'hex')
|
||||
foreignCom3.apiVersion = '1_2'
|
||||
foreignCom3.endPoint = 'http://remotehost/api'
|
||||
foreignCom3.createdAt = new Date()
|
||||
@ -181,7 +235,7 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 3,
|
||||
foreign: homeCom3.foreign,
|
||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[2].public),
|
||||
url: expect.stringMatching('http://localhost/api/2_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
@ -192,7 +246,7 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 2,
|
||||
foreign: homeCom2.foreign,
|
||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[1].public),
|
||||
url: expect.stringMatching('http://localhost/api/1_1'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
@ -203,7 +257,7 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 1,
|
||||
foreign: homeCom1.foreign,
|
||||
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
|
||||
url: expect.stringMatching('http://localhost/api/1_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
@ -214,7 +268,7 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 6,
|
||||
foreign: foreignCom3.foreign,
|
||||
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[5].public),
|
||||
url: expect.stringMatching('http://remotehost/api/1_2'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
@ -225,7 +279,7 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 5,
|
||||
foreign: foreignCom2.foreign,
|
||||
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[4].public),
|
||||
url: expect.stringMatching('http://remotehost/api/1_1'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
@ -236,7 +290,7 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 4,
|
||||
foreign: foreignCom1.foreign,
|
||||
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[3].public),
|
||||
url: expect.stringMatching('http://remotehost/api/1_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
@ -281,8 +335,8 @@ describe('CommunityResolver', () => {
|
||||
homeCom1 = DbCommunity.create()
|
||||
homeCom1.foreign = false
|
||||
homeCom1.url = 'http://localhost/api'
|
||||
homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||
homeCom1.privateKey = Buffer.from('privateKey-HomeCommunity')
|
||||
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
|
||||
homeCom1.privateKey = Buffer.from(ed25519KeyPairStaticHex[0].private, 'hex')
|
||||
homeCom1.communityUuid = 'HomeCom-UUID'
|
||||
homeCom1.authenticatedAt = new Date()
|
||||
homeCom1.name = 'HomeCommunity-name'
|
||||
@ -319,8 +373,8 @@ describe('CommunityResolver', () => {
|
||||
homeCom1 = DbCommunity.create()
|
||||
homeCom1.foreign = false
|
||||
homeCom1.url = 'http://localhost/api'
|
||||
homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||
homeCom1.privateKey = Buffer.from('privateKey-HomeCommunity')
|
||||
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
|
||||
homeCom1.privateKey = Buffer.from(ed25519KeyPairStaticHex[0].private, 'hex')
|
||||
homeCom1.communityUuid = 'HomeCom-UUID'
|
||||
homeCom1.authenticatedAt = new Date()
|
||||
homeCom1.name = 'HomeCommunity-name'
|
||||
@ -331,8 +385,8 @@ describe('CommunityResolver', () => {
|
||||
foreignCom1 = DbCommunity.create()
|
||||
foreignCom1.foreign = true
|
||||
foreignCom1.url = 'http://stage-2.gradido.net/api'
|
||||
foreignCom1.publicKey = Buffer.from('publicKey-stage-2_Community')
|
||||
foreignCom1.privateKey = Buffer.from('privateKey-stage-2_Community')
|
||||
foreignCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[3].public, 'hex')
|
||||
foreignCom1.privateKey = Buffer.from(ed25519KeyPairStaticHex[3].private, 'hex')
|
||||
// foreignCom1.communityUuid = 'Stage2-Com-UUID'
|
||||
// foreignCom1.authenticatedAt = new Date()
|
||||
foreignCom1.name = 'Stage-2_Community-name'
|
||||
@ -343,8 +397,8 @@ describe('CommunityResolver', () => {
|
||||
foreignCom2 = DbCommunity.create()
|
||||
foreignCom2.foreign = true
|
||||
foreignCom2.url = 'http://stage-3.gradido.net/api'
|
||||
foreignCom2.publicKey = Buffer.from('publicKey-stage-3_Community')
|
||||
foreignCom2.privateKey = Buffer.from('privateKey-stage-3_Community')
|
||||
foreignCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[4].public, 'hex')
|
||||
foreignCom2.privateKey = Buffer.from(ed25519KeyPairStaticHex[4].private, 'hex')
|
||||
foreignCom2.communityUuid = 'Stage3-Com-UUID'
|
||||
foreignCom2.authenticatedAt = new Date()
|
||||
foreignCom2.name = 'Stage-3_Community-name'
|
||||
|
||||
@ -2609,7 +2609,7 @@ describe('ContributionResolver', () => {
|
||||
expect(transaction[0].linkedTransactionId).toEqual(null)
|
||||
expect(transaction[0].transactionLinkId).toEqual(null)
|
||||
expect(transaction[0].previous).toEqual(null)
|
||||
expect(transaction[0].linkedUserId).toEqual(null)
|
||||
expect(transaction[0].linkedUserId).toEqual(admin.id)
|
||||
expect(transaction[0].typeId).toEqual(1)
|
||||
})
|
||||
|
||||
|
||||
@ -451,6 +451,11 @@ export class ContributionResolver {
|
||||
transaction.userId = contribution.userId
|
||||
transaction.userGradidoID = user.gradidoID
|
||||
transaction.userName = fullName(user.firstName, user.lastName)
|
||||
transaction.userCommunityUuid = user.communityUuid
|
||||
transaction.linkedUserId = moderatorUser.id
|
||||
transaction.linkedUserGradidoID = moderatorUser.gradidoID
|
||||
transaction.linkedUserName = fullName(moderatorUser.firstName, moderatorUser.lastName)
|
||||
transaction.linkedUserCommunityUuid = moderatorUser.communityUuid
|
||||
transaction.previous = lastTransaction ? lastTransaction.id : null
|
||||
transaction.amount = contribution.amount
|
||||
transaction.creationDate = contribution.contributionDate
|
||||
|
||||
@ -142,7 +142,11 @@ describe('send coins', () => {
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('No user with this credentials', 'wrong@email.com')
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'No user with this credentials',
|
||||
'wrong@email.com',
|
||||
homeCom.communityUuid,
|
||||
)
|
||||
})
|
||||
|
||||
describe('deleted recipient', () => {
|
||||
@ -165,13 +169,17 @@ describe('send coins', () => {
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('No user to given contact')],
|
||||
errors: [new GraphQLError('No user with this credentials')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('logs the error thrown', () => {
|
||||
expect(logger.error).toBeCalledWith('No user to given contact', 'stephen@hawking.uk')
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'No user with this credentials',
|
||||
'stephen@hawking.uk',
|
||||
homeCom.communityUuid,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -204,6 +212,7 @@ describe('send coins', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'No user with this credentials',
|
||||
'garrick@ollivander.com',
|
||||
homeCom.communityUuid,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -109,9 +109,11 @@ export const executeTransaction = async (
|
||||
transactionSend.userId = sender.id
|
||||
transactionSend.userGradidoID = sender.gradidoID
|
||||
transactionSend.userName = fullName(sender.firstName, sender.lastName)
|
||||
transactionSend.userCommunityUuid = sender.communityUuid
|
||||
transactionSend.linkedUserId = recipient.id
|
||||
transactionSend.linkedUserGradidoID = recipient.gradidoID
|
||||
transactionSend.linkedUserName = fullName(recipient.firstName, recipient.lastName)
|
||||
transactionSend.linkedUserCommunityUuid = recipient.communityUuid
|
||||
transactionSend.amount = amount.mul(-1)
|
||||
transactionSend.balance = sendBalance.balance
|
||||
transactionSend.balanceDate = receivedCallDate
|
||||
@ -129,9 +131,11 @@ export const executeTransaction = async (
|
||||
transactionReceive.userId = recipient.id
|
||||
transactionReceive.userGradidoID = recipient.gradidoID
|
||||
transactionReceive.userName = fullName(recipient.firstName, recipient.lastName)
|
||||
transactionReceive.userCommunityUuid = recipient.communityUuid
|
||||
transactionReceive.linkedUserId = sender.id
|
||||
transactionReceive.linkedUserGradidoID = sender.gradidoID
|
||||
transactionReceive.linkedUserName = fullName(sender.firstName, sender.lastName)
|
||||
transactionReceive.linkedUserCommunityUuid = sender.communityUuid
|
||||
transactionReceive.amount = amount
|
||||
const receiveBalance = await calculateBalance(recipient.id, amount, receivedCallDate)
|
||||
transactionReceive.balance = receiveBalance ? receiveBalance.balance : amount
|
||||
@ -254,6 +258,9 @@ export class TransactionResolver {
|
||||
// userTransactions.forEach((transaction: dbTransaction) => {
|
||||
// use normal for loop because of timing problems with await in forEach-loop
|
||||
for (const transaction of userTransactions) {
|
||||
if (transaction.typeId === TransactionTypeId.CREATION) {
|
||||
continue
|
||||
}
|
||||
if (transaction.linkedUserId && !involvedUserIds.includes(transaction.linkedUserId)) {
|
||||
involvedUserIds.push(transaction.linkedUserId)
|
||||
}
|
||||
@ -425,7 +432,7 @@ export class TransactionResolver {
|
||||
const senderUser = getUser(context)
|
||||
|
||||
if (!recipientCommunityIdentifier || (await isHomeCommunity(recipientCommunityIdentifier))) {
|
||||
// processing sendCoins within sender and recepient are both in home community
|
||||
// processing sendCoins within sender and recipient are both in home community
|
||||
const recipientUser = await findUserByIdentifier(
|
||||
recipientIdentifier,
|
||||
recipientCommunityIdentifier,
|
||||
|
||||
@ -2561,6 +2561,7 @@ describe('UserResolver', () => {
|
||||
query: userQuery,
|
||||
variables: {
|
||||
identifier: 'identifier',
|
||||
communityIdentifier: 'community identifier',
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
@ -2646,13 +2647,11 @@ describe('UserResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
new GraphQLError('Found user to given contact, but belongs to other community'),
|
||||
],
|
||||
errors: [new GraphQLError('No user with this credentials')],
|
||||
}),
|
||||
)
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Found user to given contact, but belongs to other community',
|
||||
'No user with this credentials',
|
||||
'bibi@bloxberg.de',
|
||||
foreignCom1.communityUuid,
|
||||
)
|
||||
|
||||
@ -65,7 +65,7 @@ import random from 'random-bigint'
|
||||
import { randombytes_random } from 'sodium-native'
|
||||
|
||||
import { FULL_CREATION_AVAILABLE } from './const/const'
|
||||
import { getCommunityName, getHomeCommunity } from './util/communities'
|
||||
import { getHomeCommunity } from './util/communities'
|
||||
import { getUserCreations } from './util/creations'
|
||||
import { findUserByIdentifier } from './util/findUserByIdentifier'
|
||||
import { findUsers } from './util/findUsers'
|
||||
@ -834,11 +834,6 @@ export class UserResolver {
|
||||
): Promise<User> {
|
||||
const foundDbUser = await findUserByIdentifier(identifier, communityIdentifier)
|
||||
const modelUser = new User(foundDbUser)
|
||||
if (!foundDbUser.communityUuid) {
|
||||
modelUser.communityName = (await Promise.resolve(getHomeCommunity())).name
|
||||
} else {
|
||||
modelUser.communityName = await getCommunityName(foundDbUser.communityUuid)
|
||||
}
|
||||
return modelUser
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import { Community as DbCommunity } from '@entity/Community'
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, testEnvironment, contributionDateFormatter } from '@test/helpers'
|
||||
|
||||
@ -54,7 +55,7 @@ describe('semaphore', () => {
|
||||
beforeAll(async () => {
|
||||
const now = new Date()
|
||||
homeCom = DbCommunity.create()
|
||||
homeCom.communityUuid = 'homeCom-UUID'
|
||||
homeCom.communityUuid = uuidv4()
|
||||
homeCom.creationDate = new Date('2000-01-01')
|
||||
homeCom.description = 'homeCom description'
|
||||
homeCom.foreign = false
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { FindOptionsWhere } from '@dbTools/typeorm'
|
||||
import { Community } from '@entity/Community'
|
||||
import { User as DbUser } from '@entity/User'
|
||||
import { UserContact as DbUserContact } from '@entity/UserContact'
|
||||
import { validate, version } from 'uuid'
|
||||
@ -6,15 +8,26 @@ import { LogError } from '@/server/LogError'
|
||||
|
||||
import { VALID_ALIAS_REGEX } from './validateAlias'
|
||||
|
||||
/**
|
||||
*
|
||||
* @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,
|
||||
communityIdentifier: string,
|
||||
): Promise<DbUser> => {
|
||||
let user: DbUser | null
|
||||
const communityWhere: FindOptionsWhere<Community> =
|
||||
validate(communityIdentifier) && version(communityIdentifier) === 4
|
||||
? { communityUuid: communityIdentifier }
|
||||
: { name: communityIdentifier }
|
||||
|
||||
if (validate(identifier) && version(identifier) === 4) {
|
||||
user = await DbUser.findOne({
|
||||
where: { gradidoID: identifier, communityUuid: communityIdentifier },
|
||||
relations: ['emailContact'],
|
||||
where: { gradidoID: identifier, community: communityWhere },
|
||||
relations: ['emailContact', 'community'],
|
||||
})
|
||||
if (!user) {
|
||||
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
|
||||
@ -24,28 +37,21 @@ export const findUserByIdentifier = async (
|
||||
where: {
|
||||
email: identifier,
|
||||
emailChecked: true,
|
||||
user: {
|
||||
community: communityWhere,
|
||||
},
|
||||
},
|
||||
relations: ['user'],
|
||||
relations: { user: { community: true } },
|
||||
})
|
||||
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,
|
||||
)
|
||||
throw new LogError('No user with this credentials', 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'],
|
||||
where: { alias: identifier, community: communityWhere },
|
||||
relations: ['emailContact', 'community'],
|
||||
})
|
||||
if (!user) {
|
||||
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
import { Connection } from '@dbTools/typeorm'
|
||||
import { Community as DbCommunity } from '@entity/Community'
|
||||
import { User as DbUser } from '@entity/User'
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
|
||||
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'
|
||||
|
||||
let con: Connection
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment()
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
})
|
||||
|
||||
describe('graphql/resolver/util/findUserByIdentifier', () => {
|
||||
let homeCom: DbCommunity
|
||||
let communityUuid: string
|
||||
let communityName: string
|
||||
let userBibi: DbUser
|
||||
|
||||
beforeAll(async () => {
|
||||
homeCom = await writeHomeCommunityEntry()
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
communityUuid = homeCom.communityUuid!
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -22,6 +22,13 @@ import { logger, i18n as localization } from '@test/testSetup'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
|
||||
import { creations } from '@/seeds/creation'
|
||||
import { creationFactory } from '@/seeds/factory/creation'
|
||||
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 { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz'
|
||||
|
||||
import { sendTransactionsToDltConnector } from './sendTransactionsToDltConnector'
|
||||
|
||||
@ -423,9 +430,17 @@ describe('create and send Transactions to DltConnector', () => {
|
||||
|
||||
describe('with 3 creations and active dlt-connector', () => {
|
||||
it('found 3 dlt-transactions', async () => {
|
||||
txCREATION1 = await createTxCREATION1(false)
|
||||
txCREATION2 = await createTxCREATION2(false)
|
||||
txCREATION3 = await createTxCREATION3(false)
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await userFactory(testEnv, raeuberHotzenplotz)
|
||||
await userFactory(testEnv, bobBaumeister)
|
||||
let count = 0
|
||||
for (const creation of creations) {
|
||||
await creationFactory(testEnv, creation)
|
||||
count++
|
||||
// we need only 3 for testing
|
||||
if (count >= 3) break
|
||||
}
|
||||
await createHomeCommunity()
|
||||
|
||||
CONFIG.DLT_CONNECTOR = true
|
||||
@ -435,10 +450,7 @@ describe('create and send Transactions to DltConnector', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return {
|
||||
data: {
|
||||
sendTransaction: {
|
||||
dltTransactionIdHex:
|
||||
'723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
},
|
||||
sendTransaction: { succeed: true },
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
@ -464,7 +476,7 @@ describe('create and send Transactions to DltConnector', () => {
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[0].id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
@ -472,7 +484,7 @@ describe('create and send Transactions to DltConnector', () => {
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[1].id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
@ -480,7 +492,7 @@ describe('create and send Transactions to DltConnector', () => {
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[2].id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
@ -514,10 +526,7 @@ describe('create and send Transactions to DltConnector', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return {
|
||||
data: {
|
||||
sendTransaction: {
|
||||
dltTransactionIdHex:
|
||||
'723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
},
|
||||
sendTransaction: { succeed: true },
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
@ -569,7 +578,7 @@ describe('create and send Transactions to DltConnector', () => {
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txSEND1to2.id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
@ -577,7 +586,7 @@ describe('create and send Transactions to DltConnector', () => {
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txRECEIVE2From1.id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { IsNull } from '@dbTools/typeorm'
|
||||
import { Community } from '@entity/Community'
|
||||
import { DltTransaction } from '@entity/DltTransaction'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
|
||||
import { DltConnectorClient } from '@/apis/DltConnectorClient'
|
||||
import { DltConnectorClient } from '@dltConnector/DltConnectorClient'
|
||||
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { Monitor, MonitorNames } from '@/util/Monitor'
|
||||
|
||||
@ -17,13 +17,6 @@ export async function sendTransactionsToDltConnector(): Promise<void> {
|
||||
try {
|
||||
await createDltTransactions()
|
||||
const dltConnector = DltConnectorClient.getInstance()
|
||||
// TODO: get actual communities from users
|
||||
const homeCommunity = await Community.findOneOrFail({ where: { foreign: false } })
|
||||
const senderCommunityUuid = homeCommunity.communityUuid
|
||||
if (!senderCommunityUuid) {
|
||||
throw new Error('Cannot find community uuid of home community')
|
||||
}
|
||||
const recipientCommunityUuid = ''
|
||||
if (dltConnector) {
|
||||
logger.debug('with sending to DltConnector...')
|
||||
const dltTransactions = await DltTransaction.find({
|
||||
@ -37,22 +30,14 @@ export async function sendTransactionsToDltConnector(): Promise<void> {
|
||||
continue
|
||||
}
|
||||
try {
|
||||
const messageId = await dltConnector.transmitTransaction(
|
||||
dltTx.transaction,
|
||||
senderCommunityUuid,
|
||||
recipientCommunityUuid,
|
||||
)
|
||||
const dltMessageId = Buffer.from(messageId, 'hex')
|
||||
if (dltMessageId.length !== 32) {
|
||||
logger.error(
|
||||
'Error dlt message id is invalid: %s, should by 32 Bytes long in binary after converting from hex',
|
||||
dltMessageId,
|
||||
)
|
||||
return
|
||||
const result = await dltConnector.transmitTransaction(dltTx.transaction)
|
||||
// message id isn't known at this point of time, because transaction will not direct sended to iota,
|
||||
// it will first go to db and then sended, if no transaction is in db before
|
||||
if (result) {
|
||||
dltTx.messageId = 'sended'
|
||||
await DltTransaction.save(dltTx)
|
||||
logger.info('store messageId=%s in dltTx=%d', dltTx.messageId, dltTx.id)
|
||||
}
|
||||
dltTx.messageId = dltMessageId.toString('hex')
|
||||
await DltTransaction.save(dltTx)
|
||||
logger.info('store messageId=%s in dltTx=%d', dltTx.messageId, dltTx.id)
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`error while sending to dlt-connector or writing messageId of dltTx=${dltTx.id}`,
|
||||
|
||||
@ -387,7 +387,7 @@ export const adminListContributionMessages = gql`
|
||||
`
|
||||
|
||||
export const user = gql`
|
||||
query ($identifier: String!, $communityIdentifier: String) {
|
||||
query ($identifier: String!, $communityIdentifier: String!) {
|
||||
user(identifier: $identifier, communityIdentifier: $communityIdentifier) {
|
||||
firstName
|
||||
lastName
|
||||
|
||||
@ -4,6 +4,7 @@ export const bibiBloxberg: UserInterface = {
|
||||
email: 'bibi@bloxberg.de',
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
alias: 'BBB',
|
||||
// description: 'Hex Hex',
|
||||
emailChecked: true,
|
||||
language: 'de',
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
import { Connection as DbConnection } from '@dbTools/typeorm'
|
||||
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 { CONFIG } from '@/config'
|
||||
@ -56,6 +58,28 @@ export const createServer = async (
|
||||
// cors
|
||||
app.use(cors)
|
||||
|
||||
// Helmet helps secure Express apps by setting HTTP response headers.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
app.use(helmet())
|
||||
|
||||
// rate limiter/ slow down to many requests
|
||||
const limiter = slowDown({
|
||||
windowMs: 1000, // 1 second
|
||||
delayAfter: 10, // Allow 10 requests per 1 second.
|
||||
delayMs: (hits) => hits * 50, // Add 100 ms of delay to every request after the 10th one.
|
||||
/**
|
||||
* So:
|
||||
*
|
||||
* - requests 1-10 are not delayed.
|
||||
* - request 11 is delayed by 550ms
|
||||
* - request 12 is delayed by 600ms
|
||||
* - request 13 is delayed by 650ms
|
||||
*
|
||||
* and so on. After 1 seconds, the delay is reset to 0.
|
||||
*/
|
||||
})
|
||||
app.use(limiter)
|
||||
|
||||
// bodyparser json
|
||||
app.use(json())
|
||||
// bodyparser urlencoded for elopage
|
||||
|
||||
@ -50,6 +50,7 @@ const communityDbUser: dbUser = {
|
||||
},
|
||||
foreign: false,
|
||||
communityUuid: '55555555-4444-4333-2222-11111111',
|
||||
community: null,
|
||||
gmsPublishName: 0,
|
||||
gmsAllowed: false,
|
||||
location: null,
|
||||
|
||||
@ -49,6 +49,7 @@
|
||||
"paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
"@/*": ["src/*"],
|
||||
"@arg/*": ["src/graphql/arg/*"],
|
||||
"@dltConnector/*": ["src/apis/dltConnector/*"],
|
||||
"@enum/*": ["src/graphql/enum/*"],
|
||||
"@model/*": ["src/graphql/model/*"],
|
||||
"@union/*": ["src/graphql/union/*"],
|
||||
@ -57,7 +58,8 @@
|
||||
"@test/*": ["test/*"],
|
||||
/* external */
|
||||
"@dbTools/*": ["../database/src/*", "../../database/build/src/*"],
|
||||
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"]
|
||||
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"],
|
||||
"@logging/*": ["../database/logging/*", "../../database/build/logging/*"]
|
||||
},
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
"typeRoots": ["@types", "node_modules/@types"], /* List of folders to include type definitions from. */
|
||||
|
||||
@ -3225,6 +3225,18 @@ expect@^27.2.5:
|
||||
jest-message-util "^27.2.5"
|
||||
jest-regex-util "^27.0.6"
|
||||
|
||||
express-rate-limit@7:
|
||||
version "7.1.5"
|
||||
resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.1.5.tgz#af4c81143a945ea97f2599d13957440a0ddbfcfe"
|
||||
integrity sha512-/iVogxu7ueadrepw1bS0X0kaRC/U0afwiYRSLg68Ts+p4Dc85Q5QKsOnPS/QUjPMHvOJQtBDrZgvkOzf8ejUYw==
|
||||
|
||||
express-slow-down@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/express-slow-down/-/express-slow-down-2.0.1.tgz#60c4515467314675d89c54ec608e2d586aa30f87"
|
||||
integrity sha512-zRogSZhNXJYKDBekhgFfFXGrOngH7Fub7Mx2g8OQ4RUBwSJP/3TVEKMgSGR/WlneT0mJ6NBUnidHhIELGVPe3w==
|
||||
dependencies:
|
||||
express-rate-limit "7"
|
||||
|
||||
express@^4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
|
||||
@ -3679,7 +3691,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||
|
||||
"gradido-database@file:../database":
|
||||
version "2.0.1"
|
||||
version "2.1.1"
|
||||
dependencies:
|
||||
"@types/uuid" "^8.3.4"
|
||||
cross-env "^7.0.3"
|
||||
@ -3826,6 +3838,11 @@ he@1.2.0, he@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
helmet@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/helmet/-/helmet-5.1.1.tgz#609823c5c2e78aea62dd9afc8f544ca409da5e85"
|
||||
integrity sha512-/yX0oVZBggA9cLJh8aw3PPCfedBnbd7J2aowjzsaWwZh7/UFY0nccn/aHAggIgWUFfnykX8GKd3a1pSbrmlcVQ==
|
||||
|
||||
highlight.js@^10.7.1:
|
||||
version "10.7.3"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
|
||||
|
||||
70
database/entity/0081-user_join_community/Community.ts
Normal file
70
database/entity/0081-user_join_community/Community.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import {
|
||||
BaseEntity,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToMany,
|
||||
JoinColumn,
|
||||
} from 'typeorm'
|
||||
import { User } from '../User'
|
||||
|
||||
@Entity('communities')
|
||||
export class Community extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({ name: 'foreign', type: 'bool', nullable: false, default: true })
|
||||
foreign: boolean
|
||||
|
||||
@Column({ name: 'url', length: 255, nullable: false })
|
||||
url: string
|
||||
|
||||
@Column({ name: 'public_key', type: 'binary', length: 32, nullable: false })
|
||||
publicKey: Buffer
|
||||
|
||||
@Column({ name: 'private_key', type: 'binary', length: 64, nullable: true })
|
||||
privateKey: Buffer | null
|
||||
|
||||
@Column({
|
||||
name: 'community_uuid',
|
||||
type: 'char',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
communityUuid: string | null
|
||||
|
||||
@Column({ name: 'authenticated_at', type: 'datetime', nullable: true })
|
||||
authenticatedAt: Date | null
|
||||
|
||||
@Column({ name: 'name', type: 'varchar', length: 40, nullable: true })
|
||||
name: string | null
|
||||
|
||||
@Column({ name: 'description', type: 'varchar', length: 255, nullable: true })
|
||||
description: string | null
|
||||
|
||||
@CreateDateColumn({ name: 'creation_date', type: 'datetime', nullable: true })
|
||||
creationDate: Date | null
|
||||
|
||||
@CreateDateColumn({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||
nullable: false,
|
||||
})
|
||||
createdAt: Date
|
||||
|
||||
@UpdateDateColumn({
|
||||
name: 'updated_at',
|
||||
type: 'datetime',
|
||||
onUpdate: 'CURRENT_TIMESTAMP(3)',
|
||||
nullable: true,
|
||||
})
|
||||
updatedAt: Date | null
|
||||
|
||||
@OneToMany(() => User, (user) => user.community)
|
||||
@JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' })
|
||||
users: User[]
|
||||
}
|
||||
138
database/entity/0081-user_join_community/User.ts
Normal file
138
database/entity/0081-user_join_community/User.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import {
|
||||
BaseEntity,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
DeleteDateColumn,
|
||||
OneToMany,
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
ManyToOne,
|
||||
} from 'typeorm'
|
||||
import { Contribution } from '../Contribution'
|
||||
import { ContributionMessage } from '../ContributionMessage'
|
||||
import { UserContact } from '../UserContact'
|
||||
import { UserRole } from '../UserRole'
|
||||
import { Community } from '../Community'
|
||||
|
||||
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||
export class User extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({ type: 'bool', default: false })
|
||||
foreign: boolean
|
||||
|
||||
@Column({
|
||||
name: 'gradido_id',
|
||||
length: 36,
|
||||
nullable: false,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
gradidoID: string
|
||||
|
||||
@Column({
|
||||
name: 'community_uuid',
|
||||
type: 'char',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
communityUuid: string
|
||||
|
||||
@ManyToOne(() => Community, (community) => community.users)
|
||||
@JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' })
|
||||
community: Community | null
|
||||
|
||||
@Column({
|
||||
name: 'alias',
|
||||
length: 20,
|
||||
nullable: true,
|
||||
default: null,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
alias: string
|
||||
|
||||
@OneToOne(() => UserContact, (emailContact: UserContact) => emailContact.user)
|
||||
@JoinColumn({ name: 'email_id' })
|
||||
emailContact: UserContact
|
||||
|
||||
@Column({ name: 'email_id', type: 'int', unsigned: true, nullable: true, default: null })
|
||||
emailId: number | null
|
||||
|
||||
@Column({
|
||||
name: 'first_name',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
default: null,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
firstName: string
|
||||
|
||||
@Column({
|
||||
name: 'last_name',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
default: null,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
lastName: string
|
||||
|
||||
@Column({ name: 'created_at', default: () => 'CURRENT_TIMESTAMP(3)', nullable: false })
|
||||
createdAt: Date
|
||||
|
||||
@DeleteDateColumn({ name: 'deleted_at', nullable: true })
|
||||
deletedAt: Date | null
|
||||
|
||||
@Column({ type: 'bigint', default: 0, unsigned: true })
|
||||
password: BigInt
|
||||
|
||||
@Column({
|
||||
name: 'password_encryption_type',
|
||||
type: 'int',
|
||||
unsigned: true,
|
||||
nullable: false,
|
||||
default: 0,
|
||||
})
|
||||
passwordEncryptionType: number
|
||||
|
||||
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
|
||||
language: string
|
||||
|
||||
@Column({ type: 'bool', default: false })
|
||||
hideAmountGDD: boolean
|
||||
|
||||
@Column({ type: 'bool', default: false })
|
||||
hideAmountGDT: boolean
|
||||
|
||||
@OneToMany(() => UserRole, (userRole) => userRole.user)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
userRoles: UserRole[]
|
||||
|
||||
@Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null })
|
||||
referrerId?: number | null
|
||||
|
||||
@Column({
|
||||
name: 'contribution_link_id',
|
||||
type: 'int',
|
||||
unsigned: true,
|
||||
nullable: true,
|
||||
default: null,
|
||||
})
|
||||
contributionLinkId?: number | null
|
||||
|
||||
@Column({ name: 'publisher_id', default: 0 })
|
||||
publisherId: number
|
||||
|
||||
@OneToMany(() => Contribution, (contribution) => contribution.user)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
contributions?: Contribution[]
|
||||
|
||||
@OneToMany(() => ContributionMessage, (message) => message.user)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
messages?: ContributionMessage[]
|
||||
|
||||
@OneToMany(() => UserContact, (userContact: UserContact) => userContact.user)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
userContacts?: UserContact[]
|
||||
}
|
||||
@ -5,7 +5,10 @@ import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
OneToMany,
|
||||
JoinColumn,
|
||||
} from 'typeorm'
|
||||
import { User } from '../User'
|
||||
|
||||
@Entity('communities')
|
||||
export class Community extends BaseEntity {
|
||||
@ -63,4 +66,8 @@ export class Community extends BaseEntity {
|
||||
nullable: true,
|
||||
})
|
||||
updatedAt: Date | null
|
||||
|
||||
@OneToMany(() => User, (user) => user.community)
|
||||
@JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' })
|
||||
users: User[]
|
||||
}
|
||||
@ -8,11 +8,13 @@ import {
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
Geometry,
|
||||
ManyToOne,
|
||||
} from 'typeorm'
|
||||
import { Contribution } from '../Contribution'
|
||||
import { ContributionMessage } from '../ContributionMessage'
|
||||
import { UserContact } from '../UserContact'
|
||||
import { UserRole } from '../UserRole'
|
||||
import { Community } from '../Community'
|
||||
|
||||
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||
export class User extends BaseEntity {
|
||||
@ -39,6 +41,10 @@ export class User extends BaseEntity {
|
||||
})
|
||||
communityUuid: string
|
||||
|
||||
@ManyToOne(() => Community, (community) => community.users)
|
||||
@JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' })
|
||||
community: Community | null
|
||||
|
||||
@Column({
|
||||
name: 'alias',
|
||||
length: 20,
|
||||
@ -1 +1 @@
|
||||
export { Community } from './0079-introduce_gms_registration/Community'
|
||||
export { Community } from './0082-introduce_gms_registration/Community'
|
||||
|
||||
@ -1 +1 @@
|
||||
export { User } from './0079-introduce_gms_registration/User'
|
||||
export { User } from './0082-introduce_gms_registration/User'
|
||||
|
||||
@ -1 +1 @@
|
||||
export { UserContact } from './0079-introduce_gms_registration/UserContact'
|
||||
export { UserContact } from './0082-introduce_gms_registration/UserContact'
|
||||
|
||||
38
database/logging/AbstractLogging.view.ts
Normal file
38
database/logging/AbstractLogging.view.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import util from 'util'
|
||||
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
export abstract class AbstractLoggingView {
|
||||
// eslint-disable-next-line no-undef
|
||||
protected bufferStringFormat: BufferEncoding = 'hex'
|
||||
|
||||
// This function gets called automatically when JSON.stringify() is called on this class instance
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public abstract toJSON(): any
|
||||
public toString(compact = false): string {
|
||||
if (compact) {
|
||||
return JSON.stringify(this.toJSON())
|
||||
} else {
|
||||
return JSON.stringify(this.toJSON(), null, 2)
|
||||
}
|
||||
}
|
||||
|
||||
// called form console.log or log4js logging functions
|
||||
[util.inspect.custom](): string {
|
||||
return this.toString()
|
||||
}
|
||||
|
||||
public dateToString(date: Date | undefined | null): string | undefined {
|
||||
if (date) {
|
||||
return date.toISOString()
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
public decimalToString(number: Decimal | undefined | null): string | undefined {
|
||||
if (number) {
|
||||
return number.toString()
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
26
database/logging/CommunityLogging.view.ts
Normal file
26
database/logging/CommunityLogging.view.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Community } from '../entity/Community'
|
||||
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
|
||||
export class CommunityLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: Community) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
foreign: this.self.foreign,
|
||||
url: this.self.url,
|
||||
publicKey: this.self.publicKey.toString(this.bufferStringFormat),
|
||||
communityUuid: this.self.communityUuid,
|
||||
authenticatedAt: this.dateToString(this.self.authenticatedAt),
|
||||
name: this.self.name,
|
||||
description: this.self.description?.substring(0, 24),
|
||||
creationDate: this.dateToString(this.self.creationDate),
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
updatedAt: this.dateToString(this.self.updatedAt),
|
||||
}
|
||||
}
|
||||
}
|
||||
45
database/logging/ContributionLogging.view.ts
Normal file
45
database/logging/ContributionLogging.view.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { Contribution } from '../entity/Contribution'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { ContributionMessageLoggingView } from './ContributionMessageLogging.view'
|
||||
import { TransactionLoggingView } from './TransactionLogging.view'
|
||||
import { UserLoggingView } from './UserLogging.view'
|
||||
|
||||
export class ContributionLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: Contribution) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
user: this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
resubmissionAt: this.dateToString(this.self.resubmissionAt),
|
||||
contributionDate: this.dateToString(this.self.contributionDate),
|
||||
memoLength: this.self.memo.length,
|
||||
amount: this.decimalToString(this.self.amount),
|
||||
moderatorId: this.self.moderatorId,
|
||||
contributionLinkId: this.self.contributionLinkId,
|
||||
confirmedBy: this.self.confirmedBy,
|
||||
confirmedAt: this.dateToString(this.self.confirmedAt),
|
||||
deniedBy: this.self.deniedBy,
|
||||
deniedAt: this.dateToString(this.self.deniedAt),
|
||||
contributionType: this.self.contributionType,
|
||||
contributionStatus: this.self.contributionStatus,
|
||||
transactionId: this.self.transactionId,
|
||||
updatedAt: this.dateToString(this.self.updatedAt),
|
||||
updatedBy: this.self.updatedBy,
|
||||
deletedAt: this.dateToString(this.self.deletedAt),
|
||||
deletedBy: this.self.deletedBy,
|
||||
messages: this.self.messages
|
||||
? this.self.messages.map((message) => new ContributionMessageLoggingView(message).toJSON())
|
||||
: undefined,
|
||||
transaction: this.self.transaction
|
||||
? new TransactionLoggingView(this.self.transaction).toJSON()
|
||||
: { id: this.self.transactionId },
|
||||
}
|
||||
}
|
||||
}
|
||||
30
database/logging/ContributionMessageLogging.view.ts
Normal file
30
database/logging/ContributionMessageLogging.view.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { ContributionMessage } from '../entity/ContributionMessage'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { ContributionLoggingView } from './ContributionLogging.view'
|
||||
import { UserLoggingView } from './UserLogging.view'
|
||||
|
||||
export class ContributionMessageLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: ContributionMessage) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
contribution: this.self.contribution
|
||||
? new ContributionLoggingView(this.self.contribution).toJSON()
|
||||
: { id: this.self.contributionId },
|
||||
user: this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
messageLength: this.self.message.length,
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
updatedAt: this.dateToString(this.self.updatedAt),
|
||||
deletedAt: this.dateToString(this.self.deletedAt),
|
||||
deletedBy: this.self.deletedBy,
|
||||
type: this.self.type,
|
||||
isModerator: this.self.isModerator,
|
||||
}
|
||||
}
|
||||
}
|
||||
23
database/logging/DltTransactionLogging.view.ts
Normal file
23
database/logging/DltTransactionLogging.view.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { DltTransaction } from '../entity/DltTransaction'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { TransactionLoggingView } from './TransactionLogging.view'
|
||||
|
||||
export class DltTransactionLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: DltTransaction) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
transaction: this.self.transaction
|
||||
? new TransactionLoggingView(this.self.transaction).toJSON()
|
||||
: { id: this.self.transactionId },
|
||||
messageId: this.self.messageId,
|
||||
verified: this.self.verified,
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
verifiedAt: this.dateToString(this.self.verifiedAt),
|
||||
}
|
||||
}
|
||||
}
|
||||
24
database/logging/FederatedCommunityLogging.view.ts
Normal file
24
database/logging/FederatedCommunityLogging.view.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { FederatedCommunity } from '../entity/FederatedCommunity'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
|
||||
export class FederatedCommunityLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: FederatedCommunity) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
foreign: this.self.foreign,
|
||||
publicKey: this.self.publicKey.toString(this.bufferStringFormat),
|
||||
apiVersion: this.self.apiVersion,
|
||||
endPoint: this.self.endPoint,
|
||||
lastAnnouncedAt: this.dateToString(this.self.lastAnnouncedAt),
|
||||
verifiedAt: this.self.verifiedAt,
|
||||
lastErrorAt: this.self.lastErrorAt,
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
updatedAt: this.dateToString(this.self.updatedAt),
|
||||
}
|
||||
}
|
||||
}
|
||||
27
database/logging/PendingTransactionLogging.view.ts
Normal file
27
database/logging/PendingTransactionLogging.view.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { PendingTransaction } from '../entity/PendingTransaction'
|
||||
import { Transaction } from '../entity/Transaction'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { TransactionLoggingView } from './TransactionLogging.view'
|
||||
|
||||
// TODO: move enum into database, maybe rename database
|
||||
enum PendingTransactionState {
|
||||
NEW = 1,
|
||||
PENDING = 2,
|
||||
SETTLED = 3,
|
||||
REVERTED = 4,
|
||||
}
|
||||
|
||||
export class PendingTransactionLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: PendingTransaction) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
...new TransactionLoggingView(this.self as Transaction).toJSON(),
|
||||
state: PendingTransactionState[this.self.state],
|
||||
}
|
||||
}
|
||||
}
|
||||
56
database/logging/TransactionLogging.view.ts
Normal file
56
database/logging/TransactionLogging.view.ts
Normal file
@ -0,0 +1,56 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { Transaction } from '../entity/Transaction'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { ContributionLoggingView } from './ContributionLogging.view'
|
||||
import { DltTransactionLoggingView } from './DltTransactionLogging.view'
|
||||
|
||||
// TODO: move enum into database, maybe rename database
|
||||
enum TransactionTypeId {
|
||||
CREATION = 1,
|
||||
SEND = 2,
|
||||
RECEIVE = 3,
|
||||
// This is a virtual property, never occurring on the database
|
||||
DECAY = 4,
|
||||
LINK_SUMMARY = 5,
|
||||
}
|
||||
|
||||
export class TransactionLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: Transaction) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
previous: this.self.previous,
|
||||
typeId: TransactionTypeId[this.self.typeId],
|
||||
transactionLinkId: this.self.transactionLinkId,
|
||||
amount: this.decimalToString(this.self.amount),
|
||||
balance: this.decimalToString(this.self.balance),
|
||||
balanceDate: this.dateToString(this.self.balanceDate),
|
||||
decay: this.decimalToString(this.self.decay),
|
||||
decayStart: this.dateToString(this.self.decayStart),
|
||||
memoLength: this.self.memo.length,
|
||||
creationDate: this.dateToString(this.self.creationDate),
|
||||
userId: this.self.userId,
|
||||
userCommunityUuid: this.self.userCommunityUuid,
|
||||
userGradidoId: this.self.userGradidoID,
|
||||
userName: this.self.userName?.substring(0, 3) + '...',
|
||||
linkedUserId: this.self.linkedUserId,
|
||||
linkedUserCommunityUuid: this.self.linkedUserCommunityUuid,
|
||||
linkedUserGradidoID: this.self.linkedUserGradidoID,
|
||||
linkedUserName: this.self.linkedUserName?.substring(0, 3) + '...',
|
||||
linkedTransactionId: this.self.linkedTransactionId,
|
||||
contribution: this.self.contribution
|
||||
? new ContributionLoggingView(this.self.contribution)
|
||||
: undefined,
|
||||
dltTransaction: this.self.dltTransaction
|
||||
? new DltTransactionLoggingView(this.self.dltTransaction).toJSON()
|
||||
: undefined,
|
||||
previousTransaction: this.self.previousTransaction
|
||||
? new TransactionLoggingView(this.self.previousTransaction).toJSON()
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
35
database/logging/UserContactLogging.view.ts
Normal file
35
database/logging/UserContactLogging.view.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { UserContact } from '../entity/UserContact'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { UserLoggingView } from './UserLogging.view'
|
||||
|
||||
enum OptInType {
|
||||
EMAIL_OPT_IN_REGISTER = 1,
|
||||
EMAIL_OPT_IN_RESET_PASSWORD = 2,
|
||||
}
|
||||
|
||||
export class UserContactLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: UserContact) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
type: this.self.type,
|
||||
user: this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
email: this.self.email?.substring(0, 3) + '...',
|
||||
emailVerificationCode: this.self.emailVerificationCode?.substring(0, 4) + '...',
|
||||
emailOptInTypeId: OptInType[this.self.emailOptInTypeId],
|
||||
emailResendCount: this.self.emailResendCount,
|
||||
emailChecked: this.self.emailChecked,
|
||||
phone: this.self.phone ? this.self.phone.substring(0, 3) + '...' : undefined,
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
updatedAt: this.dateToString(this.self.updatedAt),
|
||||
deletedAt: this.dateToString(this.self.deletedAt),
|
||||
}
|
||||
}
|
||||
}
|
||||
60
database/logging/UserLogging.view.ts
Normal file
60
database/logging/UserLogging.view.ts
Normal file
@ -0,0 +1,60 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { User } from '../entity/User'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { ContributionLoggingView } from './ContributionLogging.view'
|
||||
import { ContributionMessageLoggingView } from './ContributionMessageLogging.view'
|
||||
import { UserContactLoggingView } from './UserContactLogging.view'
|
||||
import { UserRoleLoggingView } from './UserRoleLogging.view'
|
||||
|
||||
enum PasswordEncryptionType {
|
||||
NO_PASSWORD = 0,
|
||||
EMAIL = 1,
|
||||
GRADIDO_ID = 2,
|
||||
}
|
||||
|
||||
export class UserLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: User) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
foreign: this.self.foreign,
|
||||
gradidoID: this.self.gradidoID,
|
||||
communityUuid: this.self.communityUuid,
|
||||
alias: this.self.alias?.substring(0, 3) + '...',
|
||||
emailContact: this.self.emailContact
|
||||
? new UserContactLoggingView(this.self.emailContact).toJSON()
|
||||
: { id: this.self.emailId },
|
||||
firstName: this.self.firstName?.substring(0, 3) + '...',
|
||||
lastName: this.self.lastName?.substring(0, 3) + '...',
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
deletedAt: this.dateToString(this.self.deletedAt),
|
||||
passwordEncryptionType: this.self.passwordEncryptionType as PasswordEncryptionType,
|
||||
language: this.self.language,
|
||||
hideAmountGDD: this.self.hideAmountGDD,
|
||||
hideAmountGDT: this.self.hideAmountGDT,
|
||||
userRoles: this.self.userRoles
|
||||
? this.self.userRoles.map((userRole) => new UserRoleLoggingView(userRole).toJSON())
|
||||
: undefined,
|
||||
referrerId: this.self.referrerId,
|
||||
contributionLinkId: this.self.contributionLinkId,
|
||||
publisherId: this.self.publisherId,
|
||||
contributions: this.self.contributions
|
||||
? this.self.contributions.map((contribution) =>
|
||||
new ContributionLoggingView(contribution).toJSON(),
|
||||
)
|
||||
: undefined,
|
||||
messages: this.self.messages
|
||||
? this.self.messages.map((message) => new ContributionMessageLoggingView(message).toJSON())
|
||||
: undefined,
|
||||
userContacts: this.self.userContacts
|
||||
? this.self.userContacts.map((userContact) =>
|
||||
new UserContactLoggingView(userContact).toJSON(),
|
||||
)
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
22
database/logging/UserRoleLogging.view.ts
Normal file
22
database/logging/UserRoleLogging.view.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { UserRole } from '../entity/UserRole'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { UserLoggingView } from './UserLogging.view'
|
||||
|
||||
export class UserRoleLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: UserRole) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
user: this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
role: this.self.role,
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
updatedAt: this.dateToString(this.self.updatedAt),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(
|
||||
`UPDATE \`transactions\` AS t
|
||||
JOIN \`contributions\` AS c ON t.id = c.transaction_id
|
||||
SET t.linked_user_id = c.confirmed_by
|
||||
WHERE t.type_id = ?`,
|
||||
[1],
|
||||
)
|
||||
}
|
||||
|
||||
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(`UPDATE \`transactions\` SET \`linked_user_id\` = NULL where \`type_id\` = ?;`, [1])
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(
|
||||
`UPDATE \`transactions\` AS t
|
||||
JOIN \`contributions\` AS c ON t.id = c.transaction_id
|
||||
JOIN \`users\` AS u ON u.id = c.confirmed_by
|
||||
SET
|
||||
t.linked_user_gradido_id = u.gradido_id,
|
||||
t.linked_user_name = CONCAT(u.first_name, ' ', u.last_name),
|
||||
t.linked_user_community_uuid = u.community_uuid
|
||||
WHERE t.type_id = ?`,
|
||||
[1],
|
||||
)
|
||||
|
||||
// fill user community uuid fields in transactions
|
||||
await queryFn(
|
||||
`UPDATE \`transactions\` AS t
|
||||
JOIN \`users\` AS u ON u.id = t.user_id
|
||||
JOIN \`users\` AS lu ON lu.id = t.linked_user_id
|
||||
SET
|
||||
t.user_community_uuid = u.community_uuid,
|
||||
t.linked_user_community_uuid = lu.community_uuid`,
|
||||
)
|
||||
}
|
||||
|
||||
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(
|
||||
`UPDATE \`transactions\` SET \`linked_user_gradido_id\` = NULL, \`linked_user_name\` = NULL where \`type_id\` = ?;`,
|
||||
[1],
|
||||
)
|
||||
|
||||
await queryFn(
|
||||
`UPDATE \`transactions\` SET \`user_community_uuid\` = NULL, \`linked_user_community_uuid\` = NULL;`,
|
||||
)
|
||||
}
|
||||
11
database/migrations/0081-user_join_community.ts
Normal file
11
database/migrations/0081-user_join_community.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(
|
||||
'ALTER TABLE users MODIFY community_uuid VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;',
|
||||
)
|
||||
}
|
||||
|
||||
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(
|
||||
'ALTER TABLE users MODIFY community_uuid VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;',
|
||||
)
|
||||
}
|
||||
@ -1,87 +1,102 @@
|
||||
GRADIDO_LOG_PATH=/home/gradido/gradido/deployment/bare_metal/log
|
||||
# Need to adjust!
|
||||
COMMUNITY_NAME="Your community name"
|
||||
COMMUNITY_DESCRIPTION="Short Description from your Community."
|
||||
COMMUNITY_HOST=gddhost.tld
|
||||
COMMUNITY_SUPPORT_MAIL=support@supportmail.com
|
||||
|
||||
# setup email account for sending gradido system messages to users
|
||||
EMAIL=true
|
||||
EMAIL_USERNAME=peter@lustig.de
|
||||
EMAIL_SENDER=peter@lustig.de
|
||||
EMAIL_PASSWORD=1234
|
||||
EMAIL_SMTP_URL=smtp.lustig.de
|
||||
EMAIL_SMTP_PORT=587
|
||||
|
||||
# if set to true allow sending gradidos to another communities
|
||||
FEDERATION_XCOM_SENDCOINS_ENABLED=false
|
||||
|
||||
# how many minutes email verification code is valid
|
||||
# also used for password reset code
|
||||
EMAIL_CODE_VALID_TIME=1440
|
||||
# how many minutes user must wait before he can request the email verification code again
|
||||
# also used for password reset code
|
||||
EMAIL_CODE_REQUEST_TIME=10
|
||||
|
||||
# Need to adjust by updates
|
||||
# config versions
|
||||
DATABASE_CONFIG_VERSION=v1.2022-03-18
|
||||
BACKEND_CONFIG_VERSION=v21.2024-01-06
|
||||
FRONTEND_CONFIG_VERSION=v5.2024-01-08
|
||||
ADMIN_CONFIG_VERSION=v2.2024-01-04
|
||||
FEDERATION_CONFIG_VERSION=v2.2023-08-24
|
||||
FEDERATION_DHT_CONFIG_VERSION=v4.2024-01-17
|
||||
|
||||
FEDERATION_DHT_TOPIC=GRADIDO_HUB
|
||||
|
||||
# Need adjustments for test system
|
||||
URL_PROTOCOL=https
|
||||
# start script
|
||||
# only for test server
|
||||
DEPLOY_SEED_DATA=false
|
||||
# test email
|
||||
# if true all email will be send to EMAIL_TEST_RECEIVER instead of email address of user
|
||||
EMAIL_TEST_MODUS=false
|
||||
EMAIL_TEST_RECEIVER=test_team@gradido.net
|
||||
|
||||
# nginx
|
||||
NGINX_REWRITE_LEGACY_URLS=true
|
||||
NGINX_SSL=true
|
||||
NGINX_SERVER_NAME=stage1.gradido.net
|
||||
NGINX_SSL_CERTIFICATE=/etc/letsencrypt/live/stage1.gradido.net/fullchain.pem
|
||||
NGINX_SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/stage1.gradido.net/privkey.pem
|
||||
NGINX_SSL_DHPARAM=/etc/letsencrypt/ssl-dhparams.pem
|
||||
NGINX_SSL_INCLUDE=/etc/letsencrypt/options-ssl-nginx.conf
|
||||
NGINX_UPDATE_PAGE_ROOT=/home/gradido/gradido/deployment/bare_metal/nginx/update-page
|
||||
# Logging
|
||||
LOG_LEVEL=INFO
|
||||
GRADIDO_LOG_PATH=/home/gradido/gradido/deployment/bare_metal/log
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=../deployment/bare_metal/log/typeorm.backend.log
|
||||
|
||||
# webhook
|
||||
WEBHOOK_GITHUB_SECRET=secret
|
||||
WEBHOOK_GITHUB_BRANCH=master
|
||||
|
||||
# community
|
||||
COMMUNITY_NAME="Gradido Development Stage1"
|
||||
COMMUNITY_URL=https://stage1.gradido.net/
|
||||
COMMUNITY_REGISTER_URL=https://stage1.gradido.net/register
|
||||
COMMUNITY_REDEEM_URL=https://stage1.gradido.net/redeem/{code}
|
||||
COMMUNITY_REDEEM_CONTRIBUTION_URL=https://stage1.gradido.net/redeem/CL-{code}
|
||||
COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community"
|
||||
COMMUNITY_SUPPORT_MAIL=support@supportmail.com
|
||||
|
||||
# backend
|
||||
BACKEND_CONFIG_VERSION=v17.2023-07-03
|
||||
# frontend and admin paths, usually don't need changes
|
||||
# used in nginx config and for links in emails
|
||||
COMMUNITY_REGISTER_PATH=/register
|
||||
COMMUNITY_REDEEM_PATH=/redeem/{code}
|
||||
COMMUNITY_REDEEM_CONTRIBUTION_PATH=/redeem/CL-{code}
|
||||
WALLET_LOGIN_PATH=/login
|
||||
WALLET_AUTH_PATH=/authenticate?token={token}
|
||||
EMAIL_LINK_VERIFICATION_PATH=/checkEmail/{optin}{code}
|
||||
EMAIL_LINK_SETPASSWORD_PATH=/reset-password/{optin}
|
||||
EMAIL_LINK_FORGOTPASSWORD_PATH=/forgot-password
|
||||
EMAIL_LINK_OVERVIEW_PATH=/overview
|
||||
ADMIN_AUTH_PATH=/admin/authenticate?token={token}
|
||||
GRAPHQL_PATH=/graphql
|
||||
|
||||
# login expire time
|
||||
JWT_EXPIRES_IN=10m
|
||||
|
||||
# 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
|
||||
# the api port is the baseport, which will be added with the api-version, e.g. 1_0 = 5010
|
||||
FEDERATION_COMMUNITY_API_PORT=5000
|
||||
FEDERATION_VALIDATE_COMMUNITY_TIMER=60000
|
||||
|
||||
# comma separated list of api-versions, which cause starting several federation modules
|
||||
FEDERATION_COMMUNITY_APIS=1_0
|
||||
|
||||
# externe gradido services (more added in future)
|
||||
GDT_API_URL=https://gdt.gradido.net
|
||||
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=../deployment/bare_metal/log/typeorm.backend.log
|
||||
# DLT-Connector (still in develop)
|
||||
DLT_CONNECTOR=false
|
||||
DLT_CONNECTOR_PORT=6010
|
||||
|
||||
# used for combining a newsletter on klicktipp with this gradido community
|
||||
# if used, user will be subscribed on register and can unsubscribe in his account
|
||||
KLICKTIPP=false
|
||||
KLICKTIPP_USER=
|
||||
KLICKTIPP_PASSWORD=
|
||||
KLICKTIPP_APIKEY_DE=
|
||||
KLICKTIPP_APIKEY_EN=
|
||||
|
||||
EMAIL=true
|
||||
EMAIL_TEST_MODUS=false
|
||||
EMAIL_TEST_RECEIVER=test_team@gradido.net
|
||||
EMAIL_USERNAME=peter@lustig.de
|
||||
EMAIL_SENDER=peter@lustig.de
|
||||
EMAIL_PASSWORD=1234
|
||||
EMAIL_SMTP_URL=smtp.lustig.de
|
||||
EMAIL_LINK_VERIFICATION=https://stage1.gradido.net/checkEmail/{optin}{code}
|
||||
EMAIL_LINK_SETPASSWORD=https://stage1.gradido.net/reset-password/{optin}
|
||||
EMAIL_LINK_FORGOTPASSWORD=https://stage1.gradido.net/forgot-password
|
||||
EMAIL_LINK_OVERVIEW=https://stage1.gradido.net/overview
|
||||
EMAIL_CODE_VALID_TIME=1440
|
||||
EMAIL_CODE_REQUEST_TIME=10
|
||||
|
||||
WEBHOOK_ELOPAGE_SECRET=secret
|
||||
|
||||
# Federation
|
||||
FEDERATION_DHT_CONFIG_VERSION=v3.2023-04-26
|
||||
# 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
|
||||
FEDERATION_COMMUNITY_URL=http://stage1.gradido.net
|
||||
# the api port is the baseport, which will be added with the api-version, e.g. 1_0 = 5010
|
||||
FEDERATION_COMMUNITY_API_PORT=5000
|
||||
|
||||
FEDERATION_CONFIG_VERSION=v1.2023-01-09
|
||||
# comma separated list of api-versions, which cause starting several federation modules
|
||||
FEDERATION_COMMUNITY_APIS=1_0,1_1
|
||||
|
||||
# database
|
||||
DATABASE_CONFIG_VERSION=v1.2022-03-18
|
||||
|
||||
# frontend
|
||||
FRONTEND_CONFIG_VERSION=v4.2022-12-20
|
||||
|
||||
GRAPHQL_URI=https://stage1.gradido.net/graphql
|
||||
ADMIN_AUTH_URL=https://stage1.gradido.net/admin/authenticate?token={token}
|
||||
|
||||
DEFAULT_PUBLISHER_ID=2896
|
||||
|
||||
META_URL=http://localhost
|
||||
# Meta data in frontend pages, important when shared via facebook or twitter or for search engines
|
||||
META_TITLE_DE="Gradido – Dein Dankbarkeitskonto"
|
||||
META_TITLE_EN="Gradido - Your gratitude account"
|
||||
META_DESCRIPTION_DE="Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle."
|
||||
@ -90,8 +105,20 @@ META_KEYWORDS_DE="Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natü
|
||||
META_KEYWORDS_EN="Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System"
|
||||
META_AUTHOR="Bernd Hückstädt - Gradido-Akademie"
|
||||
|
||||
# admin
|
||||
ADMIN_CONFIG_VERSION=v1.2022-03-18
|
||||
# update page shown while updating gradido
|
||||
# page will be fed with status changes
|
||||
NGINX_UPDATE_PAGE_ROOT=/home/gradido/gradido/deployment/bare_metal/nginx/update-page
|
||||
# NGINX SSL Setup with certbot
|
||||
# will be generated by start.sh with $COMMUNITY_HOST, only need to setup manual if setup differ from default
|
||||
#NGINX_SSL_CERTIFICATE=/etc/letsencrypt/live/gddhost.tld/fullchain.pem
|
||||
#NGINX_SSL_CERTIFICATE_KEY=/etc/letsencrypt/live/gddhost.tld/privkey.pem
|
||||
NGINX_SSL_DHPARAM=/etc/letsencrypt/ssl-dhparams.pem
|
||||
NGINX_SSL_INCLUDE=/etc/letsencrypt/options-ssl-nginx.conf
|
||||
|
||||
# LEGACY
|
||||
NGINX_REWRITE_LEGACY_URLS=false
|
||||
DEFAULT_PUBLISHER_ID=2896
|
||||
WEBHOOK_ELOPAGE_SECRET=secret
|
||||
|
||||
WALLET_AUTH_URL=https://stage1.gradido.net/authenticate?token={token}
|
||||
WALLET_URL=https://stage1.gradido.net/login
|
||||
|
||||
118
deployment/bare_metal/doc/server.drawio
Normal file
118
deployment/bare_metal/doc/server.drawio
Normal file
@ -0,0 +1,118 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="q0c1bfTOSmR5BH1DDDeU" name="Page-1">
|
||||
<mxGraphModel dx="874" dy="1662" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="Physical Server" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="15" y="40" width="410" height="420" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="mariadb<br>port: 3306" style="shape=datastore;whiteSpace=wrap;html=1;" vertex="1" parent="2">
|
||||
<mxGeometry x="165" y="350" width="60" height="60" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="4" value="nginx" style="swimlane;whiteSpace=wrap;html=1;startSize=23;" vertex="1" parent="2">
|
||||
<mxGeometry x="60" y="20" width="230" height="110" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="5" value="port 80: redirect to port 443" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="4">
|
||||
<mxGeometry x="-5" y="30" width="170" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="port 443: using ssl encryption" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="4">
|
||||
<mxGeometry x="-5" y="60" width="180" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="28" value="" style="endArrow=classic;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="4" target="15">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="45" y="110" as="sourcePoint"/>
|
||||
<mxPoint x="145" y="90" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="29" value="/<span style="color: rgb(36, 41, 46); font-family: &quot;Droid Sans Mono&quot;, &quot;monospace&quot;, monospace; font-size: 14px;">graphql</span>" style="edgeLabel;resizable=0;html=1;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="28">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="41" style="edgeStyle=none;html=1;" edge="1" parent="2" source="15">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="185" y="350" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="15" value="Backend<br>runs on port: 4000" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" vertex="1" parent="2">
|
||||
<mxGeometry x="10" y="170" width="120" height="60" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="17" value="frontend<br>static files server<br>port: 3000" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" vertex="1" parent="2">
|
||||
<mxGeometry x="125" y="210" width="120" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="18" value="admin<br>static files server<br>port: 8080" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" vertex="1" parent="2">
|
||||
<mxGeometry x="290" y="190" width="120" height="60" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="40" style="edgeStyle=none;html=1;" edge="1" parent="2" source="19">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="165" y="370" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="19" value="dht-node<br>use his own system" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" vertex="1" parent="2">
|
||||
<mxGeometry x="10" y="320" width="120" height="60" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="39" style="edgeStyle=none;html=1;entryX=0.85;entryY=0.05;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="2" source="21" target="3">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="21" value="Federation<br>on port per version" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" vertex="1" parent="2">
|
||||
<mxGeometry x="215" y="260" width="120" height="60" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="30" value="/<br>" style="endArrow=classic;html=1;" edge="1" parent="2" target="17">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="155" y="130" as="sourcePoint"/>
|
||||
<mxPoint x="305" y="150" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="31" value="/" style="edgeLabel;resizable=0;html=1;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="30">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="9" value="" style="endArrow=classic;html=1;" edge="1" parent="1" source="13" target="2">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="210" y="20" as="sourcePoint"/>
|
||||
<mxPoint x="500" y="290" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="10" value="Extern Request<br>- webbrowser with frontend running<br>- webbrowser with admin running<br>- backend<br>- federation" style="edgeLabel;resizable=0;html=1;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="9">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="11" value="Source" style="edgeLabel;resizable=0;html=1;align=left;verticalAlign=bottom;" connectable="0" vertex="1" parent="9">
|
||||
<mxGeometry x="-1" relative="1" as="geometry">
|
||||
<mxPoint y="20" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="12" value="Target" style="edgeLabel;resizable=0;html=1;align=right;verticalAlign=bottom;" connectable="0" vertex="1" parent="9">
|
||||
<mxGeometry x="1" relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="13" value="Internet" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="100" y="-180" width="120" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="" style="endArrow=classic;html=1;" edge="1" parent="1" target="18">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="270" y="170" as="sourcePoint"/>
|
||||
<mxPoint x="470" y="50" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="300" y="200"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="/admin" style="edgeLabel;resizable=0;html=1;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="33">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="35" value="" style="endArrow=classic;html=1;entryX=0.633;entryY=-0.017;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" target="21">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="220" y="170" as="sourcePoint"/>
|
||||
<mxPoint x="470" y="170" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="/api/VERSION" style="edgeLabel;resizable=0;html=1;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="35">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="43" value="Legende:" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingLeft=4;spacingRight=4;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fontStyle=1;fontSize=14;" vertex="1" parent="1">
|
||||
<mxGeometry x="550" y="130" width="80" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="44" value="<i style="">Node JS&nbsp;<br>Express Server</i>" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="540" y="170" width="120" height="60" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
4
deployment/bare_metal/nginx/common/limit_requests.conf
Normal file
4
deployment/bare_metal/nginx/common/limit_requests.conf
Normal file
@ -0,0 +1,4 @@
|
||||
limit_req_zone $binary_remote_addr zone=frontend:20m rate=5r/s;
|
||||
limit_req_zone $binary_remote_addr zone=backend:25m rate=15r/s;
|
||||
limit_req_zone $binary_remote_addr zone=api:5m rate=30r/s;
|
||||
limit_conn_zone $binary_remote_addr zone=addr:10m;
|
||||
@ -1,5 +1,8 @@
|
||||
|
||||
location /api/$FEDERATION_APIVERSION {
|
||||
limit_req zone=api burst=60 nodelay;
|
||||
limit_conn addr 30;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
include /etc/nginx/common/limit_requests.conf;
|
||||
|
||||
server {
|
||||
if ($host = $NGINX_SERVER_NAME) {
|
||||
if ($host = $COMMUNITY_HOST) {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server_name $NGINX_SERVER_NAME;
|
||||
server_name $COMMUNITY_HOST;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
return 404;
|
||||
}
|
||||
|
||||
server {
|
||||
server_name $NGINX_SERVER_NAME;
|
||||
server_name $COMMUNITY_HOST;
|
||||
|
||||
listen [::]:443 ssl ipv6only=on;
|
||||
listen 443 ssl;
|
||||
@ -22,6 +24,15 @@ server {
|
||||
include /etc/nginx/common/protect.conf;
|
||||
include /etc/nginx/common/protect_add_header.conf;
|
||||
|
||||
# protect from slow loris
|
||||
client_body_timeout 10s;
|
||||
client_header_timeout 10s;
|
||||
|
||||
# protect from range attack (in http header)
|
||||
if ($http_range ~ "d{9,}") {
|
||||
return 444;
|
||||
}
|
||||
|
||||
#gzip_static on;
|
||||
gzip on;
|
||||
gzip_proxied any;
|
||||
@ -42,6 +53,8 @@ server {
|
||||
|
||||
# Frontend (default)
|
||||
location / {
|
||||
limit_req zone=frontend burst=40 nodelay;
|
||||
limit_conn addr 40;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
@ -58,6 +71,8 @@ server {
|
||||
|
||||
# Backend
|
||||
location /graphql {
|
||||
limit_req zone=backend burst=10 nodelay;
|
||||
limit_conn addr 10;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
@ -74,6 +89,8 @@ server {
|
||||
|
||||
# Backend webhooks
|
||||
location /hook {
|
||||
limit_req zone=backend burst=10;
|
||||
limit_conn addr 10;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
@ -90,6 +107,8 @@ server {
|
||||
|
||||
# Webhook reverse proxy
|
||||
location /hooks/ {
|
||||
limit_req zone=backend burst=10;
|
||||
limit_conn addr 10;
|
||||
proxy_pass http://127.0.0.1:9000/hooks/;
|
||||
|
||||
access_log $GRADIDO_LOG_PATH/nginx-access.hooks.log gradido_log;
|
||||
@ -98,6 +117,8 @@ server {
|
||||
|
||||
# Admin Frontend
|
||||
location /admin {
|
||||
limit_req zone=frontend burst=30 nodelay;
|
||||
limit_conn addr 40;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
include /etc/nginx/common/limit_requests.conf;
|
||||
|
||||
server {
|
||||
server_name $NGINX_SERVER_NAME;
|
||||
server_name $COMMUNITY_HOST;
|
||||
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
@ -7,6 +9,15 @@ server {
|
||||
include /etc/nginx/common/protect.conf;
|
||||
include /etc/nginx/common/protect_add_header.conf;
|
||||
|
||||
# protect from slow loris
|
||||
client_body_timeout 10s;
|
||||
client_header_timeout 10s;
|
||||
|
||||
# protect from range attack (in http header)
|
||||
if ($http_range ~ "d{9,}") {
|
||||
return 444;
|
||||
}
|
||||
|
||||
#gzip_static on;
|
||||
gzip on;
|
||||
gzip_proxied any;
|
||||
@ -27,6 +38,8 @@ server {
|
||||
|
||||
# Frontend (default)
|
||||
location / {
|
||||
limit_req zone=frontend burst=40 nodelay;
|
||||
limit_conn addr 40;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
@ -43,6 +56,8 @@ server {
|
||||
|
||||
# Backend
|
||||
location /graphql {
|
||||
limit_req zone=backend burst=10 nodelay;
|
||||
limit_conn addr 10;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
@ -59,6 +74,8 @@ server {
|
||||
|
||||
# Backend webhooks
|
||||
location /hook {
|
||||
limit_req zone=backend burst=10;
|
||||
limit_conn addr 10;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
@ -66,7 +83,6 @@ server {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
# no trailing slash to keep the hook/ prefix
|
||||
proxy_pass http://127.0.0.1:4000/hook;
|
||||
proxy_redirect off;
|
||||
|
||||
@ -76,6 +92,8 @@ server {
|
||||
|
||||
# Webhook reverse proxy
|
||||
location /hooks/ {
|
||||
limit_req zone=backend burst=10;
|
||||
limit_conn addr 10;
|
||||
proxy_pass http://127.0.0.1:9000/hooks/;
|
||||
|
||||
access_log $GRADIDO_LOG_PATH/nginx-access.hooks.log gradido_log;
|
||||
@ -84,6 +102,8 @@ server {
|
||||
|
||||
# Admin Frontend
|
||||
location /admin {
|
||||
limit_req zone=frontend burst=30 nodelay;
|
||||
limit_conn addr 40;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
@ -97,7 +117,7 @@ server {
|
||||
access_log $GRADIDO_LOG_PATH/nginx-access.admin.log gradido_log;
|
||||
error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn;
|
||||
}
|
||||
|
||||
|
||||
# Federation
|
||||
$FEDERATION_NGINX_CONF
|
||||
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
include /etc/nginx/common/limit_requests.conf;
|
||||
|
||||
server {
|
||||
if ($host = $NGINX_SERVER_NAME) {
|
||||
if ($host = $COMMUNITY_HOST) {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server_name $NGINX_SERVER_NAME;
|
||||
server_name $COMMUNITY_HOST;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
return 404;
|
||||
}
|
||||
server {
|
||||
server_name $NGINX_SERVER_NAME;
|
||||
server_name $COMMUNITY_HOST;
|
||||
|
||||
listen [::]:443 ssl ipv6only=on;
|
||||
listen 443 ssl;
|
||||
@ -22,12 +23,23 @@ server {
|
||||
include /etc/nginx/common/protect.conf;
|
||||
include /etc/nginx/common/protect_add_header.conf;
|
||||
|
||||
# protect from slow loris
|
||||
client_body_timeout 10s;
|
||||
client_header_timeout 10s;
|
||||
|
||||
# protect from range attack (in http header)
|
||||
if ($http_range ~ "d{9,}") {
|
||||
return 444;
|
||||
}
|
||||
|
||||
gzip on;
|
||||
|
||||
root $NGINX_UPDATE_PAGE_ROOT;
|
||||
index updating.html;
|
||||
|
||||
location / {
|
||||
limit_req zone=frontend;
|
||||
limit_conn addr 10;
|
||||
try_files /updating.html =404;
|
||||
}
|
||||
|
||||
|
||||
@ -1,18 +1,30 @@
|
||||
include /etc/nginx/common/limit_requests.conf;
|
||||
|
||||
server {
|
||||
server_name _;
|
||||
server_name $COMMUNITY_HOST;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
include /etc/nginx/common/protect.conf;
|
||||
include /etc/nginx/common/protect_add_header.conf;
|
||||
|
||||
# protect from slow loris
|
||||
client_body_timeout 10s;
|
||||
client_header_timeout 10s;
|
||||
|
||||
# protect from range attack (in http header)
|
||||
if ($http_range ~ "d{9,}") {
|
||||
return 444;
|
||||
}
|
||||
|
||||
gzip on;
|
||||
|
||||
root $NGINX_UPDATE_PAGE_ROOT;
|
||||
index updating.html;
|
||||
|
||||
location / {
|
||||
limit_req zone=frontend;
|
||||
limit_conn addr 10;
|
||||
try_files /updating.html =404;
|
||||
}
|
||||
|
||||
|
||||
@ -10,12 +10,17 @@ PROJECT_ROOT=$SCRIPT_DIR/../..
|
||||
NGINX_CONFIG_DIR=$SCRIPT_DIR/nginx/sites-available
|
||||
set +o allexport
|
||||
|
||||
# enable nvm
|
||||
export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
|
||||
# NOTE: all config values will be in process.env when starting
|
||||
# the services and will therefore take precedence over the .env
|
||||
|
||||
# We have to load the backend .env to get DB_USERNAME, DB_PASSWORD AND JWT_SECRET
|
||||
# and the dht-node .env to get FEDERATION_DHT_SEED
|
||||
export_var(){
|
||||
export $1=$(grep -v '^#' $PROJECT_ROOT/backend/.env | grep -e "$1" | sed -e 's/.*=//')
|
||||
export $1=$(grep -v '^#' $PROJECT_ROOT/dht-node/.env | grep -e "$1" | sed -e 's/.*=//')
|
||||
}
|
||||
|
||||
if [ -f "$PROJECT_ROOT/backend/.env" ]; then
|
||||
@ -24,6 +29,10 @@ if [ -f "$PROJECT_ROOT/backend/.env" ]; then
|
||||
export_var 'JWT_SECRET'
|
||||
fi
|
||||
|
||||
if [ -f "$PROJECT_ROOT/dht-node/.env" ]; then
|
||||
export_var 'FEDERATION_DHT_SEED'
|
||||
fi
|
||||
|
||||
# Load .env or .env.dist if not present
|
||||
if [ -f "$SCRIPT_DIR/.env" ]; then
|
||||
set -o allexport
|
||||
@ -35,6 +44,14 @@ else
|
||||
set +o allexport
|
||||
fi
|
||||
|
||||
# set env variables dynamic if not already set in .env or .env.dist
|
||||
: ${NGINX_SSL_CERTIFICATE:=/etc/letsencrypt/live/$COMMUNITY_HOST/fullchain.pem}
|
||||
: ${NGINX_SSL_CERTIFICATE_KEY:=/etc/letsencrypt/live/$COMMUNITY_HOST/privkey.pem}
|
||||
|
||||
# export env variables
|
||||
export NGINX_SSL_CERTIFICATE
|
||||
export NGINX_SSL_CERTIFICATE_KEY
|
||||
|
||||
# lock start
|
||||
if [ -f $LOCK_FILE ] ; then
|
||||
echo "Already building!"
|
||||
@ -54,8 +71,7 @@ exec > >(tee -a $UPDATE_HTML) 2>&1
|
||||
|
||||
# configure nginx for the update-page
|
||||
echo 'Configuring nginx to serve the update-page' >> $UPDATE_HTML
|
||||
rm /etc/nginx/sites-enabled/gradido.conf
|
||||
ln -s /etc/nginx/sites-available/update-page.conf /etc/nginx/sites-enabled/
|
||||
ln -sf $SCRIPT_DIR/nginx/sites-available/update-page.conf $SCRIPT_DIR/nginx/sites-enabled/default
|
||||
sudo /etc/init.d/nginx restart
|
||||
|
||||
# stop all services
|
||||
@ -100,9 +116,9 @@ export FEDERATION_NGINX_CONF=$(< $NGINX_CONFIG_DIR/gradido-federation.conf.locat
|
||||
|
||||
# *** 3rd generate gradido nginx config including federation modules per api-version
|
||||
echo 'Generate new gradido nginx config' >> $UPDATE_HTML
|
||||
case "$NGINX_SSL" in
|
||||
true) TEMPLATE_FILE="gradido.conf.ssl.template" ;;
|
||||
*) TEMPLATE_FILE="gradido.conf.template" ;;
|
||||
case "$URL_PROTOCOL" in
|
||||
'https') TEMPLATE_FILE="gradido.conf.ssl.template" ;;
|
||||
*) TEMPLATE_FILE="gradido.conf.template" ;;
|
||||
esac
|
||||
envsubst '$FEDERATION_NGINX_CONF' < $NGINX_CONFIG_DIR/$TEMPLATE_FILE > $NGINX_CONFIG_DIR/gradido.conf.tmp
|
||||
unset FEDERATION_NGINX_CONF
|
||||
@ -112,9 +128,9 @@ rm $NGINX_CONFIG_DIR/gradido-federation.conf.locations
|
||||
|
||||
# Generate update-page.conf from template
|
||||
echo 'Generate new update-page nginx config' >> $UPDATE_HTML
|
||||
case "$NGINX_SSL" in
|
||||
true) TEMPLATE_FILE="update-page.conf.ssl.template" ;;
|
||||
*) TEMPLATE_FILE="update-page.conf.template" ;;
|
||||
case "$URL_PROTOCOL" in
|
||||
'https') TEMPLATE_FILE="update-page.conf.ssl.template" ;;
|
||||
*) TEMPLATE_FILE="update-page.conf.template" ;;
|
||||
esac
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $NGINX_CONFIG_DIR/$TEMPLATE_FILE > $NGINX_CONFIG_DIR/update-page.conf
|
||||
|
||||
@ -177,8 +193,7 @@ if [ "$DEPLOY_SEED_DATA" = "true" ]; then
|
||||
fi
|
||||
# TODO maybe handle this differently?
|
||||
export NODE_ENV=production
|
||||
pm2 start --name gradido-backend "yarn --cwd $PROJECT_ROOT/backend start" -l $GRADIDO_LOG_PATH/pm2.backend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
pm2 save
|
||||
|
||||
|
||||
# Install & build frontend
|
||||
echo 'Updating frontend' >> $UPDATE_HTML
|
||||
@ -189,8 +204,6 @@ yarn install
|
||||
yarn build
|
||||
# TODO maybe handle this differently?
|
||||
export NODE_ENV=production
|
||||
pm2 start --name gradido-frontend "yarn --cwd $PROJECT_ROOT/frontend start" -l $GRADIDO_LOG_PATH/pm2.frontend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
pm2 save
|
||||
|
||||
# Install & build admin
|
||||
echo 'Updating admin' >> $UPDATE_HTML
|
||||
@ -201,8 +214,6 @@ yarn install
|
||||
yarn build
|
||||
# TODO maybe handle this differently?
|
||||
export NODE_ENV=production
|
||||
pm2 start --name gradido-admin "yarn --cwd $PROJECT_ROOT/admin start" -l $GRADIDO_LOG_PATH/pm2.admin.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
pm2 save
|
||||
|
||||
# Install & build dht-node
|
||||
echo 'Updating dht-node' >> $UPDATE_HTML
|
||||
@ -213,15 +224,6 @@ yarn install
|
||||
yarn build
|
||||
# TODO maybe handle this differently?
|
||||
export NODE_ENV=production
|
||||
if [ ! -z $FEDERATION_DHT_TOPIC ]; then
|
||||
pm2 start --name gradido-dht-node "yarn --cwd $PROJECT_ROOT/dht-node start" -l $GRADIDO_LOG_PATH/pm2.dht-node.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
pm2 save
|
||||
else
|
||||
echo "=====================================================================" >> $UPDATE_HTML
|
||||
echo "WARNING: FEDERATION_DHT_TOPIC not configured. DHT-Node not started..." >> $UPDATE_HTML
|
||||
echo "=====================================================================" >> $UPDATE_HTML
|
||||
fi
|
||||
|
||||
|
||||
# Install & build federation
|
||||
echo 'Updating federation' >> $UPDATE_HTML
|
||||
@ -233,6 +235,20 @@ yarn build
|
||||
# TODO maybe handle this differently?
|
||||
export NODE_ENV=production
|
||||
|
||||
# start after building all to use up less ressources
|
||||
pm2 start --name gradido-backend "yarn --cwd $PROJECT_ROOT/backend start" -l $GRADIDO_LOG_PATH/pm2.backend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
pm2 start --name gradido-frontend "yarn --cwd $PROJECT_ROOT/frontend start" -l $GRADIDO_LOG_PATH/pm2.frontend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
pm2 start --name gradido-admin "yarn --cwd $PROJECT_ROOT/admin start" -l $GRADIDO_LOG_PATH/pm2.admin.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
pm2 save
|
||||
if [ ! -z $FEDERATION_DHT_TOPIC ]; then
|
||||
pm2 start --name gradido-dht-node "yarn --cwd $PROJECT_ROOT/dht-node start" -l $GRADIDO_LOG_PATH/pm2.dht-node.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
pm2 save
|
||||
else
|
||||
echo "=====================================================================" >> $UPDATE_HTML
|
||||
echo "WARNING: FEDERATION_DHT_TOPIC not configured. DHT-Node not started..." >> $UPDATE_HTML
|
||||
echo "=====================================================================" >> $UPDATE_HTML
|
||||
fi
|
||||
|
||||
# set FEDERATION_PORT from FEDERATION_COMMUNITY_APIS
|
||||
IFS="," read -a API_ARRAY <<< $FEDERATION_COMMUNITY_APIS
|
||||
for api in "${API_ARRAY[@]}"
|
||||
@ -254,13 +270,9 @@ do
|
||||
pm2 save
|
||||
done
|
||||
|
||||
|
||||
|
||||
|
||||
# let nginx showing gradido
|
||||
echo 'Configuring nginx to serve gradido again' >> $UPDATE_HTML
|
||||
ln -s /etc/nginx/sites-available/gradido.conf /etc/nginx/sites-enabled/
|
||||
rm /etc/nginx/sites-enabled/update-page.conf
|
||||
ln -sf $SCRIPT_DIR/nginx/sites-available/gradido.conf $SCRIPT_DIR/nginx/sites-enabled/default
|
||||
sudo /etc/init.d/nginx restart
|
||||
|
||||
# keep the update log
|
||||
|
||||
124
deployment/hetzner_cloud/README.md
Normal file
124
deployment/hetzner_cloud/README.md
Normal file
@ -0,0 +1,124 @@
|
||||
# Setup on Hetzner Cloud Server
|
||||
Suggested minimal Plan: CX41
|
||||
4x vCPU, 16 GB Ram, 160 GB Disk Space, 20.71 € per month (04.01.2024)
|
||||
|
||||
Suggested OS:
|
||||
Debian 12
|
||||
|
||||
For Hetzner Cloud Server a cloud config can be attached, which will be run before first start
|
||||
https://community.hetzner.com/tutorials/basic-cloud-config/de
|
||||
https://cloudinit.readthedocs.io/en/latest/reference/examples.html
|
||||
You can use our [cloudConfig.yaml](./cloudConfig.yaml) but you must insert you own ssh public key,
|
||||
like this:
|
||||
```yaml
|
||||
ssh_authorized_keys:
|
||||
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAkLGbzbG7KIGfkssKJBkc/0EVAzQ/8vjvVHzNdxhK8J yourname
|
||||
```
|
||||
|
||||
## After Setup Cloud Server with cloudConfig.yaml
|
||||
### setup your domain pointing on server ip address
|
||||
### login to your new server as root
|
||||
```bash
|
||||
ssh -i /path/to/privKey root@gddhost.tld
|
||||
```
|
||||
|
||||
### Change default shell
|
||||
|
||||
```bash
|
||||
chsh -s /bin/bash
|
||||
chsh -s /bin/bash gradido
|
||||
```
|
||||
|
||||
### Set password for user `gradido`
|
||||
|
||||
```bash
|
||||
$ passwd gradido
|
||||
# enter new password twice
|
||||
```
|
||||
|
||||
### Switch to the new user
|
||||
|
||||
```bash
|
||||
su gradido
|
||||
```
|
||||
|
||||
### Test authentication via SSH
|
||||
|
||||
If you logout from the server you can test authentication:
|
||||
|
||||
```bash
|
||||
$ ssh -i /path/to/privKey gradido@gddhost.tld
|
||||
# This should log you in and allow you to use sudo commands, which will require the user's password
|
||||
```
|
||||
|
||||
### Disable password root login via ssh
|
||||
|
||||
```bash
|
||||
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.org
|
||||
sudo sed -i -e '/^\(#\|\)PermitRootLogin/s/^.*$/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||
sudo sed -i '$a AllowUsers gradido' /etc/ssh/sshd_config
|
||||
sudo /etc/init.d/ssh restart
|
||||
```
|
||||
|
||||
### Test SSH Access only, no root ssh access
|
||||
|
||||
```bash
|
||||
$ ssh gradido@gddhost.tld
|
||||
# Will result in in either a passphrase request for your key or the message 'Permission denied (publickey)'
|
||||
$ ssh -i /path/to/privKey root@gddhost.tld
|
||||
# Will result in 'Permission denied (publickey)'
|
||||
$ ssh -i /path/to/privKey gradido@gddhost.tld
|
||||
# Will succeed after entering the correct keys passphrase (if any)
|
||||
```
|
||||
|
||||
### Install `Gradido` code
|
||||
```bash
|
||||
cd ~
|
||||
git clone https://github.com/gradido/gradido.git
|
||||
```
|
||||
|
||||
### Adjust the values in `.env`
|
||||
|
||||
***!!! Attention !!!***
|
||||
|
||||
*Don't forget this step!
|
||||
All your following installations in `install.sh` will fail!*
|
||||
|
||||
*Notes:*
|
||||
|
||||
- *`;` cannot be part of any value!*
|
||||
- *The GitHub secret is created on GitHub in Settings -> Webhooks.*
|
||||
|
||||
#### Create `.env` and set values
|
||||
|
||||
```bash
|
||||
cd ~/gradido/deployment/bare_metal
|
||||
cp .env.dist .env
|
||||
nano .env
|
||||
|
||||
# adjust values accordingly
|
||||
```
|
||||
|
||||
### Run `install.sh`
|
||||
***!!! Attention !!!***
|
||||
Don't use this script if you have custom config in /etc/nginx/conf.d, because this script
|
||||
will remove it and ln ../bare_metal/nginx/conf.d
|
||||
|
||||
```bash
|
||||
cd ~/gradido/deployment/hetzner_cloud
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
### Make yourself admin
|
||||
- Create an account on your new gradido instance
|
||||
- Click the link in the activation email
|
||||
- go back to your ssh session and copy this command
|
||||
|
||||
```bash
|
||||
sudo mysql -D gradido_community -e "insert into user_roles(user_id, role) values((select id from users order by id desc limit 1), 'ADMIN');"
|
||||
```
|
||||
|
||||
- it will make last registered user admin
|
||||
- login with you newly created user
|
||||
- if you has a link to `Admin Area` it worked and you are admin
|
||||
|
||||
47
deployment/hetzner_cloud/cloudConfig.yaml
Normal file
47
deployment/hetzner_cloud/cloudConfig.yaml
Normal file
@ -0,0 +1,47 @@
|
||||
#cloud-config
|
||||
users:
|
||||
- name: gradido
|
||||
groups: users, admin, sudo
|
||||
sudo: ALL=(ALL) NOPASSWD:/etc/init.d/nginx start,/etc/init.d/nginx stop,/etc/init.d/nginx restart
|
||||
shell: /bin/bash
|
||||
ssh_authorized_keys:
|
||||
- <public_ssh_key>
|
||||
|
||||
packages:
|
||||
- fail2ban
|
||||
- python3-systemd
|
||||
- ufw
|
||||
- git
|
||||
- mariadb-server
|
||||
- nginx
|
||||
- curl
|
||||
- build-essential
|
||||
- gnupg
|
||||
- certbot
|
||||
- python3-certbot-nginx
|
||||
- logrotate
|
||||
- automysqlbackup
|
||||
- expect
|
||||
package_update: true
|
||||
package_upgrade: true
|
||||
|
||||
runcmd:
|
||||
- printf "[sshd]\nenabled = true\nbanaction = iptables-multiport" > /etc/fail2ban/jail.local
|
||||
- systemctl enable fail2ban
|
||||
|
||||
- ufw allow OpenSSH
|
||||
- ufw allow http
|
||||
- ufw allow https
|
||||
- ufw enable
|
||||
|
||||
- sed -i -e '/^\(#\|\)PasswordAuthentication/s/^.*$/PasswordAuthentication no/' /etc/ssh/sshd_config
|
||||
- sed -i -e '/^\(#\|\)KbdInteractiveAuthentication/s/^.*$/KbdInteractiveAuthentication no/' /etc/ssh/sshd_config
|
||||
- sed -i -e '/^\(#\|\)ChallengeResponseAuthentication/s/^.*$/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config
|
||||
- sed -i -e '/^\(#\|\)MaxAuthTries/s/^.*$/MaxAuthTries 3/' /etc/ssh/sshd_config
|
||||
- sed -i -e '/^\(#\|\)AllowTcpForwarding/s/^.*$/AllowTcpForwarding no/' /etc/ssh/sshd_config
|
||||
- sed -i -e '/^\(#\|\)X11Forwarding/s/^.*$/X11Forwarding no/' /etc/ssh/sshd_config
|
||||
- sed -i -e '/^\(#\|\)AllowAgentForwarding/s/^.*$/AllowAgentForwarding no/' /etc/ssh/sshd_config
|
||||
- sed -i -e '/^\(#\|\)AuthorizedKeysFile/s/^.*$/AuthorizedKeysFile .ssh\/authorized_keys/' /etc/ssh/sshd_config
|
||||
- sed -i '$a AllowUsers gradido root' /etc/ssh/sshd_config
|
||||
|
||||
- reboot
|
||||
38
deployment/hetzner_cloud/crontabs.txt
Normal file
38
deployment/hetzner_cloud/crontabs.txt
Normal file
@ -0,0 +1,38 @@
|
||||
# Edit this file to introduce tasks to be run by cron.
|
||||
#
|
||||
# Each task to run has to be defined through a single line
|
||||
# indicating with different fields when the task will be run
|
||||
# and what command to run for the task
|
||||
#
|
||||
# To define the time you can provide concrete values for
|
||||
# minute (m), hour (h), day of month (dom), month (mon),
|
||||
# and day of week (dow) or use '*' in these fields (for 'any').
|
||||
#
|
||||
# Notice that tasks will be started based on the cron's system
|
||||
# daemon's notion of time and timezones.
|
||||
#
|
||||
# Output of the crontab jobs (including errors) is sent through
|
||||
# email to the user the crontab file belongs to (unless redirected).
|
||||
#
|
||||
# For example, you can run a backup of all your user accounts
|
||||
# at 5 a.m every week with:
|
||||
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
|
||||
#
|
||||
# For more information see the manual pages of crontab(5) and cron(8)
|
||||
#
|
||||
# m h dom mon dow command
|
||||
|
||||
# `yarn` creates output in `/tmp` directory. This output is generated whenever `yarn start` is called.
|
||||
# This is especially problematic on staging systems where instable versions are automatically deployed which can lead to an ever restarting,
|
||||
# hence generating a lot of yarn output.
|
||||
# the following hourly cron clean the /tmp folder
|
||||
0 * * * * find /tmp -name "yarn--*" -exec rm -r {} \; > /dev/null
|
||||
|
||||
# cronjob for a daily db backup at 3:00am
|
||||
0 3 * * * ~/gradido/deployment/bare_metal/backup.sh
|
||||
|
||||
# cronjob for a daily logfile clearance at 3:15
|
||||
# remove all log files older than 30 days
|
||||
15 3 * * * ~/gradido/deployment/bare_metal/removeLogFiles.sh
|
||||
|
||||
|
||||
161
deployment/hetzner_cloud/install.sh
Executable file
161
deployment/hetzner_cloud/install.sh
Executable file
@ -0,0 +1,161 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Note: This is needed - since there is Summer-Time included in the default server Setup - UTC is REQUIRED for production data
|
||||
timedatectl set-timezone UTC
|
||||
timedatectl set-ntp on
|
||||
apt purge ntp
|
||||
systemctl start systemd-timesyncd
|
||||
|
||||
set -o allexport
|
||||
SCRIPT_PATH=$(realpath ../bare_metal)
|
||||
SCRIPT_DIR=$(dirname $SCRIPT_PATH)
|
||||
LOCAL_SCRIPT_PATH=$(realpath $0)
|
||||
LOCAL_SCRIPT_DIR=$(dirname $LOCAL_SCRIPT_PATH)
|
||||
PROJECT_ROOT=$SCRIPT_DIR/..
|
||||
set +o allexport
|
||||
|
||||
# If install.sh will be called more than once
|
||||
# We have to load the backend .env to get DB_USERNAME, DB_PASSWORD AND JWT_SECRET
|
||||
# and the dht-node .env to get FEDERATION_DHT_SEED
|
||||
export_var(){
|
||||
export $1=$(grep -v '^#' $PROJECT_ROOT/backend/.env | grep -e "$1" | sed -e 's/.*=//')
|
||||
export $1=$(grep -v '^#' $PROJECT_ROOT/dht-node/.env | grep -e "$1" | sed -e 's/.*=//')
|
||||
}
|
||||
|
||||
if [ -f "$PROJECT_ROOT/backend/.env" ]; then
|
||||
export_var 'DB_USER'
|
||||
export_var 'DB_PASSWORD'
|
||||
export_var 'JWT_SECRET'
|
||||
fi
|
||||
|
||||
if [ -f "$PROJECT_ROOT/dht-node/.env" ]; then
|
||||
export_var 'FEDERATION_DHT_SEED'
|
||||
fi
|
||||
|
||||
|
||||
# Load .env or .env.dist if not present
|
||||
# NOTE: all config values will be in process.env when starting
|
||||
# the services and will therefore take precedence over the .env
|
||||
if [ -f "$SCRIPT_PATH/.env" ]; then
|
||||
set -o allexport
|
||||
source $SCRIPT_PATH/.env
|
||||
set +o allexport
|
||||
else
|
||||
set -o allexport
|
||||
source $SCRIPT_PATH/.env.dist
|
||||
set +o allexport
|
||||
fi
|
||||
|
||||
# Configure git
|
||||
git config pull.ff only
|
||||
|
||||
# Secure mysql https://gist.github.com/Mins/4602864
|
||||
SECURE_MYSQL=$(expect -c "
|
||||
|
||||
set timeout 10
|
||||
spawn mysql_secure_installation
|
||||
|
||||
expect \"Enter current password for root (enter for none):\"
|
||||
send \"\r\"
|
||||
|
||||
expect \"Switch to unix_socket authentication:\"
|
||||
send \"Y\r\"
|
||||
|
||||
expect \"Change the root password?\"
|
||||
send \"n\r\"
|
||||
|
||||
expect \"Remove anonymous users?\"
|
||||
send \"y\r\"
|
||||
|
||||
expect \"Disallow root login remotely?\"
|
||||
send \"y\r\"
|
||||
|
||||
expect \"Remove test database and access to it?\"
|
||||
send \"y\r\"
|
||||
|
||||
expect \"Reload privilege tables now?\"
|
||||
send \"y\r\"
|
||||
|
||||
expect eof
|
||||
")
|
||||
echo "$SECURE_MYSQL"
|
||||
|
||||
# Configure fail2ban, seems to not run out of the box on Debian 12
|
||||
echo -e "[sshd]\nbackend = systemd" | tee /etc/fail2ban/jail.d/sshd.conf
|
||||
# enable nginx-limit-req filter to block also user which exceed nginx request limiter
|
||||
echo -e "[nginx-limit-req]\nenabled = true\nlogpath = $SCRIPT_PATH/log/nginx-error.*.log" | tee /etc/fail2ban/jail.d/nginx-limit-req.conf
|
||||
# enable nginx bad request filter
|
||||
echo -e "[nginx-bad-request]\nenabled = true\nlogpath = $SCRIPT_PATH/log/nginx-error.*.log" | tee /etc/fail2ban/jail.d/nginx-bad-request.conf
|
||||
systemctl restart fail2ban
|
||||
|
||||
# Configure nginx
|
||||
rm /etc/nginx/sites-enabled/default
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $SCRIPT_PATH/nginx/sites-available/gradido.conf.template > $SCRIPT_PATH/nginx/sites-available/gradido.conf
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $SCRIPT_PATH/nginx/sites-available/update-page.conf.template > $SCRIPT_PATH/nginx/sites-available/update-page.conf
|
||||
mkdir $SCRIPT_PATH/nginx/sites-enabled
|
||||
ln -s $SCRIPT_PATH/nginx/sites-available/update-page.conf $SCRIPT_PATH/nginx/sites-enabled/default
|
||||
ln -s $SCRIPT_PATH/nginx/sites-enabled/default /etc/nginx/sites-enabled
|
||||
ln -s $SCRIPT_PATH/nginx/common /etc/nginx/
|
||||
rmdir /etc/nginx/conf.d
|
||||
ln -s $SCRIPT_PATH/nginx/conf.d /etc/nginx/
|
||||
|
||||
# setup https with certbot
|
||||
certbot certonly --nginx --non-interactive --agree-tos --domains $COMMUNITY_HOST --email $COMMUNITY_SUPPORT_MAIL
|
||||
|
||||
# Install node 16. with nvm, with nodesource is depracted
|
||||
sudo -u gradido bash -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash'
|
||||
# Close and reopen your terminal to start using nvm or run the following to use it now:
|
||||
sudo -u gradido bash -c 'export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"'
|
||||
sudo -u gradido bash -c '. $HOME/.nvm/nvm.sh && nvm install 16' # first installed version will be set to default automatic
|
||||
|
||||
# Install yarn
|
||||
sudo -u gradido bash -c '. $HOME/.nvm/nvm.sh && npm i -g yarn'
|
||||
|
||||
# Install pm2
|
||||
sudo -u gradido bash -c '. $HOME/.nvm/nvm.sh && npm i -g pm2 && pm2 startup'
|
||||
|
||||
# Install logrotate
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $SCRIPT_PATH/logrotate/gradido.conf.template > $SCRIPT_PATH/logrotate/gradido.conf
|
||||
cp $SCRIPT_PATH/logrotate/gradido.conf /etc/logrotate.d/gradido.conf
|
||||
|
||||
# create db user
|
||||
export DB_USER=gradido
|
||||
# create a new password only if it not already exist
|
||||
if [ -z "${DB_PASSWORD}" ]; then
|
||||
export DB_PASSWORD=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo);
|
||||
fi
|
||||
mysql <<EOFMYSQL
|
||||
CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASSWORD';
|
||||
GRANT ALL PRIVILEGES ON *.* TO '$DB_USER'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
EOFMYSQL
|
||||
|
||||
# Configure database
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/database/.env.template > $PROJECT_ROOT/database/.env
|
||||
|
||||
# Configure backend
|
||||
export JWT_SECRET=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo);
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/backend/.env.template > $PROJECT_ROOT/backend/.env
|
||||
|
||||
# Configure frontend
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/frontend/.env.template > $PROJECT_ROOT/frontend/.env
|
||||
|
||||
# Configure admin
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/admin/.env.template > $PROJECT_ROOT/admin/.env
|
||||
|
||||
# Configure dht-node
|
||||
export FEDERATION_DHT_SEED=$(< /dev/urandom tr -dc a-f0-9 | head -c 32;echo);
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/dht-node/.env.template > $PROJECT_ROOT/dht-node/.env
|
||||
|
||||
# Configure federation
|
||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/federation/.env.template > $PROJECT_ROOT/federation/.env
|
||||
|
||||
# set all created or modified files back to belonging to gradido
|
||||
chown -R gradido:gradido $PROJECT_ROOT
|
||||
|
||||
# create cronjob to delete yarn output in /tmp and for making backups regulary
|
||||
sudo -u gradido crontab < $LOCAL_SCRIPT_DIR/crontabs.txt
|
||||
|
||||
# Start gradido
|
||||
# Note: on first startup some errors will occur - nothing serious
|
||||
sudo -u gradido $SCRIPT_PATH/start.sh
|
||||
@ -15,5 +15,5 @@ TYPEORM_LOGGING_RELATIVE_PATH=typeorm.dht-node.log
|
||||
FEDERATION_DHT_TOPIC=GRADIDO_HUB
|
||||
# FEDERATION_DHT_SEED=64ebcb0e3ad547848fef4197c6e2332f
|
||||
FEDERATION_COMMUNITY_URL=http://localhost
|
||||
# the api port is the dht baseport, which will be added with the supported api-versions, e.g. 1_0 = 5010
|
||||
FEDERATION_COMMUNITY_API_PORT=5000
|
||||
# comma separated values, which apis should be announced
|
||||
FEDERATION_COMMUNITY_APIS=1_0
|
||||
@ -1,5 +1,5 @@
|
||||
# must match the CONFIG_VERSION.EXPECTED definition in scr/config/index.ts
|
||||
CONFIG_VERSION=v3.2023-04-26
|
||||
CONFIG_VERSION=$FEDERATION_DHT_CONFIG_VERSION
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
@ -19,5 +19,7 @@ FEDERATION_DHT_CONFIG_VERSION=$FEDERATION_DHT_CONFIG_VERSION
|
||||
# on an hash created from this topic
|
||||
FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC
|
||||
FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED
|
||||
FEDERATION_COMMUNITY_URL=$FEDERATION_COMMUNITY_URL
|
||||
FEDERATION_COMMUNITY_API_PORT=$FEDERATION_COMMUNITY_API_PORT
|
||||
# comma separated values, which apis should be announced
|
||||
FEDERATION_COMMUNITY_APIS=$FEDERATION_COMMUNITY_APIS
|
||||
COMMUNITY_HOST=$COMMUNITY_HOST
|
||||
URL_PROTOCOL=$URL_PROTOCOL
|
||||
|
||||
@ -7,7 +7,7 @@ module.exports = {
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
lines: 83,
|
||||
lines: 82,
|
||||
},
|
||||
},
|
||||
setupFiles: ['<rootDir>/test/testSetup.ts'],
|
||||
@ -21,6 +21,11 @@ module.exports = {
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/entity/$1'
|
||||
: '<rootDir>/../database/build/entity/$1',
|
||||
'@logging/(.*)':
|
||||
// eslint-disable-next-line n/no-process-env
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/logging/$1'
|
||||
: '<rootDir>/../database/build/logging/$1',
|
||||
'@dbTools/(.*)':
|
||||
// eslint-disable-next-line n/no-process-env
|
||||
process.env.NODE_ENV === 'development'
|
||||
|
||||
@ -4,46 +4,50 @@ import dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
const constants = {
|
||||
DB_VERSION: '0079-introduce_gms_registration',
|
||||
DB_VERSION: '0082-introduce_gms_registration',
|
||||
LOG4JS_CONFIG: 'log4js-config.json',
|
||||
// default log level on production should be info
|
||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
|
||||
CONFIG_VERSION: {
|
||||
DEFAULT: 'DEFAULT',
|
||||
EXPECTED: 'v3.2023-04-26',
|
||||
EXPECTED: 'v4.2024-01-17',
|
||||
CURRENT: '',
|
||||
},
|
||||
}
|
||||
|
||||
const server = {
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' || false,
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' ?? false,
|
||||
}
|
||||
|
||||
const database = {
|
||||
DB_HOST: process.env.DB_HOST || 'localhost',
|
||||
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',
|
||||
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',
|
||||
process.env.TYPEORM_LOGGING_RELATIVE_PATH ?? 'typeorm.dht-node.log',
|
||||
}
|
||||
|
||||
const community = {
|
||||
COMMUNITY_NAME: process.env.COMMUNITY_NAME || 'Gradido Entwicklung',
|
||||
COMMUNITY_NAME: process.env.COMMUNITY_NAME ?? 'Gradido Entwicklung',
|
||||
COMMUNITY_DESCRIPTION:
|
||||
process.env.COMMUNITY_DESCRIPTION || 'Gradido-Community einer lokalen Entwicklungsumgebung.',
|
||||
process.env.COMMUNITY_DESCRIPTION ?? 'Gradido-Community einer lokalen Entwicklungsumgebung.',
|
||||
}
|
||||
|
||||
const COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? 'localhost'
|
||||
const URL_PROTOCOL = process.env.URL_PROTOCOL ?? 'http'
|
||||
const COMMUNITY_URL = process.env.COMMUNITY_URL ?? `${URL_PROTOCOL}://${COMMUNITY_HOST}`
|
||||
|
||||
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 || 'http://localhost',
|
||||
FEDERATION_COMMUNITY_API_PORT: process.env.FEDERATION_COMMUNITY_API_PORT || '5000',
|
||||
FEDERATION_DHT_TOPIC: process.env.FEDERATION_DHT_TOPIC ?? 'GRADIDO_HUB',
|
||||
FEDERATION_DHT_SEED: process.env.FEDERATION_DHT_SEED ?? null,
|
||||
FEDERATION_COMMUNITY_URL: process.env.FEDERATION_COMMUNITY_URL ?? COMMUNITY_URL,
|
||||
FEDERATION_COMMUNITY_APIS: process.env.FEDERATION_COMMUNITY_APIS ?? '1_0',
|
||||
}
|
||||
|
||||
// Check config version
|
||||
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
|
||||
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,
|
||||
|
||||
5
dht-node/src/dht_node/ApiVersionType.ts
Normal file
5
dht-node/src/dht_node/ApiVersionType.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export enum ApiVersionType {
|
||||
V1_0 = '1_0',
|
||||
V1_1 = '1_1', // currently no changes
|
||||
V2_0 = '2_0', // not exist
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user