From 6500aac5857a06c7cbdab2cef87e997ea5e9df03 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 5 Feb 2024 13:48:17 +0100 Subject: [PATCH 01/15] precompress admin and frontend files, server directly via nginx --- admin/package.json | 1 + .../sites-available/gradido.conf.ssl.template | 27 +++++-------------- .../sites-available/gradido.conf.template | 22 +++------------ deployment/bare_metal/start.sh | 4 +-- frontend/package.json | 1 + 5 files changed, 13 insertions(+), 42 deletions(-) diff --git a/admin/package.json b/admin/package.json index e34136e4b..522fbd592 100644 --- a/admin/package.json +++ b/admin/package.json @@ -10,6 +10,7 @@ "start": "node run/server.js", "serve": "vue-cli-service serve --open", "build": "vue-cli-service build", + "postbuild": "find build -type f -regex '.*\\.\\(html\\|js\\|css\\|svg\\|json\\)' -exec gzip -9 -k {} +", "dev": "yarn run serve", "analyse-bundle": "yarn build && webpack-bundle-analyzer build/webpack.stats.json", "lint": "eslint --max-warnings=0 --ext .js,.vue,.json .", diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template index d8ed50ba4..047547e91 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template @@ -14,8 +14,8 @@ server { server { server_name $COMMUNITY_HOST; - listen [::]:443 ssl ipv6only=on; - listen 443 ssl; + listen [::]:443 ssl ipv6only=on http2; + listen 443 ssl http2; ssl_certificate $NGINX_SSL_CERTIFICATE; ssl_certificate_key $NGINX_SSL_CERTIFICATE_KEY; include $NGINX_SSL_INCLUDE; @@ -33,7 +33,7 @@ server { return 444; } - #gzip_static on; + gzip_static on; gzip on; gzip_proxied any; gzip_types @@ -53,18 +53,11 @@ 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'; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; + root $PROJECT_ROOT/frontend/ - proxy_pass http://127.0.0.1:3000; - proxy_redirect off; - access_log $GRADIDO_LOG_PATH/nginx-access.frontend.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.frontend.log warn; } @@ -119,15 +112,7 @@ server { 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'; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; - - proxy_pass http://127.0.0.1:8080/; - proxy_redirect off; + root $PROJECT_ROOT/admin/ access_log $GRADIDO_LOG_PATH/nginx-access.admin.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn; diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.template index e0f382467..a61fbee47 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.template @@ -18,7 +18,7 @@ server { return 444; } - #gzip_static on; + gzip_static on; gzip on; gzip_proxied any; gzip_types @@ -40,15 +40,7 @@ server { 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'; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; - - proxy_pass http://127.0.0.1:3000; - proxy_redirect off; + root $PROJECT_ROOT/frontend/ access_log $GRADIDO_LOG_PATH/nginx-access.frontend.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.frontend.log warn; @@ -104,15 +96,7 @@ server { 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'; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; - - proxy_pass http://127.0.0.1:8080/; - proxy_redirect off; + root $PROJECT_ROOT/admin/ access_log $GRADIDO_LOG_PATH/nginx-access.admin.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn; diff --git a/deployment/bare_metal/start.sh b/deployment/bare_metal/start.sh index 4b6498ee0..222e13f81 100755 --- a/deployment/bare_metal/start.sh +++ b/deployment/bare_metal/start.sh @@ -237,8 +237,8 @@ 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 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' diff --git a/frontend/package.json b/frontend/package.json index fb5210a05..1a18388e2 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,6 +6,7 @@ "start": "node run/server.js", "serve": "vue-cli-service serve --open", "build": "vue-cli-service build", + "postbuild": "find build -type f -regex '.*\\.\\(html\\|js\\|css\\|svg\\|json\\)' -exec gzip -9 -k {} +", "dev": "yarn run serve", "analyse-bundle": "yarn build && webpack-bundle-analyzer build/webpack.stats.json", "lint": "eslint --max-warnings=0 --ext .js,.vue,.json .", From 00d84d5c24bbd7ccd59147b0758bb2025e57faae Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 5 Feb 2024 14:19:44 +0100 Subject: [PATCH 02/15] fix nginx config --- .../nginx/sites-available/gradido.conf.ssl.template | 8 ++++++-- .../nginx/sites-available/gradido.conf.template | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template index 047547e91..2079960c5 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template @@ -56,7 +56,9 @@ server { limit_req zone=frontend burst=40 nodelay; limit_conn addr 40; - root $PROJECT_ROOT/frontend/ + root $PROJECT_ROOT/frontend/build/; + index index.html; + try_files $uri $uri/ /index.html = 404; access_log $GRADIDO_LOG_PATH/nginx-access.frontend.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.frontend.log warn; @@ -112,7 +114,9 @@ server { location /admin { limit_req zone=frontend burst=30 nodelay; limit_conn addr 40; - root $PROJECT_ROOT/admin/ + root $PROJECT_ROOT/admin/build/; + index index.html; + try_files $uri $uri/ /index.html = 404; access_log $GRADIDO_LOG_PATH/nginx-access.admin.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn; diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.template index a61fbee47..d1f5cfece 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.template @@ -40,7 +40,9 @@ server { location / { limit_req zone=frontend burst=40 nodelay; limit_conn addr 40; - root $PROJECT_ROOT/frontend/ + root $PROJECT_ROOT/frontend/build/; + index index.html; + try_files $uri $uri/ /index.html = 404; access_log $GRADIDO_LOG_PATH/nginx-access.frontend.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.frontend.log warn; @@ -96,7 +98,9 @@ server { location /admin { limit_req zone=frontend burst=30 nodelay; limit_conn addr 40; - root $PROJECT_ROOT/admin/ + root $PROJECT_ROOT/admin/build/; + index index.html; + try_files $uri $uri/ /index.html = 404; access_log $GRADIDO_LOG_PATH/nginx-access.admin.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn; From 7a6f1655bc9aef2221cb22170c90ad663c58384b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Mon, 5 Feb 2024 18:41:48 +0100 Subject: [PATCH 03/15] fix bug with admin --- .../bare_metal/nginx/sites-available/gradido.conf.ssl.template | 1 + .../bare_metal/nginx/sites-available/gradido.conf.template | 1 + 2 files changed, 2 insertions(+) diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template index 2079960c5..9910c3366 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template @@ -114,6 +114,7 @@ server { location /admin { limit_req zone=frontend burst=30 nodelay; limit_conn addr 40; + rewrite ^/admin/(.*)$ /$1 break; root $PROJECT_ROOT/admin/build/; index index.html; try_files $uri $uri/ /index.html = 404; diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.template index d1f5cfece..e64e7e1ce 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.template @@ -98,6 +98,7 @@ server { location /admin { limit_req zone=frontend burst=30 nodelay; limit_conn addr 40; + rewrite ^/admin/(.*)$ /$1 break; root $PROJECT_ROOT/admin/build/; index index.html; try_files $uri $uri/ /index.html = 404; From 4e955d8d9512415dcc033229c315b27268efa2c9 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 16 Feb 2024 00:42:49 +0100 Subject: [PATCH 04/15] refactor --- backend/jest.config.js | 1 + backend/src/graphql/arg/CommunityArgs.ts | 15 +- .../src/graphql/arg/TransactionSendArgs.ts | 2 +- .../src/graphql/input/EditCommunityInput.ts | 14 ++ .../resolver/CommunityResolver.test.ts | 11 +- .../src/graphql/resolver/CommunityResolver.ts | 52 +++--- .../graphql/resolver/TransactionResolver.ts | 4 +- .../src/graphql/resolver/util/communities.ts | 149 ++++++++++++------ .../resolver/util/findUserByIdentifier.ts | 13 +- backend/src/seeds/graphql/queries.ts | 4 +- backend/src/util/validate.ts | 11 +- backend/tsconfig.json | 1 + dlt-connector/.env.dist | 5 +- dlt-connector/.env.template | 5 +- dlt-connector/jest.config.js | 2 +- dlt-connector/package.json | 1 + dlt-connector/src/client/BackendClient.ts | 88 +++++++++++ dlt-connector/src/config/index.ts | 7 +- dlt-connector/src/index.ts | 48 ++++++ dlt-connector/tsconfig.json | 2 +- dlt-connector/yarn.lock | 19 ++- 21 files changed, 343 insertions(+), 111 deletions(-) create mode 100644 backend/src/graphql/input/EditCommunityInput.ts create mode 100644 dlt-connector/src/client/BackendClient.ts diff --git a/backend/jest.config.js b/backend/jest.config.js index f7edec3dd..de649d66e 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -16,6 +16,7 @@ module.exports = { moduleNameMapper: { '@/(.*)': '/src/$1', '@arg/(.*)': '/src/graphql/arg/$1', + '@input/(.*)': '/src/graphql/input/$1', '@dltConnector/(.*)': '/src/apis/dltConnector/$1', '@enum/(.*)': '/src/graphql/enum/$1', '@model/(.*)': '/src/graphql/model/$1', diff --git a/backend/src/graphql/arg/CommunityArgs.ts b/backend/src/graphql/arg/CommunityArgs.ts index 163a6e504..074901e06 100644 --- a/backend/src/graphql/arg/CommunityArgs.ts +++ b/backend/src/graphql/arg/CommunityArgs.ts @@ -1,14 +1,13 @@ -import { IsString } from 'class-validator' -import { Field, ArgsType, InputType } from 'type-graphql' +import { IsBoolean, IsString } from 'class-validator' +import { ArgsType, Field } from 'type-graphql' -@InputType() @ArgsType() export class CommunityArgs { - @Field(() => String) + @Field(() => String, { nullable: true }) @IsString() - uuid: string + communityIdentifier?: string | null - @Field(() => String) - @IsString() - gmsApiKey: string + @Field(() => Boolean, { nullable: true }) + @IsBoolean() + foreign?: boolean | null } diff --git a/backend/src/graphql/arg/TransactionSendArgs.ts b/backend/src/graphql/arg/TransactionSendArgs.ts index 5bd8b89f7..3cdb0999e 100644 --- a/backend/src/graphql/arg/TransactionSendArgs.ts +++ b/backend/src/graphql/arg/TransactionSendArgs.ts @@ -1,4 +1,4 @@ -import { MaxLength, MinLength, IsString } from 'class-validator' +import { MaxLength, MinLength, IsString, IsUUID } from 'class-validator' import { Decimal } from 'decimal.js-light' import { ArgsType, Field } from 'type-graphql' diff --git a/backend/src/graphql/input/EditCommunityInput.ts b/backend/src/graphql/input/EditCommunityInput.ts new file mode 100644 index 000000000..8c74f874b --- /dev/null +++ b/backend/src/graphql/input/EditCommunityInput.ts @@ -0,0 +1,14 @@ +import { IsString, IsUUID } from 'class-validator' +import { ArgsType, Field, InputType } from 'type-graphql' + +@ArgsType() +@InputType() +export class EditCommunityInput { + @Field(() => String) + @IsUUID('4') + uuid: string + + @Field(() => String) + @IsString() + gmsApiKey: string +} diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index e720eb716..368d88a2d 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -10,6 +10,7 @@ import { Community as DbCommunity } from '@entity/Community' import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { ApolloServerTestClient } from 'apollo-server-testing' import { GraphQLError } from 'graphql/error/GraphQLError' +import { v4 as uuidv4 } from 'uuid' import { cleanDB, testEnvironment } from '@test/helpers' import { logger, i18n as localization } from '@test/testSetup' @@ -19,7 +20,7 @@ import { login, updateHomeCommunityQuery } from '@/seeds/graphql/mutations' import { getCommunities, communitiesQuery, getCommunityByUuidQuery } from '@/seeds/graphql/queries' import { peterLustig } from '@/seeds/users/peter-lustig' -import { getCommunityByUuid } from './util/communities' +import { getCommunity } from './util/communities' // to do: We need a setup for the tests that closes the connection let mutate: ApolloServerTestClient['mutate'], @@ -459,7 +460,7 @@ describe('CommunityResolver', () => { await mutate({ mutation: login, variables: peterLoginData }) // HomeCommunity is still created in userFactory - homeCom = await getCommunityByUuid(admin.communityUuid) + homeCom = await getCommunity(admin.communityUuid) foreignCom1 = DbCommunity.create() foreignCom1.foreign = true @@ -478,7 +479,7 @@ describe('CommunityResolver', () => { foreignCom2.url = 'http://stage-3.gradido.net/api' foreignCom2.publicKey = Buffer.from('publicKey-stage-3_Community') foreignCom2.privateKey = Buffer.from('privateKey-stage-3_Community') - foreignCom2.communityUuid = 'Stage3-Com-UUID' + foreignCom2.communityUuid = uuidv4() foreignCom2.authenticatedAt = new Date() foreignCom2.name = 'Stage-3_Community-name' foreignCom2.description = 'Stage-3_Community-description' @@ -490,7 +491,7 @@ describe('CommunityResolver', () => { await expect( query({ query: getCommunityByUuidQuery, - variables: { communityUuid: homeCom?.communityUuid }, + variables: { communityIdentifier: homeCom?.communityUuid }, }), ).resolves.toMatchObject({ data: { @@ -563,7 +564,7 @@ describe('CommunityResolver', () => { expect( await mutate({ mutation: updateHomeCommunityQuery, - variables: { uuid: 'unknownUuid', gmsApiKey: 'gmsApiKey' }, + variables: { uuid: uuidv4(), gmsApiKey: 'gmsApiKey' }, }), ).toEqual( expect.objectContaining({ diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index 760b982cc..6fac870b1 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -1,16 +1,17 @@ import { IsNull, Not } from '@dbTools/typeorm' import { Community as DbCommunity } from '@entity/Community' import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' -import { Resolver, Query, Authorized, Arg, Mutation, Args } from 'type-graphql' +import { Resolver, Query, Authorized, Mutation, Args } from 'type-graphql' import { CommunityArgs } from '@arg//CommunityArgs' +import { EditCommunityInput } from '@input/EditCommunityInput' import { Community } from '@model/Community' import { FederatedCommunity } from '@model/FederatedCommunity' import { RIGHTS } from '@/auth/RIGHTS' import { LogError } from '@/server/LogError' -import { getCommunityByUuid } from './util/communities' +import { getCommunity } from './util/communities' @Resolver() export class CommunityResolver { @@ -41,41 +42,30 @@ export class CommunityResolver { return dbCommunities.map((dbCom: DbCommunity) => new Community(dbCom)) } - @Authorized([RIGHTS.COMMUNITY_BY_UUID]) + @Authorized([RIGHTS.COMMUNITIES]) @Query(() => Community) - async community(@Arg('communityUuid') communityUuid: string): Promise { - const com: DbCommunity | null = await getCommunityByUuid(communityUuid) - if (!com) { - throw new LogError('community not found', communityUuid) + async community(@Args() { communityIdentifier, foreign }: CommunityArgs): Promise { + const community = await getCommunity(communityIdentifier, foreign) + if (!community) { + throw new LogError('community not found', communityIdentifier, foreign) } - return new Community(com) + return new Community(community) } @Authorized([RIGHTS.COMMUNITY_UPDATE]) @Mutation(() => Community) - async updateHomeCommunity(@Args() { uuid, gmsApiKey }: CommunityArgs): Promise { - let homeCom: DbCommunity | null - let com: Community - if (uuid) { - let toUpdate = false - homeCom = await getCommunityByUuid(uuid) - if (!homeCom) { - throw new LogError('HomeCommunity with uuid not found: ', uuid) - } - if (homeCom.foreign) { - throw new LogError('Error: Only the HomeCommunity could be modified!') - } - if (homeCom.gmsApiKey !== gmsApiKey) { - homeCom.gmsApiKey = gmsApiKey - toUpdate = true - } - if (toUpdate) { - await DbCommunity.save(homeCom) - } - com = new Community(homeCom) - } else { - throw new LogError(`HomeCommunity without an uuid can't be modified!`) + async updateHomeCommunity(@Args() { uuid, gmsApiKey }: EditCommunityInput): Promise { + const homeCom = await getCommunity(uuid) + if (!homeCom) { + throw new LogError('HomeCommunity with uuid not found: ', uuid) } - return com + if (homeCom.foreign) { + throw new LogError('Error: Only the HomeCommunity could be modified!') + } + if (homeCom.gmsApiKey !== gmsApiKey) { + homeCom.gmsApiKey = gmsApiKey + await DbCommunity.save(homeCom) + } + return new Community(homeCom) } } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index ce1ba43da..00894ecd3 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -38,7 +38,7 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' -import { getCommunityByUuid, getCommunityName, isHomeCommunity } from './util/communities' +import { getCommunity, getCommunityName, isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' @@ -452,7 +452,7 @@ export class TransactionResolver { if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) { throw new LogError('X-Community sendCoins disabled per configuration!') } - const recipCom = await getCommunityByUuid(recipientCommunityIdentifier) + const recipCom = await getCommunity(recipientCommunityIdentifier) logger.debug('recipient commuity: ', recipCom) if (recipCom === null) { throw new LogError( diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index e506548c5..fffe5d6b9 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -1,65 +1,116 @@ +import { FindOptionsWhere } from '@dbTools/typeorm' import { Community as DbCommunity } from '@entity/Community' +import { isURL } from 'class-validator' +import { LogError } from '@/server/LogError' +import { isUUID4 } from '@/util/validate' + +function getCommunityFindOptions( + communityIdentifier?: string | boolean | null, + foreign?: boolean | null, +): FindOptionsWhere { + if (communityIdentifier === undefined && foreign === undefined) { + throw new LogError('one of communityIdentifier or foreign must be set') + } + + const where: FindOptionsWhere = {} + // != null cover !== null and !== undefined + if (communityIdentifier != null) { + if (typeof communityIdentifier === 'boolean') { + if (typeof foreign === 'boolean') { + throw new LogError('communityIdentifier cannot be boolean if foreign is set separately ') + } + where.foreign = communityIdentifier + } else if (isURL(communityIdentifier)) { + where.url = communityIdentifier + } else if (isUUID4(communityIdentifier)) { + where.communityUuid = communityIdentifier + } + } + + if (typeof foreign === 'boolean') { + where.foreign = foreign + } + return where +} + +/** + * Retrieves a community from the database based on the provided identifier and foreign status. + * If communityIdentifier is a string, it can represent either the URL, UUID, or name of the community. + * If communityIdentifier is a boolean, it represents the foreign status of the community. + * If foreign is provided separately and is a boolean, it represents the foreign status of the community. + * communityIdentifier and foreign cannot both be boolean + * @param communityIdentifier The identifier (URL, UUID, or name) of the community, or a boolean representing the foreign status. + * @param foreign Optional. If provided and is a boolean, it represents the foreign status of the community. + * @returns A promise that resolves to a DbCommunity object if found, or null if not found. + */ +export async function getCommunity( + communityIdentifier?: string | boolean | null, + foreign?: boolean | null, +): Promise { + return DbCommunity.findOne({ where: getCommunityFindOptions(communityIdentifier, foreign) }) +} + +/** + * Retrieves a community from the database based on the provided identifier and foreign status. + * If communityIdentifier is a string, it can represent either the URL, UUID, or name of the community. + * If communityIdentifier is a boolean, it represents the foreign status of the community. + * If foreign is provided separately and is a boolean, it represents the foreign status of the community. + * communityIdentifier and foreign cannot both be boolean + * @param communityIdentifier The identifier (URL, UUID, or name) of the community, or a boolean representing the foreign status. + * @param foreign Optional. If provided and is a boolean, it represents the foreign status of the community. + * @returns A promise that resolves to a DbCommunity object if found, or throw if not found. + */ +export async function getCommunityOrFail( + communityIdentifier?: string | boolean | null, + foreign?: boolean | null, +): Promise { + return DbCommunity.findOneOrFail({ + where: getCommunityFindOptions(communityIdentifier, foreign), + }) +} + +/** + * Checks if a community with the given identifier exists and is not foreign. + * @param communityIdentifier The identifier (URL, UUID, or name) of the community. + * @returns A promise that resolves to true if a non-foreign community exists with the given identifier, otherwise false. + */ export async function isHomeCommunity(communityIdentifier: string): Promise { - const homeCommunity = await DbCommunity.findOne({ - where: [ - { foreign: false, communityUuid: communityIdentifier }, - { foreign: false, name: communityIdentifier }, - { foreign: false, url: communityIdentifier }, - ], - }) - if (homeCommunity) { - return true - } else { - return false - } + return (await getCommunity(communityIdentifier, false)) !== null } +/** + * Retrieves the home community, i.e., a community that is not foreign. + * @returns A promise that resolves to the home community, or throw if no home community was found + */ export async function getHomeCommunity(): Promise { - return await DbCommunity.findOneOrFail({ - where: [{ foreign: false }], - }) + return getCommunityOrFail(false) } +/** + * Retrieves the URL of the community with the given identifier. + * @param communityIdentifier The identifier (URL, UUID, or name) of the community. + * @returns A promise that resolves to the URL of the community or throw if no community with this identifier was found + */ export async function getCommunityUrl(communityIdentifier: string): Promise { - const community = await DbCommunity.findOneOrFail({ - where: [ - { communityUuid: communityIdentifier }, - { name: communityIdentifier }, - { url: communityIdentifier }, - ], - }) - return community.url + return (await getCommunityOrFail(communityIdentifier)).url } +/** + * Checks if a community with the given identifier exists and has an authenticatedAt property set. + * @param communityIdentifier The identifier (URL, UUID, or name) of the community. + * @returns A promise that resolves to true if a community with an authenticatedAt property exists with the given identifier, otherwise false. + */ export async function isCommunityAuthenticated(communityIdentifier: string): Promise { - const community = await DbCommunity.findOne({ - where: [ - { communityUuid: communityIdentifier }, - { name: communityIdentifier }, - { url: communityIdentifier }, - ], - }) - if (community?.authenticatedAt) { - return true - } else { - return false - } + // The !! operator is used to convert the result to a boolean value. + return !!(await getCommunity(communityIdentifier))?.authenticatedAt } +/** + * Retrieves the name of the community with the given identifier. + * @param communityIdentifier The identifier (URL, UUID, or name) of the community. + * @returns A promise that resolves to the name of the community. If the community does not exist or has no name, an empty string is returned. + */ export async function getCommunityName(communityIdentifier: string): Promise { - const community = await DbCommunity.findOne({ - where: [{ communityUuid: communityIdentifier }, { url: communityIdentifier }], - }) - if (community?.name) { - return community.name - } else { - return '' - } -} - -export async function getCommunityByUuid(communityUuid: string): Promise { - return await DbCommunity.findOne({ - where: [{ communityUuid }], - }) + return (await getCommunity(communityIdentifier))?.name ?? '' } diff --git a/backend/src/graphql/resolver/util/findUserByIdentifier.ts b/backend/src/graphql/resolver/util/findUserByIdentifier.ts index 7e52327d3..435dc3d04 100644 --- a/backend/src/graphql/resolver/util/findUserByIdentifier.ts +++ b/backend/src/graphql/resolver/util/findUserByIdentifier.ts @@ -2,9 +2,11 @@ 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 { isURL } from 'class-validator' import { validate, version } from 'uuid' import { LogError } from '@/server/LogError' +import { isEMail, isUUID4 } from '@/util/validate' import { VALID_ALIAS_REGEX } from './validateAlias' @@ -19,10 +21,11 @@ export const findUserByIdentifier = async ( communityIdentifier: string, ): Promise => { let user: DbUser | null - const communityWhere: FindOptionsWhere = - validate(communityIdentifier) && version(communityIdentifier) === 4 - ? { communityUuid: communityIdentifier } - : { name: communityIdentifier } + const communityWhere: FindOptionsWhere = isURL(communityIdentifier) + ? { url: communityIdentifier } + : isUUID4(communityIdentifier) + ? { communityUuid: communityIdentifier } + : { name: communityIdentifier } if (validate(identifier) && version(identifier) === 4) { user = await DbUser.findOne({ @@ -32,7 +35,7 @@ export const findUserByIdentifier = async ( if (!user) { throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier) } - } else if (/^.{2,}@.{2,}\..{2,}$/.exec(identifier)) { + } else if (isEMail(identifier)) { const userContact = await DbUserContact.findOne({ where: { email: identifier, diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 6bd106174..9e908d202 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -135,8 +135,8 @@ export const communitiesQuery = gql` ` export const getCommunityByUuidQuery = gql` - query ($communityUuid: String!) { - community(communityUuid: $communityUuid) { + query ($communityIdentifier: String!) { + community(communityIdentifier: $communityIdentifier) { id foreign name diff --git a/backend/src/util/validate.ts b/backend/src/util/validate.ts index 4780c94e8..ab0c8a12a 100644 --- a/backend/src/util/validate.ts +++ b/backend/src/util/validate.ts @@ -1,5 +1,6 @@ import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' import { Decimal } from 'decimal.js-light' +import { validate, version } from 'uuid' import { Decay } from '@model/Decay' @@ -16,6 +17,14 @@ function isStringBoolean(value: string): boolean { return false } +function isUUID4(value: string): boolean { + return validate(value) && version(value) === 4 +} + +function isEMail(value: string): boolean { + return /^.{2,}@.{2,}\..{2,}$/.exec(value) !== null +} + async function calculateBalance( userId: number, amount: Decimal, @@ -42,4 +51,4 @@ async function calculateBalance( return { balance, lastTransactionId: lastTransaction.id, decay } } -export { calculateBalance, isStringBoolean } +export { calculateBalance, isStringBoolean, isUUID4, isEMail } diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 28ddf1c38..c61539e12 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -49,6 +49,7 @@ "paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ "@/*": ["src/*"], "@arg/*": ["src/graphql/arg/*"], + "@input/*": ["src/graphql/input/*"], "@dltConnector/*": ["src/apis/dltConnector/*"], "@enum/*": ["src/graphql/enum/*"], "@model/*": ["src/graphql/model/*"], diff --git a/dlt-connector/.env.dist b/dlt-connector/.env.dist index 1247ac3ec..6f2511c7e 100644 --- a/dlt-connector/.env.dist +++ b/dlt-connector/.env.dist @@ -19,4 +19,7 @@ DB_DATABASE_TEST=gradido_dlt_test TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log # DLT-Connector -DLT_CONNECTOR_PORT=6010 \ No newline at end of file +DLT_CONNECTOR_PORT=6010 + +# Route to Backend +BACKEND_SERVER_URL=http://localhost:4000 \ No newline at end of file diff --git a/dlt-connector/.env.template b/dlt-connector/.env.template index e3793f642..a7cbcd6ba 100644 --- a/dlt-connector/.env.template +++ b/dlt-connector/.env.template @@ -15,4 +15,7 @@ DB_DATABASE_TEST=$DB_DATABASE_TEST TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log # DLT-Connector -DLT_CONNECTOR_PORT=$DLT_CONNECTOR_PORT \ No newline at end of file +DLT_CONNECTOR_PORT=$DLT_CONNECTOR_PORT + +# Route to Backend +BACKEND_SERVER_URL=http://localhost:4000 \ No newline at end of file diff --git a/dlt-connector/jest.config.js b/dlt-connector/jest.config.js index 69bc64bb2..e8135c10d 100644 --- a/dlt-connector/jest.config.js +++ b/dlt-connector/jest.config.js @@ -6,7 +6,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { - lines: 66, + lines: 63, }, }, setupFiles: ['/test/testSetup.ts'], diff --git a/dlt-connector/package.json b/dlt-connector/package.json index 5bc9673de..146a096e1 100644 --- a/dlt-connector/package.json +++ b/dlt-connector/package.json @@ -31,6 +31,7 @@ "express": "4.17.1", "express-slow-down": "^2.0.1", "graphql": "^16.7.1", + "graphql-request": "^6.1.0", "graphql-scalars": "^1.22.2", "helmet": "^7.1.0", "log4js": "^6.7.1", diff --git a/dlt-connector/src/client/BackendClient.ts b/dlt-connector/src/client/BackendClient.ts new file mode 100644 index 000000000..2228fdaf8 --- /dev/null +++ b/dlt-connector/src/client/BackendClient.ts @@ -0,0 +1,88 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { gql, GraphQLClient } from 'graphql-request' + +import { CONFIG } from '@/config' +import { CommunityDraft } from '@/graphql/input/CommunityDraft' +import { logger } from '@/logging/logger' +import { LogError } from '@/server/LogError' + +const communityByForeign = gql` + query ($foreign: Boolean) { + community(foreign: $foreign) { + uuid + foreign + creationDate + } + } +` +interface Community { + community: { + uuid: string + foreign: boolean + creationDate: string + } +} +// Source: https://refactoring.guru/design-patterns/singleton/typescript/example +// and ../federation/client/FederationClientFactory.ts +/** + * A Singleton class defines the `getInstance` method that lets clients access + * the unique singleton instance. + */ +// eslint-disable-next-line @typescript-eslint/no-extraneous-class +export class BackendClient { + // eslint-disable-next-line no-use-before-define + private static instance: BackendClient + client: GraphQLClient + /** + * The Singleton's constructor should always be private to prevent direct + * construction calls with the `new` operator. + */ + // eslint-disable-next-line no-useless-constructor, @typescript-eslint/no-empty-function + private constructor() {} + + /** + * The static method that controls the access to the singleton instance. + * + * This implementation let you subclass the Singleton class while keeping + * just one instance of each subclass around. + */ + public static getInstance(): BackendClient | undefined { + if (!BackendClient.instance) { + BackendClient.instance = new BackendClient() + } + if (!BackendClient.instance.client) { + try { + BackendClient.instance.client = new GraphQLClient(CONFIG.BACKEND_SERVER_URL, { + headers: { + 'content-type': 'application/json', + }, + method: 'POST', + jsonSerializer: { + parse: JSON.parse, + stringify: JSON.stringify, + }, + }) + } catch (e) { + logger.error("couldn't connect to backend: ", e) + return + } + } + return BackendClient.instance + } + + public async homeCommunityUUid(): Promise { + logger.info('check home community on backend') + const { data, errors } = await this.client.rawRequest(communityByForeign, { + foreign: false, + }) + if (errors) { + throw new LogError('error getting home community from backend', errors) + } + const communityDraft = new CommunityDraft() + communityDraft.uuid = data.community.uuid + communityDraft.foreign = data.community.foreign + communityDraft.createdAt = data.community.creationDate + return communityDraft + } +} diff --git a/dlt-connector/src/config/index.ts b/dlt-connector/src/config/index.ts index e6febb482..d39623c04 100644 --- a/dlt-connector/src/config/index.ts +++ b/dlt-connector/src/config/index.ts @@ -9,7 +9,7 @@ const constants = { LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v4.2023-09-12', + EXPECTED: 'v5.2024-02-24', CURRENT: '', }, } @@ -38,6 +38,10 @@ const dltConnector = { DLT_CONNECTOR_PORT: process.env.DLT_CONNECTOR_PORT ?? 6010, } +const backendServer = { + BACKEND_SERVER_URL: process.env.BACKEND_SERVER_URL ?? 'http://backend:4000', +} + // Check config version constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION ?? constants.CONFIG_VERSION.DEFAULT if ( @@ -56,4 +60,5 @@ export const CONFIG = { ...database, ...iota, ...dltConnector, + ...backendServer, } diff --git a/dlt-connector/src/index.ts b/dlt-connector/src/index.ts index c72978b35..82bdba658 100644 --- a/dlt-connector/src/index.ts +++ b/dlt-connector/src/index.ts @@ -1,13 +1,61 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import 'reflect-metadata' + import { CONFIG } from '@/config' +import { BackendClient } from './client/BackendClient' +import { CommunityRepository } from './data/Community.repository' +import { CommunityDraft } from './graphql/input/CommunityDraft' +import { AddCommunityContext } from './interactions/backendToDb/community/AddCommunity.context' +import { logger } from './logging/logger' import createServer from './server/createServer' +import { LogError } from './server/LogError' + +async function waitForServer( + backend: BackendClient, + retryIntervalMs: number, + maxRetries: number, +): Promise { + let retries = 0 + while (retries < maxRetries) { + logger.info(`Attempt ${retries + 1} for connecting to backend`) + + try { + // Make a HEAD request to the server + return await backend.homeCommunityUUid() + } catch (error) { + logger.info('Server is not reachable: ', error) + } + + // Server is not reachable, wait and retry + await new Promise((resolve) => setTimeout(resolve, retryIntervalMs)) + retries++ + } + + throw new LogError('Max retries exceeded. Server did not become reachable.') +} async function main() { // eslint-disable-next-line no-console console.log(`DLT_CONNECTOR_PORT=${CONFIG.DLT_CONNECTOR_PORT}`) const { app } = await createServer() + // ask backend for home community if we haven't one + try { + await CommunityRepository.loadHomeCommunityKeyPair() + } catch (e) { + const backend = BackendClient.getInstance() + if (!backend) { + throw new LogError('cannot create backend client') + } + // wait for backend server to be ready + await waitForServer(backend, 1000, 8) + + const communityDraft = await backend.homeCommunityUUid() + const addCommunityContext = new AddCommunityContext(communityDraft) + await addCommunityContext.run() + } + app.listen(CONFIG.DLT_CONNECTOR_PORT, () => { // eslint-disable-next-line no-console console.log(`Server is running at http://localhost:${CONFIG.DLT_CONNECTOR_PORT}`) diff --git a/dlt-connector/tsconfig.json b/dlt-connector/tsconfig.json index e37b2a7a0..9809d3648 100644 --- a/dlt-connector/tsconfig.json +++ b/dlt-connector/tsconfig.json @@ -63,7 +63,7 @@ "@entity/*": ["../dlt-database/entity/*", "../../dlt-database/build/entity/*"] }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - "typeRoots": ["node_modules/@types"], /* List of folders to include type definitions from. */ + "typeRoots": ["@types", "node_modules/@types"], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ diff --git a/dlt-connector/yarn.lock b/dlt-connector/yarn.lock index 6d50426b1..7fbe1646f 100644 --- a/dlt-connector/yarn.lock +++ b/dlt-connector/yarn.lock @@ -569,7 +569,7 @@ "@graphql-typed-document-node/core" "^3.1.1" tslib "^2.4.0" -"@graphql-typed-document-node/core@^3.1.1": +"@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== @@ -2119,6 +2119,13 @@ cross-env@^7.0.3: dependencies: cross-spawn "^7.0.1" +cross-fetch@^3.1.5: + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + dependencies: + node-fetch "^2.6.12" + cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -3329,6 +3336,14 @@ graphql-query-complexity@^0.12.0: dependencies: lodash.get "^4.4.2" +graphql-request@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f" + integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw== + dependencies: + "@graphql-typed-document-node/core" "^3.2.0" + cross-fetch "^3.1.5" + graphql-scalars@^1.22.2: version "1.22.2" resolved "https://registry.yarnpkg.com/graphql-scalars/-/graphql-scalars-1.22.2.tgz#6326e6fe2d0ad4228a9fea72a977e2bf26b86362" @@ -4775,7 +4790,7 @@ node-abort-controller@^3.1.1: resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== -node-fetch@^2.6.7: +node-fetch@^2.6.12, node-fetch@^2.6.7: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== From cad31c821129ef15a62558a2275623324fad2f6b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 16 Feb 2024 00:51:51 +0100 Subject: [PATCH 05/15] fix lint --- backend/src/graphql/arg/TransactionSendArgs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/arg/TransactionSendArgs.ts b/backend/src/graphql/arg/TransactionSendArgs.ts index 3cdb0999e..5bd8b89f7 100644 --- a/backend/src/graphql/arg/TransactionSendArgs.ts +++ b/backend/src/graphql/arg/TransactionSendArgs.ts @@ -1,4 +1,4 @@ -import { MaxLength, MinLength, IsString, IsUUID } from 'class-validator' +import { MaxLength, MinLength, IsString } from 'class-validator' import { Decimal } from 'decimal.js-light' import { ArgsType, Field } from 'type-graphql' From f0edd686191833a535aae32f3dc9a4f38b1fde38 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 20 Feb 2024 08:59:59 +0100 Subject: [PATCH 06/15] don't longer misuse communityIdentifier as boolean --- .../resolver/CommunityResolver.test.ts | 2 +- .../src/graphql/resolver/CommunityResolver.ts | 8 +-- .../graphql/resolver/TransactionResolver.ts | 2 +- .../src/graphql/resolver/util/communities.ts | 65 +++++++------------ 4 files changed, 31 insertions(+), 46 deletions(-) diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index 368d88a2d..c9c76e306 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -460,7 +460,7 @@ describe('CommunityResolver', () => { await mutate({ mutation: login, variables: peterLoginData }) // HomeCommunity is still created in userFactory - homeCom = await getCommunity(admin.communityUuid) + homeCom = await getCommunity({ communityIdentifier: admin.communityUuid }) foreignCom1 = DbCommunity.create() foreignCom1.foreign = true diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index 6fac870b1..3162a625e 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -44,10 +44,10 @@ export class CommunityResolver { @Authorized([RIGHTS.COMMUNITIES]) @Query(() => Community) - async community(@Args() { communityIdentifier, foreign }: CommunityArgs): Promise { - const community = await getCommunity(communityIdentifier, foreign) + async community(@Args() communityArgs: CommunityArgs): Promise { + const community = await getCommunity(communityArgs) if (!community) { - throw new LogError('community not found', communityIdentifier, foreign) + throw new LogError('community not found', communityArgs) } return new Community(community) } @@ -55,7 +55,7 @@ export class CommunityResolver { @Authorized([RIGHTS.COMMUNITY_UPDATE]) @Mutation(() => Community) async updateHomeCommunity(@Args() { uuid, gmsApiKey }: EditCommunityInput): Promise { - const homeCom = await getCommunity(uuid) + const homeCom = await getCommunity({ communityIdentifier: uuid }) if (!homeCom) { throw new LogError('HomeCommunity with uuid not found: ', uuid) } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 00894ecd3..6db74f0f5 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -452,7 +452,7 @@ export class TransactionResolver { if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) { throw new LogError('X-Community sendCoins disabled per configuration!') } - const recipCom = await getCommunity(recipientCommunityIdentifier) + const recipCom = await getCommunity({ communityIdentifier: recipientCommunityIdentifier }) logger.debug('recipient commuity: ', recipCom) if (recipCom === null) { throw new LogError( diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index fffe5d6b9..0d8850181 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -2,29 +2,26 @@ import { FindOptionsWhere } from '@dbTools/typeorm' import { Community as DbCommunity } from '@entity/Community' import { isURL } from 'class-validator' +import { CommunityArgs } from '@/graphql/arg/CommunityArgs' import { LogError } from '@/server/LogError' import { isUUID4 } from '@/util/validate' -function getCommunityFindOptions( - communityIdentifier?: string | boolean | null, - foreign?: boolean | null, -): FindOptionsWhere { +function getCommunityFindOptions({ + communityIdentifier, + foreign, +}: CommunityArgs): FindOptionsWhere { if (communityIdentifier === undefined && foreign === undefined) { throw new LogError('one of communityIdentifier or foreign must be set') } const where: FindOptionsWhere = {} - // != null cover !== null and !== undefined if (communityIdentifier != null) { - if (typeof communityIdentifier === 'boolean') { - if (typeof foreign === 'boolean') { - throw new LogError('communityIdentifier cannot be boolean if foreign is set separately ') - } - where.foreign = communityIdentifier - } else if (isURL(communityIdentifier)) { + if (isURL(communityIdentifier)) { where.url = communityIdentifier } else if (isUUID4(communityIdentifier)) { where.communityUuid = communityIdentifier + } else { + where.name = communityIdentifier } } @@ -36,37 +33,23 @@ function getCommunityFindOptions( /** * Retrieves a community from the database based on the provided identifier and foreign status. - * If communityIdentifier is a string, it can represent either the URL, UUID, or name of the community. - * If communityIdentifier is a boolean, it represents the foreign status of the community. - * If foreign is provided separately and is a boolean, it represents the foreign status of the community. - * communityIdentifier and foreign cannot both be boolean - * @param communityIdentifier The identifier (URL, UUID, or name) of the community, or a boolean representing the foreign status. - * @param foreign Optional. If provided and is a boolean, it represents the foreign status of the community. + * @param communityIdentifier The identifier (URL, UUID, or name) of the community + * @param foreign Optional. If provided it represents the foreign status of the community. * @returns A promise that resolves to a DbCommunity object if found, or null if not found. */ -export async function getCommunity( - communityIdentifier?: string | boolean | null, - foreign?: boolean | null, -): Promise { - return DbCommunity.findOne({ where: getCommunityFindOptions(communityIdentifier, foreign) }) +export async function getCommunity(communityArgs: CommunityArgs): Promise { + return DbCommunity.findOne({ where: getCommunityFindOptions(communityArgs) }) } /** * Retrieves a community from the database based on the provided identifier and foreign status. - * If communityIdentifier is a string, it can represent either the URL, UUID, or name of the community. - * If communityIdentifier is a boolean, it represents the foreign status of the community. - * If foreign is provided separately and is a boolean, it represents the foreign status of the community. - * communityIdentifier and foreign cannot both be boolean - * @param communityIdentifier The identifier (URL, UUID, or name) of the community, or a boolean representing the foreign status. - * @param foreign Optional. If provided and is a boolean, it represents the foreign status of the community. + * @param communityIdentifier The identifier (URL, UUID, or name) of the community + * @param foreign Optional. If provided it represents the foreign status of the community. * @returns A promise that resolves to a DbCommunity object if found, or throw if not found. */ -export async function getCommunityOrFail( - communityIdentifier?: string | boolean | null, - foreign?: boolean | null, -): Promise { +export async function getCommunityOrFail(communityArgs: CommunityArgs): Promise { return DbCommunity.findOneOrFail({ - where: getCommunityFindOptions(communityIdentifier, foreign), + where: getCommunityFindOptions(communityArgs), }) } @@ -76,7 +59,7 @@ export async function getCommunityOrFail( * @returns A promise that resolves to true if a non-foreign community exists with the given identifier, otherwise false. */ export async function isHomeCommunity(communityIdentifier: string): Promise { - return (await getCommunity(communityIdentifier, false)) !== null + return (await getCommunity({ communityIdentifier, foreign: false })) !== null } /** @@ -84,7 +67,7 @@ export async function isHomeCommunity(communityIdentifier: string): Promise { - return getCommunityOrFail(false) + return getCommunityOrFail({ foreign: false }) } /** @@ -93,24 +76,26 @@ export async function getHomeCommunity(): Promise { * @returns A promise that resolves to the URL of the community or throw if no community with this identifier was found */ export async function getCommunityUrl(communityIdentifier: string): Promise { - return (await getCommunityOrFail(communityIdentifier)).url + return (await getCommunityOrFail({ communityIdentifier })).url } /** * Checks if a community with the given identifier exists and has an authenticatedAt property set. * @param communityIdentifier The identifier (URL, UUID, or name) of the community. - * @returns A promise that resolves to true if a community with an authenticatedAt property exists with the given identifier, otherwise false. + * @returns A promise that resolves to true if a community with an authenticatedAt property exists with the given identifier, + * otherwise false. */ export async function isCommunityAuthenticated(communityIdentifier: string): Promise { // The !! operator is used to convert the result to a boolean value. - return !!(await getCommunity(communityIdentifier))?.authenticatedAt + return !!(await getCommunity({ communityIdentifier }))?.authenticatedAt } /** * Retrieves the name of the community with the given identifier. * @param communityIdentifier The identifier (URL, UUID, or name) of the community. - * @returns A promise that resolves to the name of the community. If the community does not exist or has no name, an empty string is returned. + * @returns A promise that resolves to the name of the community. If the community does not exist or has no name, + * an empty string is returned. */ export async function getCommunityName(communityIdentifier: string): Promise { - return (await getCommunity(communityIdentifier))?.name ?? '' + return (await getCommunity({ communityIdentifier }))?.name ?? '' } From 4255c1fdfa86609dace0f36cb00b10fd22e92bd4 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 20 Feb 2024 12:47:40 +0100 Subject: [PATCH 07/15] dlt-connector use jwt token for authentication at backend --- backend/src/auth/ADMIN_RIGHTS.ts | 1 + backend/src/auth/DLT_CONNECTOR_RIGHTS.ts | 3 + backend/src/auth/RIGHTS.ts | 1 + backend/src/auth/ROLES.ts | 3 + backend/src/graphql/directive/isAuthorized.ts | 58 +++++++++++-------- backend/src/graphql/enum/RoleNames.ts | 1 + dlt-connector/.env.dist | 5 +- dlt-connector/.env.template | 2 + dlt-connector/package.json | 1 + dlt-connector/src/client/BackendClient.ts | 14 +++++ dlt-connector/src/config/index.ts | 3 +- .../community/HomeCommunity.role.ts | 15 ++++- dlt-connector/yarn.lock | 5 ++ 13 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 backend/src/auth/DLT_CONNECTOR_RIGHTS.ts diff --git a/backend/src/auth/ADMIN_RIGHTS.ts b/backend/src/auth/ADMIN_RIGHTS.ts index 79006a1de..db0b2a4d1 100644 --- a/backend/src/auth/ADMIN_RIGHTS.ts +++ b/backend/src/auth/ADMIN_RIGHTS.ts @@ -6,4 +6,5 @@ export const ADMIN_RIGHTS = [ RIGHTS.UNDELETE_USER, RIGHTS.COMMUNITY_UPDATE, RIGHTS.COMMUNITY_BY_UUID, + RIGHTS.COMMUNITY_BY_IDENTIFIER, ] diff --git a/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts b/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts new file mode 100644 index 000000000..3186082b8 --- /dev/null +++ b/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts @@ -0,0 +1,3 @@ +import { RIGHTS } from './RIGHTS' + +export const DLT_CONNECTOR_RIGHTS = [RIGHTS.COMMUNITY_BY_IDENTIFIER] diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index c95aa18fd..670302e1d 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -59,5 +59,6 @@ export enum RIGHTS { DELETE_USER = 'DELETE_USER', UNDELETE_USER = 'UNDELETE_USER', COMMUNITY_BY_UUID = 'COMMUNITY_BY_UUID', + COMMUNITY_BY_IDENTIFIER = 'COMMUNITY_BY_IDENTIFIER', COMMUNITY_UPDATE = 'COMMUNITY_UPDATE', } diff --git a/backend/src/auth/ROLES.ts b/backend/src/auth/ROLES.ts index 15ba7b263..58b127626 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -1,6 +1,7 @@ import { RoleNames } from '@/graphql/enum/RoleNames' import { ADMIN_RIGHTS } from './ADMIN_RIGHTS' +import { DLT_CONNECTOR_RIGHTS } from './DLT_CONNECTOR_RIGHTS' import { INALIENABLE_RIGHTS } from './INALIENABLE_RIGHTS' import { MODERATOR_RIGHTS } from './MODERATOR_RIGHTS' import { Role } from './Role' @@ -20,5 +21,7 @@ export const ROLE_ADMIN = new Role(RoleNames.ADMIN, [ ...ADMIN_RIGHTS, ]) +export const ROLE_DLT_CONNECTOR = new Role(RoleNames.DLT_CONNECTOR_ROLE, DLT_CONNECTOR_RIGHTS) + // TODO from database export const ROLES = [ROLE_UNAUTHORIZED, ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN] diff --git a/backend/src/graphql/directive/isAuthorized.ts b/backend/src/graphql/directive/isAuthorized.ts index 59309c91e..f3d03a539 100644 --- a/backend/src/graphql/directive/isAuthorized.ts +++ b/backend/src/graphql/directive/isAuthorized.ts @@ -6,7 +6,13 @@ import { RoleNames } from '@enum/RoleNames' import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS' import { decode, encode } from '@/auth/JWT' import { RIGHTS } from '@/auth/RIGHTS' -import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN, ROLE_MODERATOR } from '@/auth/ROLES' +import { + ROLE_UNAUTHORIZED, + ROLE_USER, + ROLE_ADMIN, + ROLE_MODERATOR, + ROLE_DLT_CONNECTOR, +} from '@/auth/ROLES' import { Context } from '@/server/context' import { LogError } from '@/server/LogError' @@ -30,31 +36,35 @@ export const isAuthorized: AuthChecker = async ({ context }, rights) => // Set context gradidoID context.gradidoID = decoded.gradidoID - // TODO - load from database dynamically & admin - maybe encode this in the token to prevent many database requests - // TODO this implementation is bullshit - two database queries cause our user identifiers are not aligned and vary between email, id and pubKey - try { - const user = await User.findOneOrFail({ - where: { gradidoID: decoded.gradidoID }, - withDeleted: true, - relations: ['emailContact', 'userRoles'], - }) - context.user = user - context.role = ROLE_USER - if (user.userRoles?.length > 0) { - switch (user.userRoles[0].role) { - case RoleNames.ADMIN: - context.role = ROLE_ADMIN - break - case RoleNames.MODERATOR: - context.role = ROLE_MODERATOR - break - default: - context.role = ROLE_USER + if (context.gradidoID === 'dlt-connector') { + context.role = ROLE_DLT_CONNECTOR + } else { + // TODO - load from database dynamically & admin - maybe encode this in the token to prevent many database requests + // TODO this implementation is bullshit - two database queries cause our user identifiers are not aligned and vary between email, id and pubKey + try { + const user = await User.findOneOrFail({ + where: { gradidoID: decoded.gradidoID }, + withDeleted: true, + relations: ['emailContact', 'userRoles'], + }) + context.user = user + context.role = ROLE_USER + if (user.userRoles?.length > 0) { + switch (user.userRoles[0].role) { + case RoleNames.ADMIN: + context.role = ROLE_ADMIN + break + case RoleNames.MODERATOR: + context.role = ROLE_MODERATOR + break + default: + context.role = ROLE_USER + } } + } catch { + // in case the database query fails (user deleted) + throw new LogError('401 Unauthorized') } - } catch { - // in case the database query fails (user deleted) - throw new LogError('401 Unauthorized') } // check for correct rights diff --git a/backend/src/graphql/enum/RoleNames.ts b/backend/src/graphql/enum/RoleNames.ts index c4a9b25cc..431154524 100644 --- a/backend/src/graphql/enum/RoleNames.ts +++ b/backend/src/graphql/enum/RoleNames.ts @@ -5,6 +5,7 @@ export enum RoleNames { USER = 'USER', MODERATOR = 'MODERATOR', ADMIN = 'ADMIN', + DLT_CONNECTOR_ROLE = 'DLT_CONNECTOR_ROLE', } registerEnumType(RoleNames, { diff --git a/dlt-connector/.env.dist b/dlt-connector/.env.dist index 6f2511c7e..50e9fe8e1 100644 --- a/dlt-connector/.env.dist +++ b/dlt-connector/.env.dist @@ -1,4 +1,4 @@ -CONFIG_VERSION=v4.2023-09-12 +CONFIG_VERSION=v6.2024-02-20 # SET LOG LEVEL AS NEEDED IN YOUR .ENV # POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal @@ -22,4 +22,5 @@ TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log DLT_CONNECTOR_PORT=6010 # Route to Backend -BACKEND_SERVER_URL=http://localhost:4000 \ No newline at end of file +BACKEND_SERVER_URL=http://localhost:4000 +JWT_SECRET=secret123 \ No newline at end of file diff --git a/dlt-connector/.env.template b/dlt-connector/.env.template index a7cbcd6ba..2e123ca81 100644 --- a/dlt-connector/.env.template +++ b/dlt-connector/.env.template @@ -1,5 +1,7 @@ CONFIG_VERSION=$DLT_CONNECTOR_CONFIG_VERSION +JWT_SECRET=$JWT_SECRET + #IOTA IOTA_API_URL=$IOTA_API_URL IOTA_COMMUNITY_ALIAS=$IOTA_COMMUNITY_ALIAS diff --git a/dlt-connector/package.json b/dlt-connector/package.json index 146a096e1..b491a7c22 100644 --- a/dlt-connector/package.json +++ b/dlt-connector/package.json @@ -34,6 +34,7 @@ "graphql-request": "^6.1.0", "graphql-scalars": "^1.22.2", "helmet": "^7.1.0", + "jose": "^5.2.2", "log4js": "^6.7.1", "nodemon": "^2.0.20", "protobufjs": "^7.2.5", diff --git a/dlt-connector/src/client/BackendClient.ts b/dlt-connector/src/client/BackendClient.ts index 2228fdaf8..0a460d116 100644 --- a/dlt-connector/src/client/BackendClient.ts +++ b/dlt-connector/src/client/BackendClient.ts @@ -6,6 +6,7 @@ import { CONFIG } from '@/config' import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { logger } from '@/logging/logger' import { LogError } from '@/server/LogError' +import { SignJWT } from 'jose' const communityByForeign = gql` query ($foreign: Boolean) { @@ -73,6 +74,7 @@ export class BackendClient { public async homeCommunityUUid(): Promise { logger.info('check home community on backend') + this.client.setHeader('token', await this.createJWTToken()) const { data, errors } = await this.client.rawRequest(communityByForeign, { foreign: false, }) @@ -85,4 +87,16 @@ export class BackendClient { communityDraft.createdAt = data.community.creationDate return communityDraft } + + private async createJWTToken(): Promise { + const secret = new TextEncoder().encode(CONFIG.JWT_SECRET) + const token = await new SignJWT({ gradidoID: 'dlt-connector', 'urn:gradido:claim': true }) + .setProtectedHeader({ alg: 'HS256' }) + .setIssuedAt() + .setIssuer('urn:gradido:issuer') + .setAudience('urn:gradido:audience') + .setExpirationTime('1m') + .sign(secret) + return token + } } diff --git a/dlt-connector/src/config/index.ts b/dlt-connector/src/config/index.ts index d39623c04..0a37d6413 100644 --- a/dlt-connector/src/config/index.ts +++ b/dlt-connector/src/config/index.ts @@ -9,13 +9,14 @@ const constants = { LOG_LEVEL: process.env.LOG_LEVEL ?? 'info', CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v5.2024-02-24', + EXPECTED: 'v6.2024-02-20', CURRENT: '', }, } const server = { PRODUCTION: process.env.NODE_ENV === 'production' ?? false, + JWT_SECRET: process.env.JWT_SECRET ?? 'secret123', } const database = { diff --git a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts index 7a4798368..bdee42ae4 100644 --- a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts +++ b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts @@ -10,6 +10,7 @@ import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { TransactionError } from '@/graphql/model/TransactionError' import { CommunityLoggingView } from '@/logging/CommunityLogging.view' import { logger } from '@/logging/logger' +import { LogError } from '@/server/LogError' import { getDataSource } from '@/typeorm/DataSource' import { CreateTransactionRecipeContext } from '../transaction/CreateTransationRecipe.context' @@ -22,7 +23,19 @@ export class HomeCommunityRole extends CommunityRole { public async create(communityDraft: CommunityDraft, topic: string): Promise { super.create(communityDraft, topic) // generate key pair for signing transactions and deriving all keys for community - const keyPair = new KeyPair(new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED ?? undefined)) + let mnemonic: Mnemonic + try { + mnemonic = new Mnemonic(CONFIG.IOTA_HOME_COMMUNITY_SEED ?? undefined) + } catch (e) { + throw new LogError( + 'error creating mnemonic for home community, please fill IOTA_HOME_COMMUNITY_SEED in .env', + { + IOTA_HOME_COMMUNITY_SEED: CONFIG.IOTA_HOME_COMMUNITY_SEED, + error: e, + }, + ) + } + const keyPair = new KeyPair(mnemonic) keyPair.fillInCommunityKeys(this.self) // create auf account and gmw account diff --git a/dlt-connector/yarn.lock b/dlt-connector/yarn.lock index 7fbe1646f..81e904cf3 100644 --- a/dlt-connector/yarn.lock +++ b/dlt-connector/yarn.lock @@ -4281,6 +4281,11 @@ jiti@^1.19.3: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.20.0.tgz#2d823b5852ee8963585c8dd8b7992ffc1ae83b42" integrity sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA== +jose@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/jose/-/jose-5.2.2.tgz#b91170e9ba6dbe609b0c0a86568f9a1fbe4335c0" + integrity sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" From e5e22c394d443857b9a9ae922870222e380599a2 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 20 Feb 2024 13:22:32 +0100 Subject: [PATCH 08/15] update test --- .../resolver/CommunityResolver.test.ts | 28 +++++++++++++++++-- .../src/graphql/resolver/CommunityResolver.ts | 2 +- backend/src/seeds/graphql/queries.ts | 6 ++-- dlt-connector/src/client/BackendClient.ts | 2 +- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index c9c76e306..5c35a8570 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -17,7 +17,7 @@ import { logger, i18n as localization } from '@test/testSetup' import { userFactory } from '@/seeds/factory/user' import { login, updateHomeCommunityQuery } from '@/seeds/graphql/mutations' -import { getCommunities, communitiesQuery, getCommunityByUuidQuery } from '@/seeds/graphql/queries' +import { getCommunities, communitiesQuery, getCommunityQuery } from '@/seeds/graphql/queries' import { peterLustig } from '@/seeds/users/peter-lustig' import { getCommunity } from './util/communities' @@ -487,10 +487,10 @@ describe('CommunityResolver', () => { await DbCommunity.insert(foreignCom2) }) - it('finds the home-community', async () => { + it('finds the home-community by uuid', async () => { await expect( query({ - query: getCommunityByUuidQuery, + query: getCommunityQuery, variables: { communityIdentifier: homeCom?.communityUuid }, }), ).resolves.toMatchObject({ @@ -509,6 +509,28 @@ describe('CommunityResolver', () => { }) }) + it('finds the home-community by foreign', async () => { + await expect( + query({ + query: getCommunityQuery, + variables: { foreign: false }, + }), + ).resolves.toMatchObject({ + data: { + community: { + id: homeCom?.id, + foreign: homeCom?.foreign, + name: homeCom?.name, + description: homeCom?.description, + url: homeCom?.url, + creationDate: homeCom?.creationDate?.toISOString(), + uuid: homeCom?.communityUuid, + authenticatedAt: homeCom?.authenticatedAt, + }, + }, + }) + }) + it('updates the home-community gmsApiKey', async () => { await expect( mutate({ diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index 3162a625e..75ac4e8bc 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -42,7 +42,7 @@ export class CommunityResolver { return dbCommunities.map((dbCom: DbCommunity) => new Community(dbCom)) } - @Authorized([RIGHTS.COMMUNITIES]) + @Authorized([RIGHTS.COMMUNITY_BY_IDENTIFIER]) @Query(() => Community) async community(@Args() communityArgs: CommunityArgs): Promise { const community = await getCommunity(communityArgs) diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 9e908d202..49dec61b5 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -134,9 +134,9 @@ export const communitiesQuery = gql` } ` -export const getCommunityByUuidQuery = gql` - query ($communityIdentifier: String!) { - community(communityIdentifier: $communityIdentifier) { +export const getCommunityQuery = gql` + query ($communityIdentifier: String, $foreign: Boolean) { + community(communityIdentifier: $communityIdentifier, foreign: $foreign) { id foreign name diff --git a/dlt-connector/src/client/BackendClient.ts b/dlt-connector/src/client/BackendClient.ts index 0a460d116..80bcaa132 100644 --- a/dlt-connector/src/client/BackendClient.ts +++ b/dlt-connector/src/client/BackendClient.ts @@ -1,12 +1,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { gql, GraphQLClient } from 'graphql-request' +import { SignJWT } from 'jose' import { CONFIG } from '@/config' import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { logger } from '@/logging/logger' import { LogError } from '@/server/LogError' -import { SignJWT } from 'jose' const communityByForeign = gql` query ($foreign: Boolean) { From 644a37384737149a2ccf8d75f1f3be229a8647a7 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 20 Feb 2024 13:58:02 +0100 Subject: [PATCH 09/15] better function name --- dlt-connector/src/client/BackendClient.ts | 15 +++++++++------ dlt-connector/src/index.ts | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/dlt-connector/src/client/BackendClient.ts b/dlt-connector/src/client/BackendClient.ts index 80bcaa132..68745fd5a 100644 --- a/dlt-connector/src/client/BackendClient.ts +++ b/dlt-connector/src/client/BackendClient.ts @@ -58,7 +58,7 @@ export class BackendClient { headers: { 'content-type': 'application/json', }, - method: 'POST', + method: 'GET', jsonSerializer: { parse: JSON.parse, stringify: JSON.stringify, @@ -72,12 +72,15 @@ export class BackendClient { return BackendClient.instance } - public async homeCommunityUUid(): Promise { + public async getHomeCommunityDraft(): Promise { logger.info('check home community on backend') - this.client.setHeader('token', await this.createJWTToken()) - const { data, errors } = await this.client.rawRequest(communityByForeign, { - foreign: false, - }) + const { data, errors } = await this.client.rawRequest( + communityByForeign, + { + foreign: false, + }, + { authorization: 'Bearer ' + (await this.createJWTToken()) }, + ) if (errors) { throw new LogError('error getting home community from backend', errors) } diff --git a/dlt-connector/src/index.ts b/dlt-connector/src/index.ts index 82bdba658..28b8b0a0e 100644 --- a/dlt-connector/src/index.ts +++ b/dlt-connector/src/index.ts @@ -22,7 +22,7 @@ async function waitForServer( try { // Make a HEAD request to the server - return await backend.homeCommunityUUid() + return await backend.getHomeCommunityDraft() } catch (error) { logger.info('Server is not reachable: ', error) } @@ -51,7 +51,7 @@ async function main() { // wait for backend server to be ready await waitForServer(backend, 1000, 8) - const communityDraft = await backend.homeCommunityUUid() + const communityDraft = await backend.getHomeCommunityDraft() const addCommunityContext = new AddCommunityContext(communityDraft) await addCommunityContext.run() } From abb8cdcf3d45d37fd64af6112364ebafc62835f4 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 21 Feb 2024 15:34:52 +0100 Subject: [PATCH 10/15] re-refactor, remove 'complex' code --- backend/src/auth/ADMIN_RIGHTS.ts | 1 + backend/src/auth/DLT_CONNECTOR_RIGHTS.ts | 2 +- backend/src/auth/RIGHTS.ts | 1 + .../resolver/CommunityResolver.test.ts | 17 ++- .../src/graphql/resolver/CommunityResolver.ts | 23 ++-- .../graphql/resolver/TransactionResolver.ts | 4 +- .../src/graphql/resolver/util/communities.ts | 101 ++++++++---------- backend/src/seeds/graphql/queries.ts | 22 +++- dlt-connector/src/client/BackendClient.ts | 20 ++-- dlt-connector/src/index.ts | 2 +- 10 files changed, 103 insertions(+), 90 deletions(-) diff --git a/backend/src/auth/ADMIN_RIGHTS.ts b/backend/src/auth/ADMIN_RIGHTS.ts index db0b2a4d1..e95935fd0 100644 --- a/backend/src/auth/ADMIN_RIGHTS.ts +++ b/backend/src/auth/ADMIN_RIGHTS.ts @@ -7,4 +7,5 @@ export const ADMIN_RIGHTS = [ RIGHTS.COMMUNITY_UPDATE, RIGHTS.COMMUNITY_BY_UUID, RIGHTS.COMMUNITY_BY_IDENTIFIER, + RIGHTS.HOME_COMMUNITY, ] diff --git a/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts b/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts index 3186082b8..399b7c2d4 100644 --- a/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts +++ b/backend/src/auth/DLT_CONNECTOR_RIGHTS.ts @@ -1,3 +1,3 @@ import { RIGHTS } from './RIGHTS' -export const DLT_CONNECTOR_RIGHTS = [RIGHTS.COMMUNITY_BY_IDENTIFIER] +export const DLT_CONNECTOR_RIGHTS = [RIGHTS.COMMUNITY_BY_IDENTIFIER, RIGHTS.HOME_COMMUNITY] diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 670302e1d..c8f02976b 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -60,5 +60,6 @@ export enum RIGHTS { UNDELETE_USER = 'UNDELETE_USER', COMMUNITY_BY_UUID = 'COMMUNITY_BY_UUID', COMMUNITY_BY_IDENTIFIER = 'COMMUNITY_BY_IDENTIFIER', + HOME_COMMUNITY = 'HOME_COMMUNITY', COMMUNITY_UPDATE = 'COMMUNITY_UPDATE', } diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index 5c35a8570..6b4da54cf 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -17,10 +17,10 @@ import { logger, i18n as localization } from '@test/testSetup' import { userFactory } from '@/seeds/factory/user' import { login, updateHomeCommunityQuery } from '@/seeds/graphql/mutations' -import { getCommunities, communitiesQuery, getCommunityQuery } from '@/seeds/graphql/queries' +import { getCommunities, communitiesQuery, getHomeCommunityQuery, getCommunityByIdentifierQuery } from '@/seeds/graphql/queries' import { peterLustig } from '@/seeds/users/peter-lustig' -import { getCommunity } from './util/communities' +import { getCommunityByUuid } from './util/communities' // to do: We need a setup for the tests that closes the connection let mutate: ApolloServerTestClient['mutate'], @@ -460,7 +460,7 @@ describe('CommunityResolver', () => { await mutate({ mutation: login, variables: peterLoginData }) // HomeCommunity is still created in userFactory - homeCom = await getCommunity({ communityIdentifier: admin.communityUuid }) + homeCom = await getCommunityByUuid(admin.communityUuid) foreignCom1 = DbCommunity.create() foreignCom1.foreign = true @@ -490,12 +490,12 @@ describe('CommunityResolver', () => { it('finds the home-community by uuid', async () => { await expect( query({ - query: getCommunityQuery, + query: getCommunityByIdentifierQuery, variables: { communityIdentifier: homeCom?.communityUuid }, }), ).resolves.toMatchObject({ data: { - community: { + communityByIdentifier: { id: homeCom?.id, foreign: homeCom?.foreign, name: homeCom?.name, @@ -509,15 +509,14 @@ describe('CommunityResolver', () => { }) }) - it('finds the home-community by foreign', async () => { + it('finds the home-community', async () => { await expect( query({ - query: getCommunityQuery, - variables: { foreign: false }, + query: getHomeCommunityQuery, }), ).resolves.toMatchObject({ data: { - community: { + homeCommunity: { id: homeCom?.id, foreign: homeCom?.foreign, name: homeCom?.name, diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index 75ac4e8bc..951a8dcb0 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -1,9 +1,8 @@ import { IsNull, Not } from '@dbTools/typeorm' import { Community as DbCommunity } from '@entity/Community' import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' -import { Resolver, Query, Authorized, Mutation, Args } from 'type-graphql' +import { Resolver, Query, Authorized, Mutation, Args, Arg } from 'type-graphql' -import { CommunityArgs } from '@arg//CommunityArgs' import { EditCommunityInput } from '@input/EditCommunityInput' import { Community } from '@model/Community' import { FederatedCommunity } from '@model/FederatedCommunity' @@ -11,7 +10,7 @@ import { FederatedCommunity } from '@model/FederatedCommunity' import { RIGHTS } from '@/auth/RIGHTS' import { LogError } from '@/server/LogError' -import { getCommunity } from './util/communities' +import { getCommunityByIdentifier, getCommunityByUuid, getHomeCommunity } from './util/communities' @Resolver() export class CommunityResolver { @@ -44,10 +43,20 @@ export class CommunityResolver { @Authorized([RIGHTS.COMMUNITY_BY_IDENTIFIER]) @Query(() => Community) - async community(@Args() communityArgs: CommunityArgs): Promise { - const community = await getCommunity(communityArgs) + async communityByIdentifier(@Arg('communityIdentifier') communityIdentifier: string): Promise { + const community = await getCommunityByIdentifier(communityIdentifier) if (!community) { - throw new LogError('community not found', communityArgs) + throw new LogError('community not found', communityIdentifier) + } + return new Community(community) + } + + @Authorized([RIGHTS.HOME_COMMUNITY]) + @Query(() => Community) + async homeCommunity(): Promise { + const community = await getHomeCommunity() + if (!community) { + throw new LogError('no home community exist') } return new Community(community) } @@ -55,7 +64,7 @@ export class CommunityResolver { @Authorized([RIGHTS.COMMUNITY_UPDATE]) @Mutation(() => Community) async updateHomeCommunity(@Args() { uuid, gmsApiKey }: EditCommunityInput): Promise { - const homeCom = await getCommunity({ communityIdentifier: uuid }) + const homeCom = await getCommunityByUuid(uuid) if (!homeCom) { throw new LogError('HomeCommunity with uuid not found: ', uuid) } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 6db74f0f5..56c928995 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -38,7 +38,7 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' -import { getCommunity, getCommunityName, isHomeCommunity } from './util/communities' +import { getCommunityByIdentifier, getCommunityName, isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' @@ -452,7 +452,7 @@ export class TransactionResolver { if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) { throw new LogError('X-Community sendCoins disabled per configuration!') } - const recipCom = await getCommunity({ communityIdentifier: recipientCommunityIdentifier }) + const recipCom = await getCommunityByIdentifier(recipientCommunityIdentifier) logger.debug('recipient commuity: ', recipCom) if (recipCom === null) { throw new LogError( diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index 0d8850181..790a4d67c 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -1,56 +1,14 @@ -import { FindOptionsWhere } from '@dbTools/typeorm' +import { FindOneOptions } from '@dbTools/typeorm' import { Community as DbCommunity } from '@entity/Community' -import { isURL } from 'class-validator' -import { CommunityArgs } from '@/graphql/arg/CommunityArgs' -import { LogError } from '@/server/LogError' -import { isUUID4 } from '@/util/validate' - -function getCommunityFindOptions({ - communityIdentifier, - foreign, -}: CommunityArgs): FindOptionsWhere { - if (communityIdentifier === undefined && foreign === undefined) { - throw new LogError('one of communityIdentifier or foreign must be set') +function findWithCommunityIdentifier(communityIdentifier: string): FindOneOptions { + return { + where: [ + { communityUuid: communityIdentifier }, + { name: communityIdentifier }, + { url: communityIdentifier }, + ], } - - const where: FindOptionsWhere = {} - if (communityIdentifier != null) { - if (isURL(communityIdentifier)) { - where.url = communityIdentifier - } else if (isUUID4(communityIdentifier)) { - where.communityUuid = communityIdentifier - } else { - where.name = communityIdentifier - } - } - - if (typeof foreign === 'boolean') { - where.foreign = foreign - } - return where -} - -/** - * Retrieves a community from the database based on the provided identifier and foreign status. - * @param communityIdentifier The identifier (URL, UUID, or name) of the community - * @param foreign Optional. If provided it represents the foreign status of the community. - * @returns A promise that resolves to a DbCommunity object if found, or null if not found. - */ -export async function getCommunity(communityArgs: CommunityArgs): Promise { - return DbCommunity.findOne({ where: getCommunityFindOptions(communityArgs) }) -} - -/** - * Retrieves a community from the database based on the provided identifier and foreign status. - * @param communityIdentifier The identifier (URL, UUID, or name) of the community - * @param foreign Optional. If provided it represents the foreign status of the community. - * @returns A promise that resolves to a DbCommunity object if found, or throw if not found. - */ -export async function getCommunityOrFail(communityArgs: CommunityArgs): Promise { - return DbCommunity.findOneOrFail({ - where: getCommunityFindOptions(communityArgs), - }) } /** @@ -59,7 +17,15 @@ export async function getCommunityOrFail(communityArgs: CommunityArgs): Promise< * @returns A promise that resolves to true if a non-foreign community exists with the given identifier, otherwise false. */ export async function isHomeCommunity(communityIdentifier: string): Promise { - return (await getCommunity({ communityIdentifier, foreign: false })) !== null + // The !! operator in JavaScript or TypeScript is a shorthand for converting a value to a boolean. + // It essentially converts any truthy value to true and any falsy value to false. + return !!(await DbCommunity.findOne({ + where: [ + { foreign: false, communityUuid: communityIdentifier }, + { foreign: false, name: communityIdentifier }, + { foreign: false, url: communityIdentifier }, + ], + })) } /** @@ -67,35 +33,56 @@ export async function isHomeCommunity(communityIdentifier: string): Promise { - return getCommunityOrFail({ foreign: false }) + return await DbCommunity.findOneOrFail({ + where: [{ foreign: false }], + }) } /** + * TODO: Check if it is needed, because currently it isn't used at all * Retrieves the URL of the community with the given identifier. * @param communityIdentifier The identifier (URL, UUID, or name) of the community. * @returns A promise that resolves to the URL of the community or throw if no community with this identifier was found */ export async function getCommunityUrl(communityIdentifier: string): Promise { - return (await getCommunityOrFail({ communityIdentifier })).url + return (await DbCommunity.findOneOrFail(findWithCommunityIdentifier(communityIdentifier))).url } /** + * TODO: Check if it is needed, because currently it isn't used at all * Checks if a community with the given identifier exists and has an authenticatedAt property set. * @param communityIdentifier The identifier (URL, UUID, or name) of the community. * @returns A promise that resolves to true if a community with an authenticatedAt property exists with the given identifier, * otherwise false. */ export async function isCommunityAuthenticated(communityIdentifier: string): Promise { - // The !! operator is used to convert the result to a boolean value. - return !!(await getCommunity({ communityIdentifier }))?.authenticatedAt + // The !! operator in JavaScript or TypeScript is a shorthand for converting a value to a boolean. + // It essentially converts any truthy value to true and any falsy value to false. + return !!(await DbCommunity.findOne(findWithCommunityIdentifier(communityIdentifier))) + ?.authenticatedAt } /** * Retrieves the name of the community with the given identifier. - * @param communityIdentifier The identifier (URL, UUID, or name) of the community. + * @param communityIdentifier The identifier (URL, UUID) of the community. * @returns A promise that resolves to the name of the community. If the community does not exist or has no name, * an empty string is returned. */ export async function getCommunityName(communityIdentifier: string): Promise { - return (await getCommunity({ communityIdentifier }))?.name ?? '' + const community = await DbCommunity.findOne({ + where: [{ communityUuid: communityIdentifier }, { url: communityIdentifier }], + }) + + return community?.name ? community.name : '' +} +export async function getCommunityByUuid(communityUuid: string): Promise { + return await DbCommunity.findOne({ + where: [{ communityUuid }], + }) +} + +export async function getCommunityByIdentifier( + communityIdentifier: string, +): Promise { + return await DbCommunity.findOne(findWithCommunityIdentifier(communityIdentifier)) } diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 49dec61b5..0afbc2db2 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -134,9 +134,25 @@ export const communitiesQuery = gql` } ` -export const getCommunityQuery = gql` - query ($communityIdentifier: String, $foreign: Boolean) { - community(communityIdentifier: $communityIdentifier, foreign: $foreign) { +export const getCommunityByIdentifierQuery = gql` + query ($communityIdentifier: String!) { + communityByIdentifier(communityIdentifier: $communityIdentifier) { + id + foreign + name + description + url + creationDate + uuid + authenticatedAt + gmsApiKey + } + } +` + +export const getHomeCommunityQuery = gql` + query { + homeCommunity { id foreign name diff --git a/dlt-connector/src/client/BackendClient.ts b/dlt-connector/src/client/BackendClient.ts index 68745fd5a..77356f5d8 100644 --- a/dlt-connector/src/client/BackendClient.ts +++ b/dlt-connector/src/client/BackendClient.ts @@ -8,9 +8,9 @@ import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { logger } from '@/logging/logger' import { LogError } from '@/server/LogError' -const communityByForeign = gql` - query ($foreign: Boolean) { - community(foreign: $foreign) { +const homeCommunity = gql` + query { + homeCommunity { uuid foreign creationDate @@ -18,7 +18,7 @@ const communityByForeign = gql` } ` interface Community { - community: { + homeCommunity: { uuid: string foreign: boolean creationDate: string @@ -75,19 +75,19 @@ export class BackendClient { public async getHomeCommunityDraft(): Promise { logger.info('check home community on backend') const { data, errors } = await this.client.rawRequest( - communityByForeign, + homeCommunity, + {}, { - foreign: false, + authorization: 'Bearer ' + (await this.createJWTToken()), }, - { authorization: 'Bearer ' + (await this.createJWTToken()) }, ) if (errors) { throw new LogError('error getting home community from backend', errors) } const communityDraft = new CommunityDraft() - communityDraft.uuid = data.community.uuid - communityDraft.foreign = data.community.foreign - communityDraft.createdAt = data.community.creationDate + communityDraft.uuid = data.homeCommunity.uuid + communityDraft.foreign = data.homeCommunity.foreign + communityDraft.createdAt = data.homeCommunity.creationDate return communityDraft } diff --git a/dlt-connector/src/index.ts b/dlt-connector/src/index.ts index 28b8b0a0e..bec35e1df 100644 --- a/dlt-connector/src/index.ts +++ b/dlt-connector/src/index.ts @@ -49,7 +49,7 @@ async function main() { throw new LogError('cannot create backend client') } // wait for backend server to be ready - await waitForServer(backend, 1000, 8) + await waitForServer(backend, 2500, 10) const communityDraft = await backend.getHomeCommunityDraft() const addCommunityContext = new AddCommunityContext(communityDraft) From 6ba5d4b63511009f3c70aa2d98df665f46994f5f Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 21 Feb 2024 15:41:12 +0100 Subject: [PATCH 11/15] fix linting --- backend/src/graphql/resolver/CommunityResolver.test.ts | 7 ++++++- backend/src/graphql/resolver/CommunityResolver.ts | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index 6b4da54cf..25c5bfcf7 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -17,7 +17,12 @@ import { logger, i18n as localization } from '@test/testSetup' import { userFactory } from '@/seeds/factory/user' import { login, updateHomeCommunityQuery } from '@/seeds/graphql/mutations' -import { getCommunities, communitiesQuery, getHomeCommunityQuery, getCommunityByIdentifierQuery } from '@/seeds/graphql/queries' +import { + getCommunities, + communitiesQuery, + getHomeCommunityQuery, + getCommunityByIdentifierQuery, +} from '@/seeds/graphql/queries' import { peterLustig } from '@/seeds/users/peter-lustig' import { getCommunityByUuid } from './util/communities' diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index 951a8dcb0..eb320586e 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -43,7 +43,9 @@ export class CommunityResolver { @Authorized([RIGHTS.COMMUNITY_BY_IDENTIFIER]) @Query(() => Community) - async communityByIdentifier(@Arg('communityIdentifier') communityIdentifier: string): Promise { + async communityByIdentifier( + @Arg('communityIdentifier') communityIdentifier: string, + ): Promise { const community = await getCommunityByIdentifier(communityIdentifier) if (!community) { throw new LogError('community not found', communityIdentifier) From 0c96b974571d90a2b5c0e0737c8270bb4a1a144b Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 22 Feb 2024 09:06:42 +0100 Subject: [PATCH 12/15] fix lint --- dlt-connector/src/index.ts | 2 +- .../interactions/backendToDb/community/HomeCommunity.role.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dlt-connector/src/index.ts b/dlt-connector/src/index.ts index 59e6782aa..bfbff10c3 100644 --- a/dlt-connector/src/index.ts +++ b/dlt-connector/src/index.ts @@ -5,12 +5,12 @@ import { CONFIG } from '@/config' import { BackendClient } from './client/BackendClient' import { CommunityRepository } from './data/Community.repository' +import { Mnemonic } from './data/Mnemonic' import { CommunityDraft } from './graphql/input/CommunityDraft' import { AddCommunityContext } from './interactions/backendToDb/community/AddCommunity.context' import { logger } from './logging/logger' import createServer from './server/createServer' import { LogError } from './server/LogError' -import { Mnemonic } from './data/Mnemonic' import { stopTransmitToIota, transmitToIota } from './tasks/transmitToIota' async function waitForServer( diff --git a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts index 31ecb8fc6..5d7bec94c 100644 --- a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts +++ b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts @@ -12,12 +12,12 @@ import { TransactionError } from '@/graphql/model/TransactionError' import { CommunityLoggingView } from '@/logging/CommunityLogging.view' import { logger } from '@/logging/logger' import { InterruptiveSleepManager } from '@/manager/InterruptiveSleepManager' +import { LogError } from '@/server/LogError' import { getDataSource } from '@/typeorm/DataSource' import { CreateTransactionRecipeContext } from '../transaction/CreateTransationRecipe.context' import { CommunityRole } from './Community.role' -import { LogError } from '@/server/LogError' export class HomeCommunityRole extends CommunityRole { private transactionRecipe: Transaction From 15d12f1641f9a4a23e301cc1b5239156b39e17b1 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 22 Feb 2024 09:09:36 +0100 Subject: [PATCH 13/15] lower coverage --- dlt-connector/jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlt-connector/jest.config.js b/dlt-connector/jest.config.js index 3d731787f..9b5a01350 100644 --- a/dlt-connector/jest.config.js +++ b/dlt-connector/jest.config.js @@ -6,7 +6,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { - lines: 75, + lines: 72, }, }, setupFiles: ['/test/testSetup.ts'], From d3c65cd7f86f7eb7c28a6a5c8d5b2bd03f9f22d0 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 23 Feb 2024 09:18:43 +0100 Subject: [PATCH 14/15] make nginx restart automatic on failure --- deployment/hetzner_cloud/install.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/deployment/hetzner_cloud/install.sh b/deployment/hetzner_cloud/install.sh index 06b92ecaf..b51f9b454 100755 --- a/deployment/hetzner_cloud/install.sh +++ b/deployment/hetzner_cloud/install.sh @@ -104,6 +104,23 @@ ln -s $SCRIPT_PATH/nginx/common /etc/nginx/ rmdir /etc/nginx/conf.d ln -s $SCRIPT_PATH/nginx/conf.d /etc/nginx/ +# Make nginx restart automatic +mkdir /etc/systemd/system/nginx.service.d +# Define the content to be put into the override.conf file +CONFIG_CONTENT="[Unit] +StartLimitIntervalSec=500 +StartLimitBurst=5 + +[Service] +Restart=on-failure +RestartSec=5s" + +# Write the content to the override.conf file +echo "$CONFIG_CONTENT" | sudo tee /etc/systemd/system/nginx.service.d/override.conf >/dev/null + +# Reload systemd to apply the changes +sudo systemctl daemon-reload + # setup https with certbot certbot certonly --nginx --non-interactive --agree-tos --domains $COMMUNITY_HOST --email $COMMUNITY_SUPPORT_MAIL From 43fee125147a5eeb2e1258ecb93284f8bf8fdbb9 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 29 Feb 2024 13:40:18 +0100 Subject: [PATCH 15/15] update role name --- backend/src/auth/ROLES.ts | 2 +- backend/src/graphql/enum/RoleNames.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/auth/ROLES.ts b/backend/src/auth/ROLES.ts index 58b127626..75d31d149 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -21,7 +21,7 @@ export const ROLE_ADMIN = new Role(RoleNames.ADMIN, [ ...ADMIN_RIGHTS, ]) -export const ROLE_DLT_CONNECTOR = new Role(RoleNames.DLT_CONNECTOR_ROLE, DLT_CONNECTOR_RIGHTS) +export const ROLE_DLT_CONNECTOR = new Role(RoleNames.DLT_CONNECTOR, DLT_CONNECTOR_RIGHTS) // TODO from database export const ROLES = [ROLE_UNAUTHORIZED, ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN] diff --git a/backend/src/graphql/enum/RoleNames.ts b/backend/src/graphql/enum/RoleNames.ts index 431154524..1eb8f22c3 100644 --- a/backend/src/graphql/enum/RoleNames.ts +++ b/backend/src/graphql/enum/RoleNames.ts @@ -5,7 +5,7 @@ export enum RoleNames { USER = 'USER', MODERATOR = 'MODERATOR', ADMIN = 'ADMIN', - DLT_CONNECTOR_ROLE = 'DLT_CONNECTOR_ROLE', + DLT_CONNECTOR = 'DLT_CONNECTOR_ROLE', } registerEnumType(RoleNames, {