diff --git a/package.json b/package.json index 20f0175..313e9fb 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "build": "tsc", "format": "prettier --write \"src/**/*.ts\"", - "start": "export NODE_ENV=dev-local; export MONGODB_HOST=localhost; ts-node -r tsconfig-paths/register src/main.ts", + "start": "export NODE_ENV=dev-local; export MONGODB_HOST=localhost; export FAUCET_SECRET=s3cr3t; ts-node -r tsconfig-paths/register src/main.ts", "start:dev": "nodemon", "start:debug": "nodemon --config nodemon-debug.json", "start:provided-env": "node dist/main.js", diff --git a/src/app.module.ts b/src/app.module.ts index 9c76390..793057c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -6,6 +6,7 @@ import { ContactsModule } from './contacts/contacts.module' import { BlockchainModule } from './blockchain/blockchain.module' import { TerminusModule } from '@nestjs/terminus' import { TerminusOptionsService } from './health/terminus-options.service' +import { FaucetModule } from './faucet/faucet.module' @Module({ imports: [ @@ -14,6 +15,7 @@ import { TerminusOptionsService } from './health/terminus-options.service' MessagingModule, ContactsModule, BlockchainModule, + FaucetModule, TerminusModule.forRootAsync({ useClass: TerminusOptionsService, }), diff --git a/src/faucet/faucet.auth.guard.ts b/src/faucet/faucet.auth.guard.ts new file mode 100644 index 0000000..69c1109 --- /dev/null +++ b/src/faucet/faucet.auth.guard.ts @@ -0,0 +1,32 @@ +import { + Injectable, + CanActivate, + ExecutionContext, + Inject, +} from '@nestjs/common' +import { Observable } from 'rxjs' +import { ConfigService } from '../config/config.service' + +@Injectable() +export class FaucetAuthGuard implements CanActivate { + constructor( + @Inject('ConfigService') private readonly configService: ConfigService + ) {} + + public canActivate( + context: ExecutionContext + ): boolean | Promise | Observable { + const faucetSecret = this.configService.get('FAUCET_SECRET') + if (!faucetSecret) { + console.log( + 'No faucet secret defined. Is environment variable $FAUCET_SECRET defined?' + ) + return false + } + + const request = context.switchToHttp().getRequest() + const authorizationHeaderValue = request.headers.authorization + + return faucetSecret === authorizationHeaderValue + } +} diff --git a/src/faucet/faucet.controller.ts b/src/faucet/faucet.controller.ts new file mode 100644 index 0000000..1766bb2 --- /dev/null +++ b/src/faucet/faucet.controller.ts @@ -0,0 +1,43 @@ +import { Blockchain, Identity } from '@kiltprotocol/prototype-sdk' +import { + Controller, + Get, + Inject, + UseGuards, + Param, + Query, +} from '@nestjs/common' +import { BlockchainService } from '../blockchain/interfaces/blockchain.interfaces' +import { FaucetAuthGuard } from './faucet.auth.guard' + +@Controller('faucet') +export class FaucetController { + constructor( + @Inject('BlockchainService') + private readonly blockchainService: BlockchainService + ) {} + + /** + * Transfer a certain amount of Kilt to an account. + * + * Call with curl: + * + * curl -X GET -H "Authorization: {FAUCET_SECRET}" \ + * "http://{services-url}/faucet/transfer?receiverAddress={adress}&amount={amount}" + * + * @param receiverAddress address of the receiver account + * @param amount the amount of Kilt to tranfer + */ + @Get('transfer') + @UseGuards(FaucetAuthGuard) + public async transfer( + @Query('receiverAddress') receiverAddress: string, + @Query('amount') amount: number + ): Promise { + const blockchain: Blockchain = await this.blockchainService.connect() + const alice = Identity.buildFromURI('//Alice') + + console.log(`Transferring ${amount} tokens to ${receiverAddress}`) + await blockchain.makeTransfer(alice, receiverAddress, amount) + } +} diff --git a/src/faucet/faucet.module.ts b/src/faucet/faucet.module.ts new file mode 100644 index 0000000..b808ff1 --- /dev/null +++ b/src/faucet/faucet.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common' +import { FaucetController } from './faucet.controller' + +@Module({ + imports: [], + controllers: [FaucetController], + providers: [], +}) +export class FaucetModule {}