Skip to content

Commit

Permalink
Merge pull request #20 from Fairy-Taless/feature/#4-face
Browse files Browse the repository at this point in the history
✨ FEAT. Webhook 요청 응답 기능 추가
  • Loading branch information
junhaa authored May 7, 2024
2 parents eb53801 + eae75b0 commit 393d46d
Show file tree
Hide file tree
Showing 7 changed files with 359 additions and 104 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'org.json:json:20200518'

// webhook
implementation 'javax.xml.bind:jaxb-api:2.3.1'

// Spring Security OAUTH 2.1
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-oauth2-client'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package fairytale.tbd.domain.faceSwap.service;

import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectResponseDto;
import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapResponseDto;

public interface FaceSwapApiService {
/*FaceSwapResponseDto*/void getFaceSwapImg(FaceDetectResponseDto faceDetectResponseDto);
}
package fairytale.tbd.domain.faceSwap.service;

import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectResponseDto;
import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapRequestDto;
import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapResponseDto;

import java.io.IOException;
import java.util.Map;

public interface FaceSwapApiService {
Map<String, String> getFaceSwapImg(FaceSwapRequestDto.FaceSwapRequest faceSwapRequestDto) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,139 @@
package fairytale.tbd.domain.faceSwap.service;

import fairytale.tbd.domain.faceSwap.web.dto.FaceDetectResponseDto;
import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapRequestDto;
import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapResponseDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class FaceSwapApiServiceImpl implements FaceSwapApiService{

// 여기에서 originalCharacter, customCharacter 둘 다에 사용할 수 있게끔 한다.
@Override
@Transactional
public void getFaceSwapImg(FaceDetectResponseDto faceDetectResponseDto){
FaceSwapRequestDto.FaceSwapRequest faceSwapRequest = new FaceSwapRequestDto.FaceSwapRequest();

}
}
package fairytale.tbd.domain.faceSwap.service;

import fairytale.tbd.domain.faceSwap.web.dto.FaceSwapRequestDto;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import okhttp3.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class FaceSwapApiServiceImpl implements FaceSwapApiService{

@Value("${face.akool.apikey}")
private String apikey;

@Value("${face.akool.clientId}")
private String clientId;

private static final Logger LOGGER = LogManager.getLogger(FaceSwapApiServiceImpl.class);

private final ObjectMapper objectMapper;

@Override
@Transactional
public Map<String, String> getFaceSwapImg(FaceSwapRequestDto.FaceSwapRequest faceSwapRequest) throws IOException{

Map<String, String> apiResponse = new HashMap<>();
String customCharacterUrl = "";
String customCharacterId = "";

OkHttpClient client = new OkHttpClient().newBuilder()
.build();

MediaType mediaType = MediaType.parse("application/json");

String requestString = "{\n" +
" \"sourceImage\": [\n" +
" {\n" +
" \"path\": \"" + faceSwapRequest.getSourceImage() + "\",\n" +
" \"opts\": \"" + faceSwapRequest.getSourceImage() + "\"\n" +
" }\n" +
" ],\n" +
" \"targetImage\": [\n" +
" {\n" +
" \"path\": \"" + faceSwapRequest.getTargetImage() + "\",\n" +
" \"opts\": \"" + faceSwapRequest.getTargetImage() + "\"\n" +
" }\n" +
" ],\n" +
" \"face_enhance\": 1" + ",\n" + // 나중에 쉼표 넣기
" \"modifyImage\": \"" + faceSwapRequest.getModifyImage() + "\",\n" +
" \"webhookUrl\": \"" + faceSwapRequest.getWebhookUrl() + "\"\n" +
"}";

RequestBody body = RequestBody.create(mediaType, requestString);

Request request = new Request.Builder()
.url("https://openapi.akool.com/api/open/v3/faceswap/highquality/specifyimage")
.method("POST", body)
.addHeader("Authorization", "Bearer " + getToken())
.addHeader("Content-Type", "application/json")
.build();

try (Response response = client.newCall(request).execute()){
if(!response.isSuccessful()){
throw new IOException("Unexpected code " + response);
}

String responseData = response.body().string();

JSONObject jsonObject = new JSONObject(responseData);

int errCode = jsonObject.getInt("code");
String errorMsg = jsonObject.getString("msg");

if(errCode!=1000){
throw new IOException("Error! \n" +
"error code : " +
errCode + "\n" +
"error massage : " +
errorMsg + "\n");
}

customCharacterId = jsonObject.getJSONObject("data").getString("_id");
customCharacterUrl = jsonObject.getJSONObject("data").getString("url");

} catch (IOException e) {
e.printStackTrace();
}

apiResponse.put("customCharacterId", customCharacterId);
apiResponse.put("customCharacterUrl", customCharacterUrl);

return apiResponse;
}

public String getToken() {

OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType,
"{\r\n \"clientId\": \"" + clientId + "\" ,\r\n \"clientSecret\": \"" + apikey + "\"\r\n}");
Request request = new Request.Builder()
.url("https://openapi.akool.com/api/open/v3/getToken")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.build();
try {
Response response = client.newCall(request).execute();
String responseBody = response.body().string();
JsonNode jsonNode = objectMapper.readTree(responseBody);
String token = jsonNode.get("token").asText();
LOGGER.info("token = {}", token);
return token;
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("getToken() 요청 중 에러 발생");
throw new RuntimeException(e);
}
}
}

95 changes: 95 additions & 0 deletions src/main/java/fairytale/tbd/domain/faceSwap/util/CryptoUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package fairytale.tbd.domain.faceSwap.util;

import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;
import java.nio.charset.StandardCharsets;
import javax.xml.bind.DatatypeConverter;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class CryptoUtils {

@Value("${face.akool.apikey}")
private String clientSecret;

@Value("${face.akool.clientId}")
private String clientId;


// Generate signature
public static String generateMsgSignature(String clientId, String timestamp, String nonce, String msgEncrypt) {
String[] arr = {clientId, timestamp, nonce, msgEncrypt};
Arrays.sort(arr);
String sortedStr = String.join("", arr);
return sha1(sortedStr);
}

// SHA-1 hash function
private static String sha1(String input) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] hashBytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
return DatatypeConverter.printHexBinary(hashBytes).toLowerCase();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

// Decryption algorithm
public static String generateAesDecrypt(String dataEncrypt, String clientId, String clientSecret) {
try {
byte[] keyBytes = clientSecret.getBytes(StandardCharsets.UTF_8);
byte[] ivBytes = clientId.getBytes(StandardCharsets.UTF_8);

SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

byte[] encryptedBytes = DatatypeConverter.parseHexBinary(dataEncrypt);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

return new String(decryptedBytes, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

// Encryption algorithm
public static String generateAesEncrypt(String data, String clientId, String clientSecret) {
try {
byte[] keyBytes = clientSecret.getBytes(StandardCharsets.UTF_8);
byte[] ivBytes = clientId.getBytes(StandardCharsets.UTF_8);

SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);

byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return DatatypeConverter.printHexBinary(encryptedBytes).toLowerCase();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

// Example usage
public String getURL(String timestamp, String nonce, String msgEncrypt, String signature){
String newSignature = generateMsgSignature(clientId, timestamp, nonce, msgEncrypt);
if (signature.equals(newSignature)) {
String result = generateAesDecrypt(msgEncrypt, clientId, clientSecret);
return result;
}
return null;
}
}
Loading

0 comments on commit 393d46d

Please sign in to comment.