Skip to content

Commit

Permalink
Merge pull request #9 from AElfProject/feature/google-recaptcha
Browse files Browse the repository at this point in the history
feat: added google-recaptcha verification integration in claim api
  • Loading branch information
RutvikGhaskataEalf authored Oct 31, 2024
2 parents fe940cf + 72a9ade commit 59f5b66
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 71 deletions.
74 changes: 74 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Build and Deploy Faucet Backend
on:
workflow_dispatch:
inputs:
env:
description: Environment
type: choice
required: true
default: staging
options:
- staging
- production
run-name: Build and Deploy Faucet Backend ${{ github.head_ref }} ${{ github.sha }} to ${{ inputs.env }}
env:
IMAGE_NAME: faucet-backend
jobs:
build-and-push-image:
runs-on: aelf-faucet-backend-runner
strategy:
matrix:
include:
- service: "dbmigrator"
name: "AELFFaucet.DbMigrator"
- service: "web"
name: "AELFFaucet.Web"
outputs:
short_sha: ${{ steps.vars.outputs.short_sha }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set short git commit SHA
id: vars
run: |
calculatedSha=$(git rev-parse --short ${{ github.sha }})
echo "short_sha=$calculatedSha" >> "$GITHUB_OUTPUT"
- name: Create image tag
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ secrets.REPOSITORY_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.REPOSITORY }}/${{ env.IMAGE_NAME }}-${{ matrix.service }}
tags: |
type=sha
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64
build-args: |
servicename=${{ matrix.name }}
dispatch:
runs-on: aelf-faucet-backend-runner
needs: build-and-push-image
steps:
- name: Deploy
uses: actions/github-script@v6
with:
github-token: ${{ secrets.TOK }}
script: |
await github.rest.actions.createWorkflowDispatch({
owner: 'AElfDevops',
repo: 'devops',
workflow_id: 'faucet-backend-deploy.yaml',
ref: 'main',
inputs: {
env: '${{ github.event.inputs.env }}',
commit_sha: 'sha-${{ needs.build-and-push-image.outputs.short_sha }}',
}
})
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS fetch-env
WORKDIR /App
COPY . .
RUN dotnet restore -s https://api.nuget.org/v3/index.json -s https://www.myget.org/F/aelf-project-dev/api/v3/index.json

FROM fetch-env AS build-env
WORKDIR /App
ARG servicename
RUN dotnet publish src/$servicename/$servicename.csproj -o /output

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build-env /output .
ARG servicename
ENV RUNCMD="dotnet $servicename.dll"
CMD $RUNCMD
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ This project implements a backend service that allows users to request test toke

Before you begin, ensure you have met the following requirements:

- [.NET SDK](https://dotnet.microsoft.com/download) (version 6.0 or later)
- [.NET SDK](https://dotnet.microsoft.com/download) (version 8.0 or later)
- [AElf SDK](https://github.com/AElfProject/AElf)

## Installation
Expand Down Expand Up @@ -64,14 +64,6 @@ Create a `appsettings.json` file in the AELFFaucet.Web directory of the project
"ConnectionStrings": {
"Default": "mongodb://localhost:27017/SendTokenDB"
},
"ApiConfig": {
"BaseUrl": "http://3.25.10.185:8000",
"BaseUrlForMainchain": "https://aelf-test-node.aelf.io",
"BaseUrlForSidechain": "https://tdvw-test-node.aelf.io",
"PrivateKey": "b192e307da4c4e1a00eeed442a4b5e8e0b7b2c6f838d0472627b846e5280c51c",
"SendCount": 100,
"NftSeedPrivateKey": "97a1747af1539a6b839c92227c8f5ca90ef0e7de70e5c2f46c64f27b12fabdd7"
},
"Kestrel": {
"EndPoints": {
"Http": {
Expand All @@ -88,7 +80,22 @@ Create a `appsettings.json` file in the AELFFaucet.Web directory of the project
}
}
```
Replace the PrivateKey and NftSeedPrivateKey with your actual AElf testnet node private keys for storing token pool for token distribution.

## Setup secrets.json file

Run this following command in terminal of the AELFFaucet.Web directory to generate the secrets.json file with all ENV keys:

```base title="Terminal"
dotnet user-secrets init
dotnet user-secrets set "ApiConfig:PrivateKey" "b192e30XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX280c51c"
dotnet user-secrets set "ApiConfig:NftSeedPrivateKey" "97a1747XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX12fabdd7"
dotnet user-secrets set "ApiConfig:FaucetUI" "6LcovXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKxcEW"
dotnet user-secrets set "ApiConfig:Playground" "6LfgxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXrnKGK"
dotnet user-secrets set "ApiConfig:AelfStudio" "6LfkxXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_UXyr"
dotnet user-secrets set "ConnectionStrings:Default" "mongodb://localhost:27017/SendTokenDB"
```

Replace the Value of all keys with your actual values for storing token pool for token distribution.

## Usage
Run the service:
Expand Down
6 changes: 3 additions & 3 deletions src/AELFFaucet.Application.Contracts/Project/IClaimService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace AELFFaucet.Project;

public interface IClaimService: IApplicationService
{
Task<MessageResult> ClaimTokenAsync(string walletAddress);
Task<MessageResult> ClaimSeedAsync(string walletAddress);
Task<MessageResult> ClaimNFTSeedAsync(string walletAddress);
Task<MessageResult> ClaimTokenAsync(string walletAddress, string recaptchaToken, string platform);
Task<MessageResult> ClaimSeedAsync(string walletAddress, string recaptchaToken, string platform);
Task<MessageResult> ClaimNFTSeedAsync(string walletAddress, string recaptchaToken, string platform);
}
5 changes: 5 additions & 0 deletions src/AELFFaucet.Application/ApiConfigOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@ public class ApiConfigOptions
public string PrivateKey { get; set; }
public int SendCount { get; set; }
public string NftSeedPrivateKey { get; set; }
public string RecaptchaVerifyUrl { get; set; }
public string FaucetUI { get; set; }
public string Playground { get; set; }
public string AelfStudio { get; set; }

}
7 changes: 7 additions & 0 deletions src/AELFFaucet.Application/CaptchaResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace AELFFaucet
{
public class CaptchaResponse
{
public bool success { get; set; }
}
}
12 changes: 7 additions & 5 deletions src/AELFFaucet.Application/CodeStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ namespace AELFFaucet
{
public enum CodeStatus
{
Success = 1, //成功
HadReceived = 2, //已领取过
InvalidAddress = 3, //无效地址
BalanceNotAdequate = 4, //余额不足
SystemError = 5 //系统错误
Success = 1, // (success)
HadReceived = 2, // (Already received)
InvalidAddress = 3, // (Invalid address)
BalanceNotAdequate = 4, // (Insufficient balance)
SystemError = 5, // (System error)
InvalidPlatform = 6,
InvalidCaptcha = 7
}
}
117 changes: 106 additions & 11 deletions src/AELFFaucet.Application/Project/ClaimService.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,90 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

using Microsoft.Extensions.Options;
namespace AELFFaucet.Project;

public class ClaimService : AELFFaucetAppService, IClaimService
{
private readonly ISendTokenInfoRepository _sendTokenInfoRepository;
private readonly ITokenSendContractService _otherTokenSendContractService;
private readonly ITokenSendContractService _nftSeedTokenSendContractService;

private readonly ApiConfigOptions _apiConfig;
public ClaimService(ISendTokenInfoRepository sendTokenInfoRepository,
OtherTokenSendContractService otherTokenSendContractService,
NftSeedTokenSendContractService nftSeedTokenSendContractService)
NftSeedTokenSendContractService nftSeedTokenSendContractService,
IOptions<ApiConfigOptions> apiConfig)
{
_sendTokenInfoRepository = sendTokenInfoRepository;
_otherTokenSendContractService = otherTokenSendContractService;
_nftSeedTokenSendContractService = nftSeedTokenSendContractService;
_apiConfig = apiConfig.Value;
}

// Method to retrieve the reCAPTCHA secret key based on the platform
private string GetRecaptchaSecretKey(string platform)
{
return platform switch
{
"FaucetUI" => _apiConfig.FaucetUI,
"Playground" => _apiConfig.Playground,
"AelfStudio" => _apiConfig.AelfStudio,
_ => null
};
}

private async Task<bool> VerifyRecaptchaAsync(string recaptchaToken, string secretKey)
{
var url = $"{_apiConfig.RecaptchaVerifyUrl}?secret={secretKey}&response={recaptchaToken}";

using (var client = new HttpClient())
{
var response = await client.PostAsync(url, null);
var jsonResult = await response.Content.ReadAsStringAsync();
var captchaResponse = JsonSerializer.Deserialize<CaptchaResponse>(jsonResult);

return captchaResponse.success;
}
}

[Route("api/claim")]
public async Task<MessageResult> ClaimTokenAsync(string walletAddress)
public async Task<MessageResult> ClaimTokenAsync(string walletAddress, string recaptchaToken, [FromHeader(Name = "Platform")] string platform)
{
var messageResult = new MessageResult();

if (string.IsNullOrEmpty(walletAddress))
{
messageResult.IsSuccess = false;
messageResult.Code = Convert.ToInt32(CodeStatus.InvalidAddress);
messageResult.Message = "Incorrect address formant.";
messageResult.Message = "Incorrect address format.";
return messageResult;
}

// Verify reCAPTCHA token here based on platform
var secretKey = GetRecaptchaSecretKey(platform);
if (string.IsNullOrEmpty(secretKey))
{
messageResult.IsSuccess = false;
messageResult.Code = Convert.ToInt32(CodeStatus.InvalidPlatform);
messageResult.Message = "Invalid platform specified.";
return messageResult;
}

// Verify reCAPTCHA token here
var isCaptchaValid = await VerifyRecaptchaAsync(recaptchaToken, secretKey);

if (!isCaptchaValid)
{
messageResult.IsSuccess = false;
messageResult.Code = Convert.ToInt32(CodeStatus.InvalidCaptcha);
messageResult.Message = "Invalid reCAPTCHA verification.";
return messageResult;
}

// Existing code
var chainType = ChainType.Mainchain;
if (walletAddress.Contains("ELF_"))
{
Expand All @@ -44,6 +96,7 @@ public async Task<MessageResult> ClaimTokenAsync(string walletAddress)
walletAddress = walletAddress.Split('_')[1];
}

// Rest of your existing code...
var gotTokenBefore = await _sendTokenInfoRepository.GetAsync(walletAddress.ToLower());
if (gotTokenBefore is { IsSentToken: true })
{
Expand Down Expand Up @@ -73,7 +126,6 @@ public async Task<MessageResult> ClaimTokenAsync(string walletAddress)
var sendTokenInfo = await _sendTokenInfoRepository.GetAsync(walletAddress.ToLower());
if (sendTokenInfo == null)
{
// Never get tokens before.
sendTokenInfo = new SendTokenInfo
{
WalletAddress = walletAddress,
Expand All @@ -83,7 +135,6 @@ public async Task<MessageResult> ClaimTokenAsync(string walletAddress)
}
else
{
// Get seed token before.
sendTokenInfo.IsSentToken = true;
result = await _sendTokenInfoRepository.UpdateAsync(sendTokenInfo);
}
Expand All @@ -106,17 +157,61 @@ public async Task<MessageResult> ClaimTokenAsync(string walletAddress)
}

[HttpPost("api/claim-seed")]
public async Task<MessageResult> ClaimNFTSeedAsync(string walletAddress)
public async Task<MessageResult> ClaimNFTSeedAsync(string walletAddress, string recaptchaToken, [FromHeader(Name = "Platform")] string platform)
{
var messageResult = new MessageResult();

// Verify reCAPTCHA token here based on platform
var secretKey = GetRecaptchaSecretKey(platform);
if (string.IsNullOrEmpty(secretKey))
{
messageResult.IsSuccess = false;
messageResult.Code = Convert.ToInt32(CodeStatus.InvalidPlatform);
messageResult.Message = "Invalid platform specified.";
return messageResult;
}
// Verify reCAPTCHA token here
var isCaptchaValid = await VerifyRecaptchaAsync(recaptchaToken, secretKey);

if (!isCaptchaValid)
{
messageResult.IsSuccess = false;
messageResult.Code = Convert.ToInt32(CodeStatus.InvalidCaptcha);
messageResult.Message = "Invalid reCAPTCHA verification.";
return messageResult;
}

return await ClaimSeedAsync(walletAddress, _otherTokenSendContractService, tokenInfo => tokenInfo.IsSentSeed, (tokenInfo, isSent) => tokenInfo.IsSentSeed = isSent);
}

[HttpPost("api/claim-nft-seed")]
public async Task<MessageResult> ClaimSeedAsync(string walletAddress)
public async Task<MessageResult> ClaimSeedAsync(string walletAddress, string recaptchaToken, [FromHeader(Name = "Platform")] string platform)
{
var messageResult = new MessageResult();

// Verify reCAPTCHA token here based on platform
var secretKey = GetRecaptchaSecretKey(platform);
if (string.IsNullOrEmpty(secretKey))
{
messageResult.IsSuccess = false;
messageResult.Code = Convert.ToInt32(CodeStatus.InvalidPlatform);
messageResult.Message = "Invalid platform specified.";
return messageResult;
}
// Verify reCAPTCHA token here
var isCaptchaValid = await VerifyRecaptchaAsync(recaptchaToken, secretKey);

if (!isCaptchaValid)
{
messageResult.IsSuccess = false;
messageResult.Code = Convert.ToInt32(CodeStatus.InvalidCaptcha);
messageResult.Message = "Invalid reCAPTCHA verification.";
return messageResult;
}

return await ClaimSeedAsync(walletAddress, _nftSeedTokenSendContractService, tokenInfo => tokenInfo.IsSentNftSeed, (tokenInfo, isSent) => tokenInfo.IsSentNftSeed = isSent);
}

private async Task<MessageResult> ClaimSeedAsync(string walletAddress,
ITokenSendContractService tokenSendContractService,
Func<SendTokenInfo, bool> isSentCheck,
Expand Down Expand Up @@ -187,4 +282,4 @@ private async Task<MessageResult> ClaimSeedAsync(string walletAddress,

return messageResult;
}
}
}
Loading

0 comments on commit 59f5b66

Please sign in to comment.