From 47e94c167164d8102c18f0171f63b9944945aaf2 Mon Sep 17 00:00:00 2001 From: clauspeterhuebner Date: Tue, 27 Jan 2026 02:10:57 +0100 Subject: [PATCH] sendEmail per CommandPattern --- backend/src/graphql/schema.ts | 2 + core/src/command/BaseCommand.ts | 15 ++++ core/src/command/Command.ts | 4 + core/src/command/CommandExecutor.ts | 68 +++++++++++++++++ core/src/command/CommandFactory.ts | 28 +++++++ core/src/command/CommandRegistry.ts | 24 ++++++ core/src/command/commands/ExampleCommands.ts | 26 +++++++ core/src/command/commands/SendEmailCommand.ts | 75 +++++++++++++++++++ core/src/command/initCommands.ts | 11 +++ .../federation/client/1_0/CommandClient.ts | 44 +++++++++++ .../client/1_0/query/sendCommand.ts | 11 +++ .../federation/client/1_1/CommandClient.ts | 3 + .../federation/client/CommandClientFactory.ts | 57 ++++++++++++++ core/src/graphql/logic/processCommand.ts | 10 +++ .../src/graphql/logic/processXComSendCoins.ts | 34 +++++++++ core/src/graphql/model/CommandResult.ts | 13 ++++ core/src/graphql/resolver/CommandResolver.ts | 19 +++++ core/src/index.ts | 9 +++ federation/src/graphql/api/1_0/schema.ts | 2 + federation/src/index.ts | 4 + shared/src/index.ts | 1 + .../jwt/payloadtypes/CommandJwtPayloadType.ts | 17 +++++ 22 files changed, 477 insertions(+) create mode 100644 core/src/command/BaseCommand.ts create mode 100644 core/src/command/Command.ts create mode 100644 core/src/command/CommandExecutor.ts create mode 100644 core/src/command/CommandFactory.ts create mode 100644 core/src/command/CommandRegistry.ts create mode 100644 core/src/command/commands/ExampleCommands.ts create mode 100644 core/src/command/commands/SendEmailCommand.ts create mode 100644 core/src/command/initCommands.ts create mode 100644 core/src/federation/client/1_0/CommandClient.ts create mode 100644 core/src/federation/client/1_0/query/sendCommand.ts create mode 100644 core/src/federation/client/1_1/CommandClient.ts create mode 100644 core/src/federation/client/CommandClientFactory.ts create mode 100644 core/src/graphql/logic/processCommand.ts create mode 100644 core/src/graphql/model/CommandResult.ts create mode 100644 core/src/graphql/resolver/CommandResolver.ts create mode 100644 shared/src/jwt/payloadtypes/CommandJwtPayloadType.ts diff --git a/backend/src/graphql/schema.ts b/backend/src/graphql/schema.ts index bebc3dbda..77e0d8469 100644 --- a/backend/src/graphql/schema.ts +++ b/backend/src/graphql/schema.ts @@ -6,6 +6,7 @@ import { buildSchema } from 'type-graphql' import { isAuthorized } from './directive/isAuthorized' import { AiChatResolver } from './resolver/AiChatResolver' import { BalanceResolver } from './resolver/BalanceResolver' +import { CommandResolver } from 'core/src/graphql/resolver/CommandResolver' import { CommunityResolver } from './resolver/CommunityResolver' import { ContributionLinkResolver } from './resolver/ContributionLinkResolver' import { ContributionMessageResolver } from './resolver/ContributionMessageResolver' @@ -25,6 +26,7 @@ export const schema = async (): Promise => { resolvers: [ AiChatResolver, BalanceResolver, + CommandResolver, CommunityResolver, ContributionLinkResolver, ContributionMessageResolver, diff --git a/core/src/command/BaseCommand.ts b/core/src/command/BaseCommand.ts new file mode 100644 index 000000000..a2c75d902 --- /dev/null +++ b/core/src/command/BaseCommand.ts @@ -0,0 +1,15 @@ +import { Command } from './Command'; + +export abstract class BaseCommand implements Command { + protected constructor(protected readonly params: any = {}) {} + + abstract execute(): Promise; + + validate(): boolean { + return true; // Default implementation, override in subclasses + } + + protected validateParams(requiredParams: string[]): boolean { + return requiredParams.every(param => this.params[param] !== undefined); + } +} diff --git a/core/src/command/Command.ts b/core/src/command/Command.ts new file mode 100644 index 000000000..c95e45af9 --- /dev/null +++ b/core/src/command/Command.ts @@ -0,0 +1,4 @@ +export interface Command { + execute(): Promise; + validate?(): boolean; +} diff --git a/core/src/command/CommandExecutor.ts b/core/src/command/CommandExecutor.ts new file mode 100644 index 000000000..ec7209d20 --- /dev/null +++ b/core/src/command/CommandExecutor.ts @@ -0,0 +1,68 @@ +// core/src/command/CommandExecutor.ts +import { CommandJwtPayloadType } from 'shared'; +import { interpretEncryptedTransferArgs } from '../graphql/logic/interpretEncryptedTransferArgs'; +import { EncryptedTransferArgs } from '../graphql/model/EncryptedTransferArgs'; +import { BaseCommand } from './BaseCommand'; +import { Command } from './Command'; +import { getLogger } from 'log4js'; +import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const'; +import { CommandFactory } from './CommandFactory'; +import { CommandResult } from '../graphql/model/CommandResult'; + +const createLogger = (method: string) => + getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.command.CommandExecutor.${method}`) + +export class CommandExecutor { + async executeCommand(command: Command): Promise { + const methodLogger = createLogger(`executeCommand`) + try { + if (command.validate && !command.validate()) { + return { success: false, error: 'Command validation failed' }; + } + methodLogger.debug(`executeCommand() executing command=${command.constructor.name}`) + const result = await command.execute(); + methodLogger.debug(`executeCommand() executed result=${JSON.stringify(result)}`) + return { success: true, data: result }; + } catch (error) { + methodLogger.error(`executeCommand() error=${error}`) + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error occurred' + }; + } + } + + async executeEncryptedCommand( + encryptedArgs: EncryptedTransferArgs + ): Promise { + const methodLogger = createLogger(`executeEncryptedCommand`) + try { + // Decrypt the command data + const commandArgs = (await interpretEncryptedTransferArgs(encryptedArgs)) as CommandJwtPayloadType + if (!commandArgs) { + const errmsg = `invalid commandArgs payload of requesting community with publicKey=${encryptedArgs.publicKey}` + methodLogger.error(errmsg) + throw new Error(errmsg) + } + if (methodLogger.isDebugEnabled()) { + methodLogger.debug(`executeEncryptedCommand() commandArgs=${JSON.stringify(commandArgs)}`) + } + const command = CommandFactory.getInstance().createCommand(commandArgs.commandName, commandArgs.params); + + // Execute the command + const result = await this.executeCommand(command); + if (methodLogger.isDebugEnabled()) { + methodLogger.debug(`executeCommand() result=${JSON.stringify(result)}`) + } + + return result + } catch (error) { + methodLogger.error(`executeEncryptedCommand() error=${error}`) + const errorResult: CommandResult = { + success: false, + error: error instanceof Error ? error.message : 'Failed to process command' + }; + return errorResult; + } + } +} diff --git a/core/src/command/CommandFactory.ts b/core/src/command/CommandFactory.ts new file mode 100644 index 000000000..41064ed44 --- /dev/null +++ b/core/src/command/CommandFactory.ts @@ -0,0 +1,28 @@ +import { Command } from './Command'; +import { BaseCommand } from './BaseCommand'; + +export class CommandFactory { + private static instance: CommandFactory; + private commands: Map Command> = new Map(); + + private constructor() {} + + static getInstance(): CommandFactory { + if (!CommandFactory.instance) { + CommandFactory.instance = new CommandFactory(); + } + return CommandFactory.instance; + } + + registerCommand(name: string, commandClass: new (params: any) => Command): void { + this.commands.set(name, commandClass); + } + + createCommand(name: string, params: any = {}): Command { + const CommandClass = this.commands.get(name); + if (!CommandClass) { + throw new Error(`Command ${name} not found`); + } + return new CommandClass(params) as Command; + } +} diff --git a/core/src/command/CommandRegistry.ts b/core/src/command/CommandRegistry.ts new file mode 100644 index 000000000..321ca370d --- /dev/null +++ b/core/src/command/CommandRegistry.ts @@ -0,0 +1,24 @@ +// core/src/command/CommandRegistry.ts +import { ICommand } from './CommandTypes'; + +export class CommandRegistry { + private static instance: CommandRegistry; + private commands: Map ICommand> = new Map(); + + private constructor() {} + + static getInstance(): CommandRegistry { + if (!CommandRegistry.instance) { + CommandRegistry.instance = new CommandRegistry(); + } + return CommandRegistry.instance; + } + + static registerCommand(type: string, commandClass: new (params: any) => ICommand): void { + this.getInstance().commands.set(type, commandClass); + } + + static getCommandClass(type: string): (new (params: any) => ICommand) | undefined { + return this.getInstance().commands.get(type); + } +} diff --git a/core/src/command/commands/ExampleCommands.ts b/core/src/command/commands/ExampleCommands.ts new file mode 100644 index 000000000..9fcc3787e --- /dev/null +++ b/core/src/command/commands/ExampleCommands.ts @@ -0,0 +1,26 @@ +// core/src/command/commands/ExampleCommand.ts +import { BaseCommand } from '../BaseCommand'; +import { CommandRegistry } from '../CommandRegistry'; + +export interface ExampleCommandParams { + someData: string; +} + +export class ExampleCommand extends BaseCommand<{ processed: boolean }> { + constructor(params: ExampleCommandParams) { + super('EXAMPLE_COMMAND', params); + } + + validate(): boolean { + return !!this.params.someData; + } + + async execute(): Promise<{ processed: boolean }> { + // Command implementation here + console.log('Executing ExampleCommand with data:', this.params.someData); + return { processed: true }; + } +} + +// Register the command +CommandRegistry.registerCommand('EXAMPLE_COMMAND', ExampleCommand); diff --git a/core/src/command/commands/SendEmailCommand.ts b/core/src/command/commands/SendEmailCommand.ts new file mode 100644 index 000000000..f9abc51c7 --- /dev/null +++ b/core/src/command/commands/SendEmailCommand.ts @@ -0,0 +1,75 @@ +import { BaseCommand } from '../BaseCommand'; +import { sendTransactionReceivedEmail } from '../../emails/sendEmailVariants'; +import { findForeignUserByUuids, findUserByIdentifier } from 'database'; +import { LOG4JS_BASE_CATEGORY_NAME } from '../../config/const'; +import { getLogger } from 'log4js'; + +const createLogger = (method: string) => + getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.command.CommandExecutor.${method}`) + +export class SendEmailCommand extends BaseCommand<{ success: boolean }> { + static readonly SEND_MAIL_COMMAND = 'SEND_MAIL_COMMAND'; + + constructor(params: { + mailType: string, + senderComUuid: string, + senderGradidoId: string, + receiverComUuid: string, + receiverGradidoId: string, + memo?: string, + amount?: number, + }) { + super(params); + } + + validate(): boolean { + return this.validateParams(['mailType', 'senderComUuid', 'senderGradidoId', 'receiverComUuid', 'receiverGradidoId']); + } + + async execute(): Promise<{ success: boolean }> { + const methodLogger = createLogger(`execute`) + if (!this.validate()) { + throw new Error('Invalid command parameters'); + } + // find sender user + const senderUser = await findForeignUserByUuids(this.params.senderComUuid, this.params.senderGradidoId); + if (!senderUser) { + const errmsg = `Sender user not found: ${this.params.senderComUuid} ${this.params.senderGradidoId}`; + methodLogger.error(errmsg); + throw new Error(errmsg); + } + const recipientUser = await findUserByIdentifier(this.params.receiverGradidoId, this.params.receiverComUuid); + if (!recipientUser) { + const errmsg = `Recipient user not found: ${this.params.receiverComUuid} ${this.params.receiverGradidoId}`; + methodLogger.error(errmsg); + throw new Error(errmsg); + } + + const emailParams = { + firstName: recipientUser.firstName, + lastName: recipientUser.lastName, + email: recipientUser.emailContact.email, + language: recipientUser.language, + senderFirstName: senderUser.firstName, + senderLastName: senderUser.lastName, + senderEmail: senderUser.emailContact?.email, + memo: this.params.memo || '', + transactionAmount: this.params.amount || 0, + }; + switch(this.params.mailType) { + case 'sendTransactionReceivedEmail': + await sendTransactionReceivedEmail(emailParams); + break; + default: + throw new Error(`Unknown mail type: ${this.params.mailType}`); + } + + try { + // Example: const result = await emailService.sendEmail(this.params); + return { success: true }; + } catch (error) { + methodLogger.error('Error executing SendEmailCommand:', error); + throw error; + } + } +} \ No newline at end of file diff --git a/core/src/command/initCommands.ts b/core/src/command/initCommands.ts new file mode 100644 index 000000000..631335d26 --- /dev/null +++ b/core/src/command/initCommands.ts @@ -0,0 +1,11 @@ +import { CommandFactory } from './CommandFactory'; +import { SendEmailCommand } from './commands/SendEmailCommand'; +// Import other commands... + +export function initializeCommands(): void { + const factory = CommandFactory.getInstance(); + + // Register all commands + factory.registerCommand(SendEmailCommand.SEND_MAIL_COMMAND, SendEmailCommand); + // Register other commands... + } diff --git a/core/src/federation/client/1_0/CommandClient.ts b/core/src/federation/client/1_0/CommandClient.ts new file mode 100644 index 000000000..448615765 --- /dev/null +++ b/core/src/federation/client/1_0/CommandClient.ts @@ -0,0 +1,44 @@ +import { EncryptedTransferArgs, ensureUrlEndsWithSlash } from 'core' +import { FederatedCommunity as DbFederatedCommunity } from 'database' +import { GraphQLClient } from 'graphql-request' +import { getLogger } from 'log4js' +import { LOG4JS_BASE_CATEGORY_NAME } from '../../../config/const' +import { sendCommand } from './query/sendCommand' + +const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.federation.client.1_0.CommandClient`) + +export class CommandClient { + dbCom: DbFederatedCommunity + endpoint: string + client: GraphQLClient + + constructor(dbCom: DbFederatedCommunity) { + this.dbCom = dbCom + this.endpoint = ensureUrlEndsWithSlash(dbCom.endPoint).concat(dbCom.apiVersion).concat('/') + this.client = new GraphQLClient(this.endpoint, { + method: 'POST', + jsonSerializer: { + parse: JSON.parse, + stringify: JSON.stringify, + }, + }) + } + + async sendCommand(args: EncryptedTransferArgs): Promise { + logger.debug(`sendCommand at ${this.endpoint} for args:`, args) + try { + const { data } = await this.client.rawRequest<{ success: boolean }>(sendCommand, { + args, + }) + if (!data?.success) { + logger.warn('sendCommand without response data from endpoint', this.endpoint) + return false + } + logger.debug('sendCommand successfully started with endpoint', this.endpoint) + return true + } catch (err) { + logger.error('error on sendCommand: ', err) + return false + } + } +} diff --git a/core/src/federation/client/1_0/query/sendCommand.ts b/core/src/federation/client/1_0/query/sendCommand.ts new file mode 100644 index 000000000..906833c82 --- /dev/null +++ b/core/src/federation/client/1_0/query/sendCommand.ts @@ -0,0 +1,11 @@ +import { gql } from 'graphql-request' + +export const sendCommand = gql` + mutation ($args: EncryptedTransferArgs!) { + sendCommand(encryptedArgs: $args) { + success + data + error + } + } +` diff --git a/core/src/federation/client/1_1/CommandClient.ts b/core/src/federation/client/1_1/CommandClient.ts new file mode 100644 index 000000000..2d0b35152 --- /dev/null +++ b/core/src/federation/client/1_1/CommandClient.ts @@ -0,0 +1,3 @@ +import { CommandClient as V1_0_CommandClient } from 'core/src/federation/client/1_0/CommandClient' + +export class CommandClient extends V1_0_CommandClient {} diff --git a/core/src/federation/client/CommandClientFactory.ts b/core/src/federation/client/CommandClientFactory.ts new file mode 100644 index 000000000..a03fb493a --- /dev/null +++ b/core/src/federation/client/CommandClientFactory.ts @@ -0,0 +1,57 @@ +import { ApiVersionType } from 'core' +import { FederatedCommunity as DbFederatedCommunity } from 'database' +import { CommandClient as V1_0_CommandClient } from './1_0/CommandClient' +import { CommandClient as V1_1_CommandClient } from './1_1/CommandClient' + +type CommandClient = V1_0_CommandClient | V1_1_CommandClient + +interface CommandClientInstance { + id: number + + client: CommandClient +} + +export class CommandClientFactory { + private static instanceArray: CommandClientInstance[] = [] + + /** + * The Singleton's constructor should always be private to prevent direct + * construction calls with the `new` operator. + */ + + private constructor() {} + + private static createCommandClient = (dbCom: DbFederatedCommunity) => { + switch (dbCom.apiVersion) { + case ApiVersionType.V1_0: + return new V1_0_CommandClient(dbCom) + case ApiVersionType.V1_1: + return new V1_1_CommandClient(dbCom) + default: + return null + } + } + + /** + * 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(dbCom: DbFederatedCommunity): CommandClient | null { + const instance = CommandClientFactory.instanceArray.find( + (instance) => instance.id === dbCom.id, + ) + if (instance) { + return instance.client + } + const client = CommandClientFactory.createCommandClient(dbCom) + if (client) { + CommandClientFactory.instanceArray.push({ + id: dbCom.id, + client, + } as CommandClientInstance) + } + return client + } +} diff --git a/core/src/graphql/logic/processCommand.ts b/core/src/graphql/logic/processCommand.ts new file mode 100644 index 000000000..355f71892 --- /dev/null +++ b/core/src/graphql/logic/processCommand.ts @@ -0,0 +1,10 @@ +import { getLogger } from 'log4js' +import { LOG4JS_BASE_CATEGORY_NAME } from '../../config/const' + +const createLogger = (method: string) => + getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.logic.processCommand.${method}`) + +export async function processCommand(commandClass: string, commandMethod: string, commandArgs: string[]) { + const methodLogger = createLogger(`processCommand`) + methodLogger.info('processing a command...') +} diff --git a/core/src/graphql/logic/processXComSendCoins.ts b/core/src/graphql/logic/processXComSendCoins.ts index 7b2ca3dc6..345c6e406 100644 --- a/core/src/graphql/logic/processXComSendCoins.ts +++ b/core/src/graphql/logic/processXComSendCoins.ts @@ -16,6 +16,7 @@ import { Decimal } from 'decimal.js-light' // import { LogError } from '@server/LogError' import { getLogger } from 'log4js' import { + CommandJwtPayloadType, encryptAndSign, PendingTransactionState, SendCoinsJwtPayloadType, @@ -30,6 +31,7 @@ import { SendCoinsResultLoggingView } from '../../federation/client/1_0/logging/ import { SendCoinsResult } from '../../federation/client/1_0/model/SendCoinsResult' import { SendCoinsClient as V1_0_SendCoinsClient } from '../../federation/client/1_0/SendCoinsClient' import { SendCoinsClientFactory } from '../../federation/client/SendCoinsClientFactory' +import { CommandClientFactory } from '../../federation/client/CommandClientFactory' import { TransactionTypeId } from '../../graphql/enum/TransactionTypeId' import { EncryptedTransferArgs } from '../../graphql/model/EncryptedTransferArgs' import { calculateSenderBalance } from '../../util/calculateSenderBalance' @@ -37,6 +39,8 @@ import { fullName } from '../../util/utilities' import { settlePendingSenderTransaction } from './settlePendingSenderTransaction' import { storeForeignUser } from './storeForeignUser' import { storeLinkAsRedeemed } from './storeLinkAsRedeemed' +import { V1_0_CommandClient } from '../..' +import { SendEmailCommand } from '../../command/commands/SendEmailCommand' const createLogger = (method: string) => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.processXComSendCoins.${method}`) @@ -511,6 +515,36 @@ export async function processXComCommittingSendCoins( sendCoinsResult.recipGradidoID = pendingTx.linkedUserGradidoID sendCoinsResult.recipAlias = recipient.recipAlias } + // ** after successfull settle of the pending transaction on sender side we have to send a trigger to the recipient community to send an email to the x-com-tx recipient + const cmdClient = CommandClientFactory.getInstance(receiverFCom) + + if (cmdClient instanceof V1_0_CommandClient) { + const payload = new CommandJwtPayloadType( + handshakeID, + SendEmailCommand.SEND_MAIL_COMMAND, + SendEmailCommand.name, + [ + JSON.stringify({ + mailType: 'sendTransactionReceivedEmail', + senderComUuid: senderCom.communityUuid, + senderGradidoId: sender.gradidoID, + receiverComUuid: receiverCom.communityUuid, + receiverGradidoId: sendCoinsResult.recipGradidoID, + memo: pendingTx.memo, + amount: pendingTx.amount, + }), + ]) + const jws = await encryptAndSign( + payload, + senderCom.privateJwtKey!, + receiverCom.publicJwtKey!, + ) + const args = new EncryptedTransferArgs() + args.publicKey = senderCom.publicKey.toString('hex') + args.jwt = jws + args.handshakeID = handshakeID + cmdClient.sendCommand(args) + } } catch (err) { methodLogger.error( `Error in writing sender pending transaction: ${JSON.stringify(err, null, 2)}`, diff --git a/core/src/graphql/model/CommandResult.ts b/core/src/graphql/model/CommandResult.ts new file mode 100644 index 000000000..38b7e2dd4 --- /dev/null +++ b/core/src/graphql/model/CommandResult.ts @@ -0,0 +1,13 @@ +import { Field, ObjectType } from 'type-graphql' + +@ObjectType() +export class CommandResult { + @Field(() => Boolean) + success: boolean; + + @Field(() => String, { nullable: true }) + data?: any; + + @Field(() => String, { nullable: true }) + error?: string; +} diff --git a/core/src/graphql/resolver/CommandResolver.ts b/core/src/graphql/resolver/CommandResolver.ts new file mode 100644 index 000000000..eeedf9c34 --- /dev/null +++ b/core/src/graphql/resolver/CommandResolver.ts @@ -0,0 +1,19 @@ +// backend/src/graphql/resolver/CommandResolver.ts +import { Resolver, Mutation, Arg, Ctx } from 'type-graphql'; +import { CommandExecutor } from '../../command/CommandExecutor'; +import { CommandResult } from '../model/CommandResult'; + +@Resolver() +export class CommandResolver { + private commandExecutor = new CommandExecutor(); + + @Mutation(() => CommandResult) + async executeCommand( + @Arg('encryptedArgs', () => Object) encryptedArgs: any, + @Ctx() context: any + ): Promise { + // Convert to EncryptedTransferArgs if needed + const result = await this.commandExecutor.executeEncryptedCommand(encryptedArgs); + return result as unknown as CommandResult; + } +} diff --git a/core/src/index.ts b/core/src/index.ts index ddc204f5b..1c7817fb4 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -1,4 +1,7 @@ export * from './config/index' +export * from './command/CommandExecutor' +export * from './command/CommandFactory' +export * from './command/initCommands' export * from './emails' export * from './federation/client/1_0/logging/SendCoinsArgsLogging.view' export * from './federation/client/1_0/logging/SendCoinsResultLogging.view' @@ -8,18 +11,24 @@ export * from './federation/client/1_0/query/revertSendCoins' export * from './federation/client/1_0/query/revertSettledSendCoins' export * from './federation/client/1_0/query/settleSendCoins' export * from './federation/client/1_0/query/voteForSendCoins' +export { CommandClient as V1_0_CommandClient } from './federation/client/1_0/CommandClient' +export { CommandClient as V1_1_CommandClient } from './federation/client/1_1/CommandClient' export { SendCoinsClient as V1_0_SendCoinsClient } from './federation/client/1_0/SendCoinsClient' export { SendCoinsClient as V1_1_SendCoinsClient } from './federation/client/1_1/SendCoinsClient' +export * from './federation/client/CommandClientFactory' export * from './federation/client/SendCoinsClientFactory' export * from './federation/enum/apiVersionType' export * from './graphql/enum/TransactionTypeId' export * from './graphql/logging/DecayLogging.view' export * from './graphql/logic/interpretEncryptedTransferArgs' +export * from './graphql/logic/processCommand' export * from './graphql/logic/processXComSendCoins' export * from './graphql/logic/settlePendingSenderTransaction' export * from './graphql/logic/storeForeignUser' +export * from './graphql/model/CommandResult' export * from './graphql/model/Decay' export * from './graphql/model/EncryptedTransferArgs' +export * from './graphql/resolver/CommandResolver' export * from './logic' export * from './util/calculateSenderBalance' export * from './util/utilities' diff --git a/federation/src/graphql/api/1_0/schema.ts b/federation/src/graphql/api/1_0/schema.ts index 90ed13cb1..9c4741549 100644 --- a/federation/src/graphql/api/1_0/schema.ts +++ b/federation/src/graphql/api/1_0/schema.ts @@ -1,5 +1,6 @@ import { NonEmptyArray } from 'type-graphql' import { AuthenticationResolver } from './resolver/AuthenticationResolver' +import { CommandResolver } from 'core' import { DisbursementResolver } from './resolver/DisbursementResolver' import { PublicCommunityInfoResolver } from './resolver/PublicCommunityInfoResolver' import { PublicKeyResolver } from './resolver/PublicKeyResolver' @@ -8,6 +9,7 @@ import { SendCoinsResolver } from './resolver/SendCoinsResolver' export const getApiResolvers = (): NonEmptyArray => { return [ AuthenticationResolver, + CommandResolver, DisbursementResolver, PublicCommunityInfoResolver, PublicKeyResolver, diff --git a/federation/src/index.ts b/federation/src/index.ts index 421934c58..5dea4703f 100644 --- a/federation/src/index.ts +++ b/federation/src/index.ts @@ -7,6 +7,7 @@ import { onShutdown, printServerCrashAsciiArt, ShutdownReason } from 'shared' import { CONFIG } from './config' import { LOG4JS_BASE_CATEGORY_NAME } from './config/const' import { createServer } from './server/createServer' +import { initializeCommands } from 'core' async function main() { const startTime = new Date() @@ -44,6 +45,9 @@ async function main() { } }) }) + + initializeCommands() + } main().catch((e) => { diff --git a/shared/src/index.ts b/shared/src/index.ts index aff9f4ea9..ba3a5ad23 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -4,6 +4,7 @@ export * from './helper' export * from './jwt/JWT' export * from './jwt/payloadtypes/AuthenticationJwtPayloadType' export * from './jwt/payloadtypes/AuthenticationResponseJwtPayloadType' +export * from './jwt/payloadtypes/CommandJwtPayloadType' export * from './jwt/payloadtypes/DisburseJwtPayloadType' export * from './jwt/payloadtypes/EncryptedJWEJwtPayloadType' export * from './jwt/payloadtypes/JwtPayloadType' diff --git a/shared/src/jwt/payloadtypes/CommandJwtPayloadType.ts b/shared/src/jwt/payloadtypes/CommandJwtPayloadType.ts new file mode 100644 index 000000000..f41832466 --- /dev/null +++ b/shared/src/jwt/payloadtypes/CommandJwtPayloadType.ts @@ -0,0 +1,17 @@ +import { JwtPayloadType } from './JwtPayloadType' + +export class CommandJwtPayloadType extends JwtPayloadType { + static COMMAND_TYPE = 'command' + + commandName: string + commandClass: string + commandArgs: string[] + + constructor(handshakeID: string, commandName: string, commandClass: string, commandArgs: string[]) { + super(handshakeID) + this.tokentype = CommandJwtPayloadType.COMMAND_TYPE + this.commandName = commandName + this.commandClass = commandClass + this.commandArgs = commandArgs + } +}