diff --git a/backend/src/federation/client/1_0/FederationClient.ts b/backend/src/federation/client/1_0/FederationClient.ts deleted file mode 100644 index 743d17348..000000000 --- a/backend/src/federation/client/1_0/FederationClient.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' -import { gql } from 'graphql-request' - -import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient' -import { LogError } from '@/server/LogError' -import { backendLogger as logger } from '@/server/logger' - -export async function requestGetPublicKey( - dbCom: DbFederatedCommunity, -): Promise { - let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' - endpoint = `${endpoint}${dbCom.apiVersion}/` - logger.info(`requestGetPublicKey with endpoint='${endpoint}'...`) - - const graphQLClient = GraphQLGetClient.getInstance(endpoint) - logger.debug(`graphQLClient=${JSON.stringify(graphQLClient)}`) - const query = gql` - query { - getPublicKey { - publicKey - } - } - ` - const variables = {} - - try { - const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest( - query, - variables, - ) - logger.debug(`Response-Data:`, data, errors, extensions, headers, status) - if (data) { - logger.debug(`Response-PublicKey:`, data.getPublicKey.publicKey) - logger.info(`requestGetPublicKey processed successfully`) - return data.getPublicKey.publicKey - } - logger.warn(`requestGetPublicKey processed without response data`) - } catch (err) { - throw new LogError(`Request-Error:`, err) - } -} diff --git a/backend/src/federation/client/1_0/FederationClientImpl.ts b/backend/src/federation/client/1_0/FederationClientImpl.ts new file mode 100644 index 000000000..deb2c959f --- /dev/null +++ b/backend/src/federation/client/1_0/FederationClientImpl.ts @@ -0,0 +1,83 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' +import { gql } from 'graphql-request' + +import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient' +import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' + +// eslint-disable-next-line import/no-relative-parent-imports +import { FederationClient, PublicInfo } from '../FederationClient' + +export class FederationClientImpl implements FederationClient { + public async requestGetPublicKey(dbCom: DbFederatedCommunity): Promise { + let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' + endpoint = `${endpoint}${dbCom.apiVersion}/` + logger.info(`requestGetPublicKey with endpoint='${endpoint}'...`) + + const graphQLClient = GraphQLGetClient.getInstance(endpoint) + logger.debug(`graphQLClient=${JSON.stringify(graphQLClient)}`) + const query = gql` + query { + getPublicKey { + publicKey + } + } + ` + const variables = {} + + try { + const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest( + query, + variables, + ) + logger.debug(`Response-Data:`, data, errors, extensions, headers, status) + if (data) { + logger.debug(`Response-PublicKey:`, data.getPublicKey.publicKey) + logger.info(`requestGetPublicKey processed successfully`) + return data.getPublicKey.publicKey + } + logger.warn(`requestGetPublicKey processed without response data`) + } catch (err) { + throw new LogError(`Request-Error:`, err) + } + } + + public async requestGetPublicInfo(dbCom: DbFederatedCommunity): Promise { + let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' + endpoint = `${endpoint}${dbCom.apiVersion}/` + logger.info(`requestGetPublicInfo with endpoint='${endpoint}'...`) + + const graphQLClient = GraphQLGetClient.getInstance(endpoint) + logger.debug(`graphQLClient=${JSON.stringify(graphQLClient)}`) + const query = gql` + query { + getPublicInfo { + name + description + createdAt + publicKey + } + } + ` + const variables = {} + + try { + const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest( + query, + variables, + ) + logger.debug(`Response-Data:`, data, errors, extensions, headers, status) + if (data) { + logger.debug(`Response-PublicInfo:`, data.getPublicInfo.publicInfo) + logger.info(`requestGetPublicInfo processed successfully`) + return data.getPublicInfo.publicInfo + } + logger.warn(`requestGetPublicInfo processed without response data`) + } catch (err) { + throw new LogError(`Request-Error:`, err) + } + } +} diff --git a/backend/src/federation/client/1_1/FederationClient.ts b/backend/src/federation/client/1_1/FederationClient.ts deleted file mode 100644 index 35c88bf3b..000000000 --- a/backend/src/federation/client/1_1/FederationClient.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' -import { gql } from 'graphql-request' - -import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient' -import { LogError } from '@/server/LogError' -import { backendLogger as logger } from '@/server/logger' - -export async function requestGetPublicKey( - dbCom: DbFederatedCommunity, -): Promise { - let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' - endpoint = `${endpoint}${dbCom.apiVersion}/` - logger.info(`requestGetPublicKey with endpoint='${endpoint}'...`) - - const graphQLClient = GraphQLGetClient.getInstance(endpoint) - logger.debug(`graphQLClient=${JSON.stringify(graphQLClient)}`) - const query = gql` - query { - getPublicKey { - publicKey - } - } - ` - const variables = {} - - try { - const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest( - query, - variables, - ) - logger.debug(`Response-Data:`, data, errors, extensions, headers, status) - if (data) { - logger.debug(`Response-PublicKey:`, data.getPublicKey.publicKey) - logger.info(`requestGetPublicKey processed successfully`) - return data.getPublicKey.publicKey - } - logger.warn(`requestGetPublicKey processed without response data`) - } catch (err) { - throw new LogError(`Request-Error:`, err) - } -} diff --git a/backend/src/federation/client/1_1/FederationClientImpl.ts b/backend/src/federation/client/1_1/FederationClientImpl.ts new file mode 100644 index 000000000..472edcdbd --- /dev/null +++ b/backend/src/federation/client/1_1/FederationClientImpl.ts @@ -0,0 +1,83 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' +import { gql } from 'graphql-request' + +import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient' +import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' + +// eslint-disable-next-line import/no-relative-parent-imports +import { FederationClient, PublicInfo } from '../FederationClient' + +export class FederationClientImpl implements FederationClient { + async requestGetPublicKey(dbCom: DbFederatedCommunity): Promise { + let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' + endpoint = `${endpoint}${dbCom.apiVersion}/` + logger.info(`requestGetPublicKey with endpoint='${endpoint}'...`) + + const graphQLClient = GraphQLGetClient.getInstance(endpoint) + logger.debug(`graphQLClient=${JSON.stringify(graphQLClient)}`) + const query = gql` + query { + getPublicKey { + publicKey + } + } + ` + const variables = {} + + try { + const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest( + query, + variables, + ) + logger.debug(`Response-Data:`, data, errors, extensions, headers, status) + if (data) { + logger.debug(`Response-PublicKey:`, data.getPublicKey.publicKey) + logger.info(`requestGetPublicKey processed successfully`) + return data.getPublicKey.publicKey + } + logger.warn(`requestGetPublicKey processed without response data`) + } catch (err) { + throw new LogError(`Request-Error:`, err) + } + } + + async requestGetPublicInfo(dbCom: DbFederatedCommunity): Promise { + let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' + endpoint = `${endpoint}${dbCom.apiVersion}/` + logger.info(`requestGetPublicInfo with endpoint='${endpoint}'...`) + + const graphQLClient = GraphQLGetClient.getInstance(endpoint) + logger.debug(`graphQLClient=${JSON.stringify(graphQLClient)}`) + const query = gql` + query { + getPublicInfo { + name + description + createdAt + publicKey + } + } + ` + const variables = {} + + try { + const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest( + query, + variables, + ) + logger.debug(`Response-Data:`, data, errors, extensions, headers, status) + if (data) { + logger.debug(`Response-PublicInfo:`, data.getPublicInfo.publicInfo) + logger.info(`requestGetPublicInfo processed successfully`) + return data.getPublicInfo.publicInfo + } + logger.warn(`requestGetPublicInfo processed without response data`) + } catch (err) { + throw new LogError(`Request-Error:`, err) + } + } +} diff --git a/backend/src/federation/client/FederationClient.ts b/backend/src/federation/client/FederationClient.ts new file mode 100644 index 000000000..3ddd80cfb --- /dev/null +++ b/backend/src/federation/client/FederationClient.ts @@ -0,0 +1,13 @@ +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' + +export type PublicInfo = { + name: string + description: string + createdAt: Date + publicKey: string +} + +export interface FederationClient { + requestGetPublicKey(dbCom: DbFederatedCommunity): Promise + requestGetPublicInfo(dbCom: DbFederatedCommunity): Promise +} diff --git a/backend/src/federation/validateCommunities.ts b/backend/src/federation/validateCommunities.ts index b38f38ee9..da8484318 100644 --- a/backend/src/federation/validateCommunities.ts +++ b/backend/src/federation/validateCommunities.ts @@ -1,15 +1,17 @@ -/** eslint-disable @typescript-eslint/no-unsafe-call */ /** eslint-disable @typescript-eslint/no-unsafe-assignment */ +/** eslint-disable @typescript-eslint/no-unsafe-call */ import { IsNull } from '@dbTools/typeorm' +import { Community as DbCommunity } from '@entity/Community' import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' // eslint-disable-next-line camelcase -import { requestGetPublicKey as v1_0_requestGetPublicKey } from './client/1_0/FederationClient' +import { FederationClientImpl as V1_0_FederationClientImpl } from './client/1_0/FederationClientImpl' // eslint-disable-next-line camelcase -import { requestGetPublicKey as v1_1_requestGetPublicKey } from './client/1_1/FederationClient' +import { FederationClientImpl as V1_1_FederationClientImpl } from './client/1_1/FederationClientImpl' +import { FederationClient, PublicInfo } from './client/FederationClient' import { ApiVersionType } from './enum/apiVersionType' export function startValidateCommunities(timerInterval: number): void { @@ -41,7 +43,10 @@ export async function validateCommunities(): Promise { `Federation: validate publicKey for dbCom: ${dbCom.id} with apiVersion=${dbCom.apiVersion}`, ) try { - const pubKey = await invokeVersionedRequestGetPublicKey(dbCom) + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const pubKey = await getVersionedFederationClient(dbCom.apiVersion).requestGetPublicKey( + dbCom, + ) logger.info( 'Federation: received publicKey from endpoint', pubKey, @@ -51,6 +56,17 @@ export async function validateCommunities(): Promise { logger.info(`Federation: matching publicKey: ${pubKey}`) await DbFederatedCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() }) logger.debug(`Federation: updated dbCom: ${JSON.stringify(dbCom)}`) + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const pubInfo = await getVersionedFederationClient(dbCom.apiVersion).requestGetPublicInfo( + dbCom, + ) + logger.debug(`Federation: getPublicInfo pubInfo: ${JSON.stringify(pubInfo)}`) + if (pubInfo) { + logger.info(`Federation: write foreign community...`) + await writeForeignCommunity(dbCom, pubInfo) + logger.info(`Federation: write foreign community... successfully`) + } } else { logger.warn( `Federation: received not matching publicKey -> received: ${ @@ -73,19 +89,36 @@ export async function validateCommunities(): Promise { } } +async function writeForeignCommunity( + dbCom: DbFederatedCommunity, + pubInfo: PublicInfo, +): Promise { + if (dbCom && pubInfo) { + const foreignCom = DbCommunity.create() + foreignCom.foreign = true + foreignCom.publicKey = dbCom.publicKey + foreignCom.url = dbCom.endPoint + foreignCom.name = pubInfo.name + foreignCom.description = pubInfo.description + foreignCom.creationDate = pubInfo.createdAt + await DbCommunity.save(foreignCom) + } +} + function isLogError(err: unknown) { return err instanceof LogError } -async function invokeVersionedRequestGetPublicKey( - dbCom: DbFederatedCommunity, -): Promise { - switch (dbCom.apiVersion) { +function getVersionedFederationClient(apiVersion: string): FederationClient { + switch (apiVersion) { case ApiVersionType.V1_0: - return v1_0_requestGetPublicKey(dbCom) + // eslint-disable-next-line camelcase + return new V1_0_FederationClientImpl() case ApiVersionType.V1_1: - return v1_1_requestGetPublicKey(dbCom) + // eslint-disable-next-line camelcase + return new V1_1_FederationClientImpl() default: - return undefined + // eslint-disable-next-line camelcase + return new V1_0_FederationClientImpl() } } diff --git a/federation/src/graphql/api/1_0/model/GetPublicInfoResult.ts b/federation/src/graphql/api/1_0/model/GetPublicInfoResult.ts new file mode 100644 index 000000000..102f446ba --- /dev/null +++ b/federation/src/graphql/api/1_0/model/GetPublicInfoResult.ts @@ -0,0 +1,26 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Community as DbCommunity } from '@entity/Community' +import { Field, ObjectType } from 'type-graphql' + +@ObjectType() +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export class GetPublicInfoResult { + constructor(dbCom: DbCommunity) { + this.publicKey = dbCom.publicKey.toString('hex') + this.name = dbCom.name + this.description = dbCom.description + this.createdAt = dbCom.creationDate + } + + @Field(() => String) + name: string | null + + @Field(() => String) + description: string | null + + @Field(() => Date) + createdAt: Date | null + + @Field(() => String) + publicKey: string +} diff --git a/federation/src/graphql/api/1_0/resolver/PublicInfoResolver.ts b/federation/src/graphql/api/1_0/resolver/PublicInfoResolver.ts new file mode 100644 index 000000000..ad988670b --- /dev/null +++ b/federation/src/graphql/api/1_0/resolver/PublicInfoResolver.ts @@ -0,0 +1,18 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { Query, Resolver } from 'type-graphql' +import { federationLogger as logger } from '@/server/logger' +import { Community as DbCommunity } from '@entity/Community' +import { GetPublicInfoResult } from '../model/GetPublicInfoResult' + +@Resolver() +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export class PublicInfoResolver { + @Query(() => GetPublicInfoResult) + async getPublicInfo(): Promise { + logger.debug(`getPublicInfo() via apiVersion=1_0 ...`) + const homeCom = await DbCommunity.findOneOrFail({ foreign: false }) + const result = new GetPublicInfoResult(homeCom) + logger.info(`getPublicInfo()-1_0... return publicInfo=${result}`) + return result + } +}