-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
✨ FEAT. Webhook 요청 응답 기능 추가
- Loading branch information
Showing
7 changed files
with
359 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 12 additions & 8 deletions
20
src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapApiService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
163 changes: 139 additions & 24 deletions
163
src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapApiServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
95
src/main/java/fairytale/tbd/domain/faceSwap/util/CryptoUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.