diff --git a/.gitignore b/.gitignore index cfc5246ae..27a104efb 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ docs/.vitepress/dist .env.local .env.*.local backend/.env +ai/.env # Log files npm-debug.log* diff --git a/backend/src/main/java/io/diveni/backend/controller/AiController.java b/backend/src/main/java/io/diveni/backend/controller/AiController.java index 2a47347c9..8cd13a2ff 100644 --- a/backend/src/main/java/io/diveni/backend/controller/AiController.java +++ b/backend/src/main/java/io/diveni/backend/controller/AiController.java @@ -1,5 +1,6 @@ package io.diveni.backend.controller; +import io.diveni.backend.dto.AiServiceResponse; import io.diveni.backend.dto.GptConfidentialData; import io.diveni.backend.service.ai.AiService; import org.slf4j.Logger; @@ -64,9 +65,9 @@ public ResponseEntity markDescription(@RequestBody GptConfidentialData d } @GetMapping("check-api-key") - public ResponseEntity checkApiKey() { + public ResponseEntity ensureServiceAndApiKey() { LOGGER.debug("--> checkApiKey()"); - ResponseEntity response = aiService.checkApiKey(); + ResponseEntity response = aiService.ensureServiceAndApiKey(); LOGGER.debug("<-- checkApiKey()"); return response; } diff --git a/backend/src/main/java/io/diveni/backend/dto/AiServiceResponse.java b/backend/src/main/java/io/diveni/backend/dto/AiServiceResponse.java new file mode 100644 index 000000000..e7f8c6d2a --- /dev/null +++ b/backend/src/main/java/io/diveni/backend/dto/AiServiceResponse.java @@ -0,0 +1,15 @@ +package io.diveni.backend.dto; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode +@Builder +public class AiServiceResponse { + + private final Boolean apiKeyValid; + + private final Boolean serviceAvailable; +} diff --git a/backend/src/main/java/io/diveni/backend/service/ai/AiService.java b/backend/src/main/java/io/diveni/backend/service/ai/AiService.java index 1097a3db7..2a355cca8 100644 --- a/backend/src/main/java/io/diveni/backend/service/ai/AiService.java +++ b/backend/src/main/java/io/diveni/backend/service/ai/AiService.java @@ -1,12 +1,15 @@ package io.diveni.backend.service.ai; import com.google.gson.Gson; +import io.diveni.backend.dto.AiServiceResponse; import io.diveni.backend.dto.GptConfidentialData; import jakarta.annotation.PostConstruct; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.*; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.beans.factory.annotation.Value; @@ -25,7 +28,8 @@ public void logConfig() { LOGGER.info("Url to Server is: " + aiUrl); } - public ResponseEntity executeRequest(String url, HttpMethod method, Object body) { + public ResponseEntity executeRequest(String url, HttpMethod method, Object body) + throws RestClientException { LOGGER.debug("--> executeRequest()"); // Create a RestTemplate object RestTemplate restTemplate = new RestTemplate(); @@ -114,11 +118,22 @@ public ResponseEntity markDescription(GptConfidentialData data) { return response; } - public ResponseEntity checkApiKey() { - LOGGER.debug("--> checkApiKey()"); - ResponseEntity response = - executeRequest(aiUrl + "/check-api-key", HttpMethod.GET, null); - LOGGER.debug("<-- checkApiKey()"); - return response; + public ResponseEntity ensureServiceAndApiKey() { + LOGGER.debug("--> ensureServiceAndApiKey()"); + AiServiceResponse result; + try { + ResponseEntity response = + executeRequest(aiUrl + "/check-api-key", HttpMethod.GET, null); + result = + AiServiceResponse.builder() + .apiKeyValid(new JSONObject(response.getBody()).getBoolean("has_api_key")) + .serviceAvailable(response.getStatusCode().is2xxSuccessful()) + .build(); + } catch (RestClientException rce) { + LOGGER.debug("AI Service is offline/unavailable: {}", rce.getMessage()); + result = AiServiceResponse.builder().apiKeyValid(false).serviceAvailable(false).build(); + } + LOGGER.debug("<-- ensureServiceAndApiKey()"); + return new ResponseEntity<>(result, HttpStatus.OK); } } diff --git a/frontend/src/services/api.service.ts b/frontend/src/services/api.service.ts index c4390577e..7f0739569 100644 --- a/frontend/src/services/api.service.ts +++ b/frontend/src/services/api.service.ts @@ -310,9 +310,20 @@ class ApiService { return response.data.description; } - public async checkApiKey() { - const response = await axios.get(`${constants.backendURL}/ai/check-api-key`); - return response.data.has_api_key; + public async ensureServiceAndApiKey() { + try { + const response = await axios.get(`${constants.backendURL}/ai/check-api-key`); + const { apiKeyValid, serviceAvailable } = response.data; + if (!serviceAvailable) { + console.log("AI Service is not available!"); + return false; + } + + return apiKeyValid; + } catch (error) { + console.log("Error checking Service or API key:", error); + return false; + } } } diff --git a/frontend/src/views/SessionPage.vue b/frontend/src/views/SessionPage.vue index e038c6384..226ab9479 100644 --- a/frontend/src/views/SessionPage.vue +++ b/frontend/src/views/SessionPage.vue @@ -551,7 +551,7 @@ export default defineComponent({ }, async created() { this.store.clearStoreWithoutUserStories(); - this.hasApiKey = await apiService.checkApiKey(); + this.hasApiKey = await apiService.ensureServiceAndApiKey(); if (!this.sessionID || !this.adminID) { //check for cookie await this.checkAdminCookie();