mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
merged
This commit is contained in:
commit
2c28d4c9fb
20
CHANGELOG.md
20
CHANGELOG.md
@ -4,8 +4,28 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [2.2.0](https://github.com/gradido/gradido/compare/2.1.1...2.2.0)
|
||||
|
||||
- feat(frontend): update news text [`#3279`](https://github.com/gradido/gradido/pull/3279)
|
||||
- feat(frontend): use params instead of query for send/identifier route [`#3277`](https://github.com/gradido/gradido/pull/3277)
|
||||
- fix(other): deployment bugfixes [`#3276`](https://github.com/gradido/gradido/pull/3276)
|
||||
- refactor(other): federation optimize logging, fix bug [`#3272`](https://github.com/gradido/gradido/pull/3272)
|
||||
- feat(other): request limit [`#3274`](https://github.com/gradido/gradido/pull/3274)
|
||||
- feat(dlt): logging views [`#3270`](https://github.com/gradido/gradido/pull/3270)
|
||||
- refactor(other): hetzner cloud deploy, refactor .env [`#3267`](https://github.com/gradido/gradido/pull/3267)
|
||||
- refactor(dlt): dlt connector try out dci [`#3223`](https://github.com/gradido/gradido/pull/3223)
|
||||
- feat(backend): fill linked_user_gradido_id and linked_user_name for creation transactions [`#3268`](https://github.com/gradido/gradido/pull/3268)
|
||||
- feat(frontend): use day for contribution dates [`#3269`](https://github.com/gradido/gradido/pull/3269)
|
||||
- feat(backend): fill linked_user_id for creation transactions [`#3266`](https://github.com/gradido/gradido/pull/3266)
|
||||
- docs(other): 3258 feature create gms usecase docu [`#3260`](https://github.com/gradido/gradido/pull/3260)
|
||||
- fix(frontend): update moderatorChangedMemo [`#3259`](https://github.com/gradido/gradido/pull/3259)
|
||||
- chore(other): change filename date-pattern and stop all modules with db-write-access [`#3245`](https://github.com/gradido/gradido/pull/3245)
|
||||
|
||||
#### [2.1.1](https://github.com/gradido/gradido/compare/2.0.1...2.1.1)
|
||||
|
||||
> 1 December 2023
|
||||
|
||||
- chore(release): v2.1.1 [`#3257`](https://github.com/gradido/gradido/pull/3257)
|
||||
- feat(admin): wiedervorlage v2 [`#3255`](https://github.com/gradido/gradido/pull/3255)
|
||||
- feat(admin): resubmission [`#3252`](https://github.com/gradido/gradido/pull/3252)
|
||||
- feat(backend): grant moderator right to edit contribution memo [`#3233`](https://github.com/gradido/gradido/pull/3233)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"description": "Administraion Interface for Gradido",
|
||||
"main": "index.js",
|
||||
"author": "Moriz Wahl",
|
||||
"version": "2.1.1",
|
||||
"version": "2.2.0",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
|
||||
@ -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,11 +49,11 @@ 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_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
|
||||
EMAIL_CODE_REQUEST_TIME=$EMAIL_CODE_REQUEST_TIME
|
||||
|
||||
@ -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
|
||||
|
||||
@ -28,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'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido-backend",
|
||||
"version": "2.1.1",
|
||||
"version": "2.2.0",
|
||||
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/backend",
|
||||
@ -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",
|
||||
|
||||
@ -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: '0082-join_community_federated_communities',
|
||||
DB_VERSION: '0083-join_community_federated_communities',
|
||||
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 {
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { IsBoolean, IsInt, IsString } from 'class-validator'
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
import { ArgsType, Field, InputType, Int } from 'type-graphql'
|
||||
|
||||
import { Location } from '@model/Location'
|
||||
|
||||
import { isValidLocation } from '@/graphql/validator/Location'
|
||||
|
||||
@InputType()
|
||||
@ArgsType()
|
||||
export class UpdateUserInfosArgs {
|
||||
@Field({ nullable: true })
|
||||
@ -38,4 +43,20 @@ export class UpdateUserInfosArgs {
|
||||
@Field({ nullable: true })
|
||||
@IsBoolean()
|
||||
hideAmountGDT?: boolean
|
||||
|
||||
@Field({ nullable: true, defaultValue: true })
|
||||
@IsBoolean()
|
||||
gmsAllowed?: boolean
|
||||
|
||||
@Field(() => Int, { nullable: true, defaultValue: 0 })
|
||||
@IsInt()
|
||||
gmsPublishName?: number | null
|
||||
|
||||
@Field(() => Location, { nullable: true })
|
||||
@isValidLocation()
|
||||
gmsLocation?: Location | null
|
||||
|
||||
@Field(() => Int, { nullable: true, defaultValue: 2 })
|
||||
@IsInt()
|
||||
gmsPublishLocation?: number | null
|
||||
}
|
||||
|
||||
@ -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
|
||||
})
|
||||
10
backend/src/graphql/model/Location.ts
Normal file
10
backend/src/graphql/model/Location.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class Location {
|
||||
@Field(() => Int)
|
||||
longitude: number
|
||||
|
||||
@Field(() => Int)
|
||||
latitude: number
|
||||
}
|
||||
@ -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,10 +133,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
homeCom1 = DbFederatedCommunity.create()
|
||||
homeCom1.foreign = false
|
||||
homeCom1.publicKey = Buffer.from(
|
||||
'8a1f9374b99c30d827b85dcd23f7e50328430d64ef65ef35bf375ea8eb9a2e1d',
|
||||
'hex',
|
||||
)
|
||||
homeCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[0].public, 'hex')
|
||||
homeCom1.apiVersion = '1_0'
|
||||
homeCom1.endPoint = 'http://localhost/api'
|
||||
homeCom1.createdAt = new Date()
|
||||
@ -90,10 +141,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
homeCom2 = DbFederatedCommunity.create()
|
||||
homeCom2.foreign = false
|
||||
homeCom2.publicKey = Buffer.from(
|
||||
'7d849b4b53232ed8f3c913e1944b4a20b1a33e44f65ee56673209c14df0e3252',
|
||||
'hex',
|
||||
)
|
||||
homeCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[1].public, 'hex')
|
||||
homeCom2.apiVersion = '1_1'
|
||||
homeCom2.endPoint = 'http://localhost/api'
|
||||
homeCom2.createdAt = new Date()
|
||||
@ -101,10 +149,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
homeCom3 = DbFederatedCommunity.create()
|
||||
homeCom3.foreign = false
|
||||
homeCom3.publicKey = Buffer.from(
|
||||
'f9a683abf2d1bf265c8dcb543d3674e080db5a5d6976a2dd6ea6969c798f8a8c',
|
||||
'hex',
|
||||
)
|
||||
homeCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[2].public, 'hex')
|
||||
homeCom3.apiVersion = '2_0'
|
||||
homeCom3.endPoint = 'http://localhost/api'
|
||||
homeCom3.createdAt = new Date()
|
||||
@ -118,10 +163,8 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 3,
|
||||
foreign: homeCom3.foreign,
|
||||
publicKey: expect.stringMatching(
|
||||
'f9a683abf2d1bf265c8dcb543d3674e080db5a5d6976a2dd6ea6969c798f8a8c',
|
||||
),
|
||||
endPoint: expect.stringMatching('http://localhost/api/'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[2].public),
|
||||
url: expect.stringMatching('http://localhost/api/2_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
@ -131,10 +174,8 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 2,
|
||||
foreign: homeCom2.foreign,
|
||||
publicKey: expect.stringMatching(
|
||||
'7d849b4b53232ed8f3c913e1944b4a20b1a33e44f65ee56673209c14df0e3252',
|
||||
),
|
||||
endPoint: expect.stringMatching('http://localhost/api/'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[1].public),
|
||||
url: expect.stringMatching('http://localhost/api/1_1'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
@ -144,10 +185,8 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 1,
|
||||
foreign: homeCom1.foreign,
|
||||
publicKey: expect.stringMatching(
|
||||
'8a1f9374b99c30d827b85dcd23f7e50328430d64ef65ef35bf375ea8eb9a2e1d',
|
||||
),
|
||||
endPoint: expect.stringMatching('http://localhost/api/'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
|
||||
url: expect.stringMatching('http://localhost/api/1_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
@ -166,10 +205,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
foreignCom1 = DbFederatedCommunity.create()
|
||||
foreignCom1.foreign = true
|
||||
foreignCom1.publicKey = Buffer.from(
|
||||
'2e3c58f8fb7d9e4c1f2e3a9ec51cf4b1473d9b6b422853ac4c28e68ec41db4f3',
|
||||
'hex',
|
||||
)
|
||||
foreignCom1.publicKey = Buffer.from(ed25519KeyPairStaticHex[3].public, 'hex')
|
||||
foreignCom1.apiVersion = '1_0'
|
||||
foreignCom1.endPoint = 'http://remotehost/api'
|
||||
foreignCom1.createdAt = new Date()
|
||||
@ -177,10 +213,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
foreignCom2 = DbFederatedCommunity.create()
|
||||
foreignCom2.foreign = true
|
||||
foreignCom2.publicKey = Buffer.from(
|
||||
'9c74d200b9c8b7210db8e65a58b869e4a8c84eb5b2b8b8e8d1ed74e6d9f5b7a1',
|
||||
'hex',
|
||||
)
|
||||
foreignCom2.publicKey = Buffer.from(ed25519KeyPairStaticHex[4].public, 'hex')
|
||||
foreignCom2.apiVersion = '1_1'
|
||||
foreignCom2.endPoint = 'http://remotehost/api'
|
||||
foreignCom2.createdAt = new Date()
|
||||
@ -188,10 +221,7 @@ describe('CommunityResolver', () => {
|
||||
|
||||
foreignCom3 = DbFederatedCommunity.create()
|
||||
foreignCom3.foreign = true
|
||||
foreignCom3.publicKey = Buffer.from(
|
||||
'13fd4edf5b708b5815f8d7d0c0ba34e6aa810f3f187f0bd3d25e0e74315ec16a',
|
||||
'hex',
|
||||
)
|
||||
foreignCom3.publicKey = Buffer.from(ed25519KeyPairStaticHex[5].public, 'hex')
|
||||
foreignCom3.apiVersion = '1_2'
|
||||
foreignCom3.endPoint = 'http://remotehost/api'
|
||||
foreignCom3.createdAt = new Date()
|
||||
@ -205,10 +235,8 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 3,
|
||||
foreign: homeCom3.foreign,
|
||||
publicKey: expect.stringMatching(
|
||||
'f9a683abf2d1bf265c8dcb543d3674e080db5a5d6976a2dd6ea6969c798f8a8c',
|
||||
),
|
||||
endPoint: expect.stringMatching('http://localhost/api'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[2].public),
|
||||
url: expect.stringMatching('http://localhost/api/2_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
@ -218,10 +246,8 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 2,
|
||||
foreign: homeCom2.foreign,
|
||||
publicKey: expect.stringMatching(
|
||||
'7d849b4b53232ed8f3c913e1944b4a20b1a33e44f65ee56673209c14df0e3252',
|
||||
),
|
||||
endPoint: expect.stringMatching('http://localhost/api'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[1].public),
|
||||
url: expect.stringMatching('http://localhost/api/1_1'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
@ -231,10 +257,8 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 1,
|
||||
foreign: homeCom1.foreign,
|
||||
publicKey: expect.stringMatching(
|
||||
'8a1f9374b99c30d827b85dcd23f7e50328430d64ef65ef35bf375ea8eb9a2e1d',
|
||||
),
|
||||
endPoint: expect.stringMatching('http://localhost/api'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[0].public),
|
||||
url: expect.stringMatching('http://localhost/api/1_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
@ -244,10 +268,8 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 6,
|
||||
foreign: foreignCom3.foreign,
|
||||
publicKey: expect.stringMatching(
|
||||
'13fd4edf5b708b5815f8d7d0c0ba34e6aa810f3f187f0bd3d25e0e74315ec16a',
|
||||
),
|
||||
endPoint: expect.stringMatching('http://remotehost/api'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[5].public),
|
||||
url: expect.stringMatching('http://remotehost/api/1_2'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
@ -257,10 +279,8 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 5,
|
||||
foreign: foreignCom2.foreign,
|
||||
publicKey: expect.stringMatching(
|
||||
'9c74d200b9c8b7210db8e65a58b869e4a8c84eb5b2b8b8e8d1ed74e6d9f5b7a1',
|
||||
),
|
||||
endPoint: expect.stringMatching('http://remotehost/api'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[4].public),
|
||||
url: expect.stringMatching('http://remotehost/api/1_1'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
@ -270,10 +290,8 @@ describe('CommunityResolver', () => {
|
||||
{
|
||||
id: 4,
|
||||
foreign: foreignCom1.foreign,
|
||||
publicKey: expect.stringMatching(
|
||||
'2e3c58f8fb7d9e4c1f2e3a9ec51cf4b1473d9b6b422853ac4c28e68ec41db4f3',
|
||||
),
|
||||
endPoint: expect.stringMatching('http://remotehost/api'),
|
||||
publicKey: expect.stringMatching(ed25519KeyPairStaticHex[3].public),
|
||||
url: expect.stringMatching('http://remotehost/api/1_0'),
|
||||
lastAnnouncedAt: null,
|
||||
verifiedAt: null,
|
||||
lastErrorAt: null,
|
||||
@ -317,11 +335,8 @@ describe('CommunityResolver', () => {
|
||||
homeCom1 = DbCommunity.create()
|
||||
homeCom1.foreign = false
|
||||
homeCom1.url = 'http://localhost/api'
|
||||
homeCom1.publicKey = Buffer.from(
|
||||
'8a1f9374b99c30d827b85dcd23f7e50328430d64ef65ef35bf375ea8eb9a2e1d',
|
||||
'hex',
|
||||
)
|
||||
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'
|
||||
@ -358,11 +373,8 @@ describe('CommunityResolver', () => {
|
||||
homeCom1 = DbCommunity.create()
|
||||
homeCom1.foreign = false
|
||||
homeCom1.url = 'http://localhost/api'
|
||||
homeCom1.publicKey = Buffer.from(
|
||||
'8a1f9374b99c30d827b85dcd23f7e50328430d64ef65ef35bf375ea8eb9a2e1d',
|
||||
'hex',
|
||||
)
|
||||
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'
|
||||
@ -373,14 +385,8 @@ describe('CommunityResolver', () => {
|
||||
foreignCom1 = DbCommunity.create()
|
||||
foreignCom1.foreign = true
|
||||
foreignCom1.url = 'http://stage-2.gradido.net/api'
|
||||
foreignCom1.publicKey = Buffer.from(
|
||||
'8a1f9374b99c30d827b85dcd23f7e50328430d64ef65ef35bf375ea8eb9a2e1d',
|
||||
'hex',
|
||||
)
|
||||
foreignCom1.privateKey = Buffer.from(
|
||||
'f6c2a9d78e20a3c910f35b8ffcf824aa7b37f0d3d81bfc4c0e65e17a194b3a4a',
|
||||
'hex',
|
||||
)
|
||||
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'
|
||||
@ -391,14 +397,8 @@ describe('CommunityResolver', () => {
|
||||
foreignCom2 = DbCommunity.create()
|
||||
foreignCom2.foreign = true
|
||||
foreignCom2.url = 'http://stage-3.gradido.net/api'
|
||||
foreignCom2.publicKey = Buffer.from(
|
||||
'e047365a54082e8a7e9273da61b55c8134a2a0c836799ba12b78b9b0c52bc85f',
|
||||
'hex',
|
||||
)
|
||||
foreignCom2.privateKey = Buffer.from(
|
||||
'e047365a54082e8a7e9273da61b55c8134a2a0c836799ba12b78b9b0c52bc85f',
|
||||
'hex',
|
||||
)
|
||||
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'
|
||||
@ -506,7 +506,7 @@ describe('CommunityResolver', () => {
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
getCommunityByUuid: {
|
||||
community: {
|
||||
id: homeCom?.id,
|
||||
foreign: homeCom?.foreign,
|
||||
name: homeCom?.name,
|
||||
|
||||
@ -50,7 +50,7 @@ export class CommunityResolver {
|
||||
|
||||
@Authorized([RIGHTS.COMMUNITY_BY_UUID])
|
||||
@Query(() => Community)
|
||||
async getCommunityByUuid(@Arg('communityUuid') communityUuid: string): Promise<Community> {
|
||||
async community(@Arg('communityUuid') communityUuid: string): Promise<Community> {
|
||||
const com: DbCommunity | null = await getCommunityByUuid(communityUuid)
|
||||
if (!com) {
|
||||
throw new LogError('community not found', communityUuid)
|
||||
|
||||
@ -15,6 +15,8 @@ import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { GmsPublishLocationType } from '@enum/GmsPublishLocationType'
|
||||
import { GmsPublishNameType } from '@enum/GmsPublishNameType'
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { logger } from '@test/testSetup'
|
||||
|
||||
@ -142,7 +144,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 +171,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 +214,7 @@ describe('send coins', () => {
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'No user with this credentials',
|
||||
'garrick@ollivander.com',
|
||||
homeCom.communityUuid,
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -523,6 +534,9 @@ describe('send coins', () => {
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
alias: 'bob',
|
||||
gmsAllowed: true,
|
||||
gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS,
|
||||
gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM,
|
||||
},
|
||||
})
|
||||
await mutate({
|
||||
|
||||
@ -432,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,
|
||||
|
||||
@ -16,11 +16,14 @@ import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { v4 as uuidv4, validate as validateUUID, version as versionUUID } from 'uuid'
|
||||
|
||||
import { GmsPublishLocationType } from '@enum/GmsPublishLocationType'
|
||||
import { GmsPublishNameType } from '@enum/GmsPublishNameType'
|
||||
import { OptInType } from '@enum/OptInType'
|
||||
import { PasswordEncryptionType } from '@enum/PasswordEncryptionType'
|
||||
import { RoleNames } from '@enum/RoleNames'
|
||||
import { UserContactType } from '@enum/UserContactType'
|
||||
import { ContributionLink } from '@model/ContributionLink'
|
||||
import { Location } from '@model/Location'
|
||||
import { testEnvironment, headerPushMock, resetToken, cleanDB } from '@test/helpers'
|
||||
import { logger, i18n as localization } from '@test/testSetup'
|
||||
|
||||
@ -68,6 +71,8 @@ import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { printTimeDuration } from '@/util/time'
|
||||
import { objectValuesToArray } from '@/util/utilities'
|
||||
|
||||
import { Location2Point } from './util/Location2Point'
|
||||
|
||||
jest.mock('@/emails/sendEmailVariants', () => {
|
||||
const originalModule = jest.requireActual('@/emails/sendEmailVariants')
|
||||
return {
|
||||
@ -1165,7 +1170,12 @@ describe('UserResolver', () => {
|
||||
it('throws an error', async () => {
|
||||
jest.clearAllMocks()
|
||||
resetToken()
|
||||
await expect(mutate({ mutation: updateUserInfos })).resolves.toEqual(
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('401 Unauthorized')],
|
||||
}),
|
||||
@ -1190,7 +1200,12 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('returns true', async () => {
|
||||
await expect(mutate({ mutation: updateUserInfos })).resolves.toEqual(
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
updateUserInfos: true,
|
||||
@ -1214,6 +1229,9 @@ describe('UserResolver', () => {
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
language: 'en',
|
||||
gmsAllowed: true,
|
||||
gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS,
|
||||
gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM,
|
||||
}),
|
||||
])
|
||||
})
|
||||
@ -1249,6 +1267,76 @@ describe('UserResolver', () => {
|
||||
await expect(User.find()).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
alias: 'bibi_Bloxberg',
|
||||
gmsAllowed: true,
|
||||
gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS,
|
||||
gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM,
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('gms attributes', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('default settings', () => {
|
||||
it('updates the user in DB', async () => {
|
||||
await mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {},
|
||||
})
|
||||
await expect(User.find()).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
gmsAllowed: true,
|
||||
gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS,
|
||||
gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM,
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('individual settings', () => {
|
||||
it('updates the user in DB', async () => {
|
||||
await mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
gmsAllowed: false,
|
||||
gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL,
|
||||
gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_APPROXIMATE,
|
||||
},
|
||||
})
|
||||
await expect(User.find()).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
gmsAllowed: false,
|
||||
gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_FIRST_INITIAL,
|
||||
gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_APPROXIMATE,
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('with gms location', () => {
|
||||
const loc = new Location()
|
||||
loc.longitude = 9.573224
|
||||
loc.latitude = 49.679437
|
||||
it('updates the user in DB', async () => {
|
||||
await mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
gmsAllowed: true,
|
||||
gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS,
|
||||
gmsLocation: loc,
|
||||
gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM,
|
||||
},
|
||||
})
|
||||
await expect(User.find()).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
gmsAllowed: true,
|
||||
gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS,
|
||||
location: Location2Point(loc),
|
||||
gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM,
|
||||
}),
|
||||
])
|
||||
})
|
||||
@ -2561,6 +2649,7 @@ describe('UserResolver', () => {
|
||||
query: userQuery,
|
||||
variables: {
|
||||
identifier: 'identifier',
|
||||
communityIdentifier: 'community identifier',
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
@ -2585,6 +2674,9 @@ describe('UserResolver', () => {
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
alias: 'bibi',
|
||||
gmsAllowed: true,
|
||||
gmsPublishName: GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS,
|
||||
gmsPublishLocation: GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM,
|
||||
},
|
||||
})
|
||||
})
|
||||
@ -2646,13 +2738,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,
|
||||
)
|
||||
|
||||
@ -19,11 +19,14 @@ import { SearchUsersFilters } from '@arg/SearchUsersFilters'
|
||||
import { SetUserRoleArgs } from '@arg/SetUserRoleArgs'
|
||||
import { UnsecureLoginArgs } from '@arg/UnsecureLoginArgs'
|
||||
import { UpdateUserInfosArgs } from '@arg/UpdateUserInfosArgs'
|
||||
import { GmsPublishLocationType } from '@enum/GmsPublishLocationType'
|
||||
import { GmsPublishNameType } from '@enum/GmsPublishNameType'
|
||||
import { OptInType } from '@enum/OptInType'
|
||||
import { Order } from '@enum/Order'
|
||||
import { PasswordEncryptionType } from '@enum/PasswordEncryptionType'
|
||||
import { UserContactType } from '@enum/UserContactType'
|
||||
import { SearchAdminUsersResult } from '@model/AdminUser'
|
||||
// import { Location } from '@model/Location'
|
||||
import { User } from '@model/User'
|
||||
import { UserAdmin, SearchUsersResult } from '@model/UserAdmin'
|
||||
|
||||
@ -65,11 +68,12 @@ 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'
|
||||
import { getKlicktippState } from './util/getKlicktippState'
|
||||
import { Location2Point } from './util/Location2Point'
|
||||
import { setUserRole, deleteUserRole } from './util/modifyUserRole'
|
||||
import { sendUserToGms } from './util/sendUserToGms'
|
||||
import { validateAlias } from './util/validateAlias'
|
||||
@ -547,12 +551,29 @@ export class UserResolver {
|
||||
passwordNew,
|
||||
hideAmountGDD,
|
||||
hideAmountGDT,
|
||||
gmsAllowed,
|
||||
gmsPublishName,
|
||||
gmsLocation,
|
||||
gmsPublishLocation,
|
||||
}: UpdateUserInfosArgs,
|
||||
@Ctx() context: Context,
|
||||
): Promise<boolean> {
|
||||
logger.info(`updateUserInfos(${firstName}, ${lastName}, ${language}, ***, ***)...`)
|
||||
const user = getUser(context)
|
||||
logger.info(
|
||||
`updateUserInfos(${firstName}, ${lastName}, ${alias}, ${language}, ***, ***, ${hideAmountGDD}, ${hideAmountGDT}, ${gmsAllowed}, ${gmsPublishName}, ${gmsLocation}, ${gmsPublishLocation})...`,
|
||||
)
|
||||
// check default arg settings
|
||||
if (gmsAllowed === null || gmsAllowed === undefined) {
|
||||
gmsAllowed = true
|
||||
}
|
||||
if (!gmsPublishName) {
|
||||
gmsPublishName = GmsPublishNameType.GMS_PUBLISH_NAME_ALIAS_OR_INITALS
|
||||
}
|
||||
if (!gmsPublishLocation) {
|
||||
gmsPublishLocation = GmsPublishLocationType.GMS_LOCATION_TYPE_RANDOM
|
||||
}
|
||||
|
||||
const user = getUser(context)
|
||||
// try {
|
||||
if (firstName) {
|
||||
user.firstName = firstName
|
||||
}
|
||||
@ -599,6 +620,15 @@ export class UserResolver {
|
||||
user.hideAmountGDT = hideAmountGDT
|
||||
}
|
||||
|
||||
user.gmsAllowed = gmsAllowed
|
||||
user.gmsPublishName = gmsPublishName
|
||||
if (gmsLocation) {
|
||||
user.location = Location2Point(gmsLocation)
|
||||
}
|
||||
user.gmsPublishLocation = gmsPublishLocation
|
||||
// } catch (err) {
|
||||
// console.log('error:', err)
|
||||
// }
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ')
|
||||
@ -609,7 +639,7 @@ export class UserResolver {
|
||||
})
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
logger.debug('writing User data successful...')
|
||||
logger.debug('writing User data successful...', user)
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
throw new LogError('Error on writing updated user data', e)
|
||||
@ -834,11 +864,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
|
||||
|
||||
27
backend/src/graphql/resolver/util/Location2Point.ts
Normal file
27
backend/src/graphql/resolver/util/Location2Point.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Point } from '@dbTools/typeorm'
|
||||
|
||||
import { Location } from '@model/Location'
|
||||
|
||||
export function Location2Point(location: Location): Point {
|
||||
let pointStr: string
|
||||
if (location.longitude && location.latitude) {
|
||||
pointStr = '{ "type": "Point", "coordinates": ['
|
||||
.concat(location.longitude?.toString())
|
||||
.concat(', ')
|
||||
.concat(location.latitude?.toString())
|
||||
.concat('] }')
|
||||
} else {
|
||||
pointStr = '{ "type": "Point", "coordinates": [] }'
|
||||
}
|
||||
const point = JSON.parse(pointStr) as Point
|
||||
return point
|
||||
}
|
||||
|
||||
export function Point2Location(point: Point): Location {
|
||||
const location = new Location()
|
||||
if (point.type === 'Point' && point.coordinates.length === 2) {
|
||||
location.longitude = point.coordinates[0]
|
||||
location.latitude = point.coordinates[1]
|
||||
}
|
||||
return location
|
||||
}
|
||||
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
43
backend/src/graphql/scalar/Location.ts
Normal file
43
backend/src/graphql/scalar/Location.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
import { GraphQLScalarType, Kind } from 'graphql'
|
||||
|
||||
import { Location } from '@model/Location'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
export const LocationScalar = new GraphQLScalarType({
|
||||
name: 'Location',
|
||||
description:
|
||||
'The `Location` scalar type to represent longitude and latitude values of a geo location',
|
||||
|
||||
serialize(value: Location) {
|
||||
return value
|
||||
},
|
||||
|
||||
parseValue(value): Location {
|
||||
try {
|
||||
const loc = new Location()
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
|
||||
loc.longitude = value.longitude
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
|
||||
loc.latitude = value.latitude
|
||||
return loc
|
||||
} catch (err) {
|
||||
throw new LogError('Error:', err)
|
||||
}
|
||||
// return new Location()
|
||||
},
|
||||
|
||||
parseLiteral(ast): Location {
|
||||
if (ast.kind !== Kind.STRING) {
|
||||
throw new TypeError(`${String(ast)} is not a valid Location value.`)
|
||||
}
|
||||
let loc = new Location()
|
||||
try {
|
||||
loc = JSON.parse(ast.value) as Location
|
||||
} catch (err) {
|
||||
throw new LogError('Error:', err)
|
||||
}
|
||||
return loc
|
||||
},
|
||||
})
|
||||
31
backend/src/graphql/scalar/Point.ts
Normal file
31
backend/src/graphql/scalar/Point.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
import { Point as DbPoint } from '@dbTools/typeorm'
|
||||
import { GraphQLScalarType, Kind } from 'graphql'
|
||||
|
||||
export const PointScalar = new GraphQLScalarType({
|
||||
name: 'Point',
|
||||
description:
|
||||
'The `Point` scalar type to represent longitude and latitude values of a geo location',
|
||||
|
||||
serialize(value: DbPoint) {
|
||||
// Check type of value
|
||||
if (value.type !== 'Point') {
|
||||
throw new Error(`PointScalar can only serialize Geometry type 'Point' values`)
|
||||
}
|
||||
return value
|
||||
},
|
||||
|
||||
parseValue(value): DbPoint {
|
||||
const point = JSON.parse(value) as DbPoint
|
||||
return point
|
||||
},
|
||||
|
||||
parseLiteral(ast) {
|
||||
if (ast.kind !== Kind.STRING) {
|
||||
throw new TypeError(`${String(ast)} is not a valid Geometry value.`)
|
||||
}
|
||||
|
||||
const point = JSON.parse(ast.value) as DbPoint
|
||||
return point
|
||||
},
|
||||
})
|
||||
@ -4,14 +4,20 @@ import { Decimal } from 'decimal.js-light'
|
||||
import { GraphQLSchema } from 'graphql'
|
||||
import { buildSchema } from 'type-graphql'
|
||||
|
||||
import { Location } from '@model/Location'
|
||||
|
||||
import { isAuthorized } from './directive/isAuthorized'
|
||||
import { DecimalScalar } from './scalar/Decimal'
|
||||
import { LocationScalar } from './scalar/Location'
|
||||
|
||||
export const schema = async (): Promise<GraphQLSchema> => {
|
||||
return buildSchema({
|
||||
resolvers: [path.join(__dirname, 'resolver', `!(*.test).{js,ts}`)],
|
||||
authChecker: isAuthorized,
|
||||
scalarsMap: [{ type: Decimal, scalar: DecimalScalar }],
|
||||
scalarsMap: [
|
||||
{ type: Decimal, scalar: DecimalScalar },
|
||||
{ type: Location, scalar: LocationScalar },
|
||||
],
|
||||
validate: {
|
||||
validationError: { target: false },
|
||||
skipMissingProperties: true,
|
||||
|
||||
29
backend/src/graphql/validator/Location.ts
Normal file
29
backend/src/graphql/validator/Location.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'
|
||||
|
||||
import { Location } from '@model/Location'
|
||||
|
||||
import { Location2Point } from '@/graphql/resolver/util/Location2Point'
|
||||
|
||||
export function isValidLocation(validationOptions?: ValidationOptions) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
return function (object: Object, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'isValidLocation',
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
validate(value: Location) {
|
||||
// console.log('isValidLocation:', value, value.getPoint())
|
||||
if (!value || Location2Point(value).type === 'Point') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
return `${propertyName} must be a valid Location, ${args.property}`
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -34,6 +34,10 @@ export const updateUserInfos = gql`
|
||||
$locale: String
|
||||
$hideAmountGDD: Boolean
|
||||
$hideAmountGDT: Boolean
|
||||
$gmsAllowed: Boolean
|
||||
$gmsPublishName: Int
|
||||
$gmsLocation: Location
|
||||
$gmsPublishLocation: Int
|
||||
) {
|
||||
updateUserInfos(
|
||||
firstName: $firstName
|
||||
@ -44,6 +48,10 @@ export const updateUserInfos = gql`
|
||||
language: $locale
|
||||
hideAmountGDD: $hideAmountGDD
|
||||
hideAmountGDT: $hideAmountGDT
|
||||
gmsAllowed: $gmsAllowed
|
||||
gmsPublishName: $gmsPublishName
|
||||
gmsLocation: $gmsLocation
|
||||
gmsPublishLocation: $gmsPublishLocation
|
||||
)
|
||||
}
|
||||
`
|
||||
|
||||
@ -136,7 +136,7 @@ export const communitiesQuery = gql`
|
||||
|
||||
export const getCommunityByUuidQuery = gql`
|
||||
query ($communityUuid: String!) {
|
||||
getCommunityByUuid(communityUuid: $communityUuid) {
|
||||
community(communityUuid: $communityUuid) {
|
||||
id
|
||||
foreign
|
||||
name
|
||||
@ -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,30 @@ 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)
|
||||
// because of nginx proxy, needed for limiter
|
||||
app.set('trust proxy', 1)
|
||||
|
||||
// bodyparser json
|
||||
app.use(json())
|
||||
// bodyparser urlencoded for elopage
|
||||
|
||||
@ -30,6 +30,7 @@ export class Connection {
|
||||
Connection.instance = await createConnection({
|
||||
name: 'default',
|
||||
type: 'mysql',
|
||||
legacySpatialSupport: false,
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
username: CONFIG.DB_USER,
|
||||
|
||||
@ -50,6 +50,7 @@ const communityDbUser: dbUser = {
|
||||
},
|
||||
foreign: false,
|
||||
communityUuid: '55555555-4444-4333-2222-11111111',
|
||||
community: null,
|
||||
gmsPublishName: 0,
|
||||
gmsAllowed: false,
|
||||
location: null,
|
||||
|
||||
@ -58,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"
|
||||
@ -3471,6 +3483,11 @@ gensync@^1.0.0-beta.2:
|
||||
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||
|
||||
geojson@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/geojson/-/geojson-0.5.0.tgz#3cd6c96399be65b56ee55596116fe9191ce701c0"
|
||||
integrity sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==
|
||||
|
||||
get-caller-file@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
@ -3679,18 +3696,20 @@ 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"
|
||||
crypto "^1.0.1"
|
||||
decimal.js-light "^2.5.1"
|
||||
dotenv "^10.0.0"
|
||||
geojson "^0.5.0"
|
||||
mysql2 "^2.3.0"
|
||||
reflect-metadata "^0.1.13"
|
||||
ts-mysql-migrate "^1.0.2"
|
||||
typeorm "^0.3.16"
|
||||
uuid "^8.3.2"
|
||||
wkx "^0.5.0"
|
||||
|
||||
grapheme-splitter@^1.0.4:
|
||||
version "1.0.4"
|
||||
@ -3826,6 +3845,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"
|
||||
@ -7284,6 +7308,13 @@ with@^7.0.0:
|
||||
assert-never "^1.2.1"
|
||||
babel-walk "3.0.0-canary-5"
|
||||
|
||||
wkx@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c"
|
||||
integrity sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
word-wrap@^1.2.3, word-wrap@~1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
|
||||
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,14 @@ import {
|
||||
JoinColumn,
|
||||
OneToOne,
|
||||
Geometry,
|
||||
ManyToOne,
|
||||
} from 'typeorm'
|
||||
import { Contribution } from '../Contribution'
|
||||
import { ContributionMessage } from '../ContributionMessage'
|
||||
import { UserContact } from '../UserContact'
|
||||
import { UserRole } from '../UserRole'
|
||||
import { GeometryTransformer } from '../../src/typeorm/GeometryTransformer'
|
||||
import { Community } from '../Community'
|
||||
|
||||
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||
export class User extends BaseEntity {
|
||||
@ -39,6 +42,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,
|
||||
@ -125,7 +132,13 @@ export class User extends BaseEntity {
|
||||
@Column({ name: 'gms_allowed', type: 'bool', default: true })
|
||||
gmsAllowed: boolean
|
||||
|
||||
@Column({ name: 'location', type: 'geometry', default: null, nullable: true })
|
||||
@Column({
|
||||
name: 'location',
|
||||
type: 'geometry',
|
||||
default: null,
|
||||
nullable: true,
|
||||
transformer: GeometryTransformer,
|
||||
})
|
||||
location: Geometry | null
|
||||
|
||||
@Column({
|
||||
@ -9,6 +9,7 @@ import {
|
||||
JoinColumn,
|
||||
} from 'typeorm'
|
||||
import { FederatedCommunity } from '../FederatedCommunity'
|
||||
import { User } from '../User'
|
||||
|
||||
@Entity('communities')
|
||||
export class Community extends BaseEntity {
|
||||
@ -67,6 +68,10 @@ export class Community extends BaseEntity {
|
||||
})
|
||||
updatedAt: Date | null
|
||||
|
||||
@OneToMany(() => User, (user) => user.community)
|
||||
@JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' })
|
||||
users: User[]
|
||||
|
||||
@OneToMany(() => FederatedCommunity, (federatedCommunity) => federatedCommunity.community)
|
||||
@JoinColumn({ name: 'public_key', referencedColumnName: 'publicKey' })
|
||||
federatedCommunities?: FederatedCommunity[]
|
||||
@ -1 +1 @@
|
||||
export { Community } from './0082-join_community_federated_communities/Community'
|
||||
export { Community } from './0083-join_community_federated_communities/Community'
|
||||
|
||||
@ -1 +1 @@
|
||||
export { FederatedCommunity } from './0082-join_community_federated_communities/FederatedCommunity'
|
||||
export { FederatedCommunity } from './0083-join_community_federated_communities/FederatedCommunity'
|
||||
|
||||
@ -1 +1 @@
|
||||
export { User } from './0081-introduce_gms_registration/User'
|
||||
export { User } from './0082-introduce_gms_registration/User'
|
||||
|
||||
@ -1 +1 @@
|
||||
export { UserContact } from './0081-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),
|
||||
}
|
||||
}
|
||||
}
|
||||
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,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido-database",
|
||||
"version": "2.1.1",
|
||||
"version": "2.2.0",
|
||||
"description": "Gradido Database Tool to execute database migrations",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/database",
|
||||
@ -21,6 +21,7 @@
|
||||
"devDependencies": {
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "^3.2.1",
|
||||
"@types/faker": "^5.5.9",
|
||||
"@types/geojson": "^7946.0.13",
|
||||
"@types/node": "^16.10.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
@ -45,11 +46,13 @@
|
||||
"crypto": "^1.0.1",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"dotenv": "^10.0.0",
|
||||
"geojson": "^0.5.0",
|
||||
"mysql2": "^2.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"ts-mysql-migrate": "^1.0.2",
|
||||
"typeorm": "^0.3.16",
|
||||
"uuid": "^8.3.2"
|
||||
"uuid": "^8.3.2",
|
||||
"wkx": "^0.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
|
||||
28
database/src/typeorm/GeometryTransformer.ts
Normal file
28
database/src/typeorm/GeometryTransformer.ts
Normal file
@ -0,0 +1,28 @@
|
||||
/* eslint-disable camelcase */
|
||||
import { Geometry as wkx_Geometry } from 'wkx'
|
||||
import { Geometry } from 'geojson'
|
||||
import { ValueTransformer } from 'typeorm/decorator/options/ValueTransformer'
|
||||
|
||||
/**
|
||||
* TypeORM transformer to convert GeoJSON to MySQL WKT (Well Known Text) e.g. POINT(LAT, LON) and back
|
||||
*/
|
||||
export const GeometryTransformer: ValueTransformer = {
|
||||
to: (geojson: Geometry): string | null => {
|
||||
if (geojson) {
|
||||
const wkxg = wkx_Geometry.parseGeoJSON(geojson)
|
||||
const str = wkxg.toWkt()
|
||||
return str
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
from: (wkb: string): Record<string, any> | null => {
|
||||
// wkb ? wkx_Geometry.parse(wkb).toGeoJSON() : undefined
|
||||
if (!wkb) {
|
||||
return null
|
||||
}
|
||||
const record = wkx_Geometry.parse(wkb)
|
||||
const str = record.toGeoJSON()
|
||||
return str
|
||||
},
|
||||
}
|
||||
@ -143,6 +143,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/faker/-/faker-5.5.9.tgz#588ede92186dc557bff8341d294335d50d255f0c"
|
||||
integrity sha512-uCx6mP3UY5SIO14XlspxsGjgaemrxpssJI0Ol+GfhxtcKpv9pgRZYsS4eeKeHVLje6Qtc8lGszuBI461+gVZBA==
|
||||
|
||||
"@types/geojson@^7946.0.13":
|
||||
version "7946.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.13.tgz#e6e77ea9ecf36564980a861e24e62a095988775e"
|
||||
integrity sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==
|
||||
|
||||
"@types/json-schema@^7.0.9":
|
||||
version "7.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
||||
@ -1132,6 +1137,11 @@ generate-function@^2.3.1:
|
||||
dependencies:
|
||||
is-property "^1.0.2"
|
||||
|
||||
geojson@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/geojson/-/geojson-0.5.0.tgz#3cd6c96399be65b56ee55596116fe9191ce701c0"
|
||||
integrity sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==
|
||||
|
||||
get-caller-file@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
@ -2502,6 +2512,13 @@ which@^2.0.1:
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
wkx@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.5.0.tgz#c6c37019acf40e517cc6b94657a25a3d4aa33e8c"
|
||||
integrity sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
word-wrap@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
|
||||
@ -1,87 +1,101 @@
|
||||
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,11 +104,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
|
||||
NGINX_REWRITE_LEGACY_URLS=false
|
||||
|
||||
WALLET_AUTH_URL=https://stage1.gradido.net/authenticate?token={token}
|
||||
WALLET_URL=https://stage1.gradido.net/login
|
||||
# LEGACY
|
||||
DEFAULT_PUBLISHER_ID=2896
|
||||
WEBHOOK_ELOPAGE_SECRET=secret
|
||||
|
||||
# GMS
|
||||
#GMS_ACTIVE=true
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# check for parameter
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: Please provide a branch name as the first argument."
|
||||
exit 1
|
||||
fi
|
||||
# Find current directory & configure paths
|
||||
set -o allexport
|
||||
SCRIPT_PATH=$(realpath $0)
|
||||
@ -10,12 +14,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 +33,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 +48,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 +75,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
|
||||
@ -64,7 +84,7 @@ pm2 delete all
|
||||
pm2 save
|
||||
|
||||
# git
|
||||
BRANCH=${1:-master}
|
||||
BRANCH=$1
|
||||
echo "Starting with git pull - branch:$BRANCH" >> $UPDATE_HTML
|
||||
cd $PROJECT_ROOT
|
||||
# TODO: this overfetches alot, but ensures we can use start.sh with tags
|
||||
@ -100,9 +120,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 +132,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 +197,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 +208,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 +218,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 +228,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 +239,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 +274,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
|
||||
|
||||
|
||||
166
deployment/hetzner_cloud/install.sh
Executable file
166
deployment/hetzner_cloud/install.sh
Executable file
@ -0,0 +1,166 @@
|
||||
#!/bin/bash
|
||||
# check for parameter
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: Please provide a branch name as the first argument."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 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
|
||||
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