From 61e691829e3ac6417a250eea0d87a0597ed986dc Mon Sep 17 00:00:00 2001 From: amengus87 <100192717+amengus87@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:26:25 +0200 Subject: [PATCH] Frontend operations center begin (#208) * chore: Update RaagService to include SiloRepository (#197) * Renaming route home to operations-center * Operations Center updates * Adding fake endpoints on backends to allow auth * On 404 error, deliver index.html (SPA) * Begin using pre-commit to lint over files * chore: Refactor OpenAiCompatibleV1ApiControllerTest to improve code readability and remove unused imports * chore: Add ErrorHandlerController to handle errors and deliver index.html on 404 error (SPA) * Updating frontend dependencies * chore: Update operations center to fetch numbers on page activation * refactor: Rename header-banner.vue to infrastructure-overall.vue and card-data.vue to jvm-cards.vue * Improving how to retrieve env vars for Testing * Frontend : adding infrastructure overall * chore: Update npm dependencies to latest versions * chore: Add error handling for delivering index.html on 404 error (SPA) * refactor: Update icons in jvm-cards.vue for better visual representation * chore: Add SystemMonitoringServiceTest for system monitoring functionality --- .devcontainer/postStart.sh | 3 + .devcontainer/pre-commit | 2 + .github/workflows/build.yml | 10 +- .gitignore | 3 +- backend/.gitignore | 3 +- backend/build.gradle | 5 +- .../controller/ErrorHandlerController.java | 28 ++++ .../api/app/AuthAppApiController.java | 43 +++++ .../api/app/DashboardAppApiController.java | 62 +++++++ .../ai/dragon/dto/api/GenericApiData.java | 5 + .../ai/dragon/dto/api/GenericApiResponse.java | 7 + .../ai/dragon/dto/api/SuccessApiResponse.java | 17 ++ .../dto/api/app/auth/LoginAuthAppApiData.java | 12 ++ .../api/app/auth/UserInfoAuthAppApiData.java | 16 ++ .../dashboard/NumbersDashboardAppApiData.java | 30 ++++ .../java/ai/dragon/service/RaagService.java | 15 +- .../service/SystemMonitoringService.java | 92 +++++++++++ backend/src/main/resources/.env.local.example | 1 + backend/src/main/resources/.env.properties | 1 + .../OpenAiCompatibleV1ApiControllerTest.java | 9 +- .../java/ai/dragon/junit/AbstractTest.java | 10 +- .../service/SystemMonitoringServiceTest.java | 117 ++++++++++++++ frontend/.env | 2 +- frontend/eslint.config.js | 3 + frontend/mockoon_dRAGon.json | 67 +++++++- frontend/package.json | 29 ++-- frontend/packages/axios/package.json | 6 +- frontend/packages/axios/src/index.ts | 2 +- frontend/packages/scripts/package.json | 4 +- .../src/assets/svg-icon/mdi--cpu-64-bit.svg | 1 + .../assets/svg-icon/mdi--graph-bell-curve.svg | 1 + .../src/assets/svg-icon/mdi--land-plots.svg | 1 + .../svg-icon/mdi--layers-triple-outline.svg | 1 + frontend/src/assets/svg-icon/mdi--memory.svg | 1 + frontend/src/locales/langs/en-us.ts | 85 ++-------- frontend/src/router/elegant/imports.ts | 2 +- frontend/src/router/elegant/routes.ts | 24 +-- frontend/src/router/elegant/transform.ts | 22 +-- frontend/src/router/routes/builtin.ts | 2 +- .../src/service/api/{auth.ts => app-auth.ts} | 8 +- frontend/src/service/api/app-dashboard.ts | 9 ++ .../service/api/{route.ts => app-route.ts} | 6 +- frontend/src/service/api/backend-silo.ts | 22 +++ frontend/src/service/api/index.ts | 7 +- frontend/src/service/api/system-manage.ts | 55 ------- frontend/src/service/request/shared.ts | 4 +- frontend/src/typings/api.d.ts | 40 +++++ frontend/src/typings/app.d.ts | 81 ++-------- frontend/src/typings/components.d.ts | 8 - frontend/src/typings/elegant-router.d.ts | 10 +- frontend/src/views/home/index.vue | 43 ----- .../views/home/modules/creativity-banner.vue | 17 -- .../src/views/home/modules/header-banner.vue | 66 -------- .../src/views/home/modules/line-chart.vue | 151 ------------------ frontend/src/views/home/modules/pie-chart.vue | 109 ------------- .../src/views/home/modules/project-news.vue | 40 ----- .../src/views/operations-center/index.vue | 28 ++++ .../modules/infrastructure-overall.vue | 47 ++++++ .../modules/jvm-cards.vue} | 46 +++--- 59 files changed, 812 insertions(+), 729 deletions(-) create mode 100755 .devcontainer/pre-commit create mode 100644 backend/src/main/java/ai/dragon/controller/ErrorHandlerController.java create mode 100644 backend/src/main/java/ai/dragon/controller/api/app/AuthAppApiController.java create mode 100644 backend/src/main/java/ai/dragon/controller/api/app/DashboardAppApiController.java create mode 100644 backend/src/main/java/ai/dragon/dto/api/GenericApiData.java create mode 100644 backend/src/main/java/ai/dragon/dto/api/GenericApiResponse.java create mode 100644 backend/src/main/java/ai/dragon/dto/api/SuccessApiResponse.java create mode 100644 backend/src/main/java/ai/dragon/dto/api/app/auth/LoginAuthAppApiData.java create mode 100644 backend/src/main/java/ai/dragon/dto/api/app/auth/UserInfoAuthAppApiData.java create mode 100644 backend/src/main/java/ai/dragon/dto/api/app/dashboard/NumbersDashboardAppApiData.java create mode 100644 backend/src/main/java/ai/dragon/service/SystemMonitoringService.java create mode 100644 backend/src/main/resources/.env.local.example create mode 100644 backend/src/main/resources/.env.properties create mode 100644 backend/src/test/java/ai/dragon/service/SystemMonitoringServiceTest.java create mode 100644 frontend/src/assets/svg-icon/mdi--cpu-64-bit.svg create mode 100644 frontend/src/assets/svg-icon/mdi--graph-bell-curve.svg create mode 100644 frontend/src/assets/svg-icon/mdi--land-plots.svg create mode 100644 frontend/src/assets/svg-icon/mdi--layers-triple-outline.svg create mode 100644 frontend/src/assets/svg-icon/mdi--memory.svg rename frontend/src/service/api/{auth.ts => app-auth.ts} (77%) create mode 100644 frontend/src/service/api/app-dashboard.ts rename frontend/src/service/api/{route.ts => app-route.ts} (54%) create mode 100644 frontend/src/service/api/backend-silo.ts delete mode 100644 frontend/src/service/api/system-manage.ts delete mode 100644 frontend/src/views/home/index.vue delete mode 100644 frontend/src/views/home/modules/creativity-banner.vue delete mode 100644 frontend/src/views/home/modules/header-banner.vue delete mode 100644 frontend/src/views/home/modules/line-chart.vue delete mode 100644 frontend/src/views/home/modules/pie-chart.vue delete mode 100644 frontend/src/views/home/modules/project-news.vue create mode 100644 frontend/src/views/operations-center/index.vue create mode 100644 frontend/src/views/operations-center/modules/infrastructure-overall.vue rename frontend/src/views/{home/modules/card-data.vue => operations-center/modules/jvm-cards.vue} (72%) diff --git a/.devcontainer/postStart.sh b/.devcontainer/postStart.sh index af638012..d9653247 100755 --- a/.devcontainer/postStart.sh +++ b/.devcontainer/postStart.sh @@ -1 +1,4 @@ +cp .devcontainer/pre-commit .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit + echo "Let's go for a dRAGon ride! 🐉" \ No newline at end of file diff --git a/.devcontainer/pre-commit b/.devcontainer/pre-commit new file mode 100755 index 00000000..c0c71894 --- /dev/null +++ b/.devcontainer/pre-commit @@ -0,0 +1,2 @@ +#!/bin/sh +gradle pnpmLint checkstyleMain checkstyleTest --no-daemon --quiet diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 40598710..8df9c817 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,10 +47,16 @@ jobs: - name: Set Frontend Version run: pnpm version from-git --no-git-tag-version --prefix frontend + - name: Lint Frontend + run: gradle pnpmInstall pnpmLint + + - name: Lint Backend + run: gradle checkstyleMain checkstyleTest + - name: Test dRAGon - run: gradle pnpmInstall pnpmLint checkstyleMain checkstyleTest test --stacktrace + run: gradle test - - name: Build dRAGon + - name: Package dRAGon run: gradle bootJar -Pversion=$(gradle cV -q -Prelease.quiet) - name: Upload coverage reports to Codecov diff --git a/.gitignore b/.gitignore index 12584f5e..8023f889 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ bin .vscode .DS_Store *.class -.pnpm-store \ No newline at end of file +.pnpm-store +.env.local \ No newline at end of file diff --git a/backend/.gitignore b/backend/.gitignore index c795b054..f8686e2b 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1 +1,2 @@ -build \ No newline at end of file +build +.env.local \ No newline at end of file diff --git a/backend/build.gradle b/backend/build.gradle index 63c64358..e74b66ba 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -49,6 +49,7 @@ dependencies { implementation 'net.sourceforge.argparse4j:argparse4j:0.9.0' implementation 'org.jobrunr:jobrunr-spring-boot-3-starter:7.2.2' + implementation 'me.paulschwarz:spring-dotenv:4.0.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation 'org.awaitility:awaitility:4.2.1' @@ -130,10 +131,6 @@ tasks.register("bootProdRun") { } } -jacoco { - toolVersion = "0.8.12" -} - tasks.register('copyWebApp', Copy) { dependsOn(':frontend:pnpmBuild') from "$rootDir/frontend/dist" diff --git a/backend/src/main/java/ai/dragon/controller/ErrorHandlerController.java b/backend/src/main/java/ai/dragon/controller/ErrorHandlerController.java new file mode 100644 index 00000000..87f6260c --- /dev/null +++ b/backend/src/main/java/ai/dragon/controller/ErrorHandlerController.java @@ -0,0 +1,28 @@ +package ai.dragon.controller; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; +import org.springframework.boot.web.servlet.error.ErrorController; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class ErrorHandlerController implements ErrorController { + // We are using a SPA (Single Page Application) so we need to output the + // index.html file when an error occurs : + @RequestMapping("/error") + public @ResponseBody byte[] getImage() throws IOException { + InputStream in = getClass().getResourceAsStream("/static/index.html"); + if (in == null) { + throw new IOException("index.html not found"); + } + return IOUtils.toByteArray(in); + } + + public String getErrorPath() { + return "/error"; + } +} diff --git a/backend/src/main/java/ai/dragon/controller/api/app/AuthAppApiController.java b/backend/src/main/java/ai/dragon/controller/api/app/AuthAppApiController.java new file mode 100644 index 00000000..0f72e20f --- /dev/null +++ b/backend/src/main/java/ai/dragon/controller/api/app/AuthAppApiController.java @@ -0,0 +1,43 @@ +package ai.dragon.controller.api.app; + +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import ai.dragon.dto.api.GenericApiResponse; +import ai.dragon.dto.api.SuccessApiResponse; +import ai.dragon.dto.api.app.auth.LoginAuthAppApiData; +import ai.dragon.dto.api.app.auth.UserInfoAuthAppApiData; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +@RestController +@RequestMapping("/api/app/auth") +@Tag(name = "Authentication", description = "Authentication API Endpoints") +public class AuthAppApiController { + @PostMapping("/login") + @Operation(summary = "Login", description = "Login to the application.") + public GenericApiResponse login() { + return SuccessApiResponse.builder().data(LoginAuthAppApiData + .builder() + .token("TOKEN_WILL_BE_HERE") + .refreshToken("REFRESH_TOKEN_WILL_BE_HERE") + .build()) + .build(); + } + + @GetMapping("/getUserInfo") + @Operation(summary = "Get User Info", description = "Get user info.") + public GenericApiResponse getUserInfo() { + return SuccessApiResponse.builder().data(UserInfoAuthAppApiData + .builder() + .userId("TODO") + .userName("dRAGon") + .roles(List.of("R_SUPER")) + .build()) + .build(); + } +} diff --git a/backend/src/main/java/ai/dragon/controller/api/app/DashboardAppApiController.java b/backend/src/main/java/ai/dragon/controller/api/app/DashboardAppApiController.java new file mode 100644 index 00000000..fdc77cad --- /dev/null +++ b/backend/src/main/java/ai/dragon/controller/api/app/DashboardAppApiController.java @@ -0,0 +1,62 @@ +package ai.dragon.controller.api.app; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import ai.dragon.dto.api.GenericApiResponse; +import ai.dragon.dto.api.SuccessApiResponse; +import ai.dragon.dto.api.app.dashboard.NumbersDashboardAppApiData; +import ai.dragon.repository.DocumentRepository; +import ai.dragon.repository.FarmRepository; +import ai.dragon.repository.SiloRepository; +import ai.dragon.service.SystemMonitoringService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +@RestController +@RequestMapping("/api/app/dashboard") +@Tag(name = "Operations Center", description = "Dashboard API Endpoints") +public class DashboardAppApiController { + @Autowired + private SiloRepository siloRepository; + + @Autowired + private FarmRepository farmRepository; + + @Autowired + private DocumentRepository documentRepository; + + @Autowired + private SystemMonitoringService systemMonitoringService; + + @GetMapping("/numbers") + @Operation(summary = "Get Dashboard main numbers", description = "Returns the main numbers of the dashboard.") + public GenericApiResponse numbers() { + return SuccessApiResponse.builder().data(NumbersDashboardAppApiData + .builder() + .silos(siloRepository.countAll()) + .farms(farmRepository.countAll()) + .documents(documentRepository.countAll()) + .systemLoadAverage(systemMonitoringService.getSystemLoadAverage()) + .availableProcessors(systemMonitoringService.getAvailableProcessors()) + .arch(systemMonitoringService.getArch()) + .usedMemory(systemMonitoringService.getUsedMemory()) + .totalMemory(systemMonitoringService.getTotalMemory()) + .freeMemory(systemMonitoringService.getFreeMemory()) + .usedMemoryPercentage(systemMonitoringService.getUsedMemoryPercentage()) + .freeMemoryPercentage(systemMonitoringService.getFreeMemoryPercentage()) + .uptime(systemMonitoringService.getUptime()) + .heapMemoryUsage(systemMonitoringService.getHeapMemoryUsage()) + .heapMemoryUsagePercentage(systemMonitoringService.getHeapMemoryUsagePercentage()) + .nonHeapMemoryUsage(systemMonitoringService.getNonHeapMemoryUsage()) + .heapMemoryMax(systemMonitoringService.getHeapMemoryMax()) + .heapMemoryCommitted(systemMonitoringService.getHeapMemoryCommitted()) + .nonHeapMemoryCommitted(systemMonitoringService.getNonHeapMemoryCommitted()) + .heapMemoryInit(systemMonitoringService.getHeapMemoryInit()) + .nonHeapMemoryInit(systemMonitoringService.getNonHeapMemoryInit()) + .build()) + .build(); + } +} diff --git a/backend/src/main/java/ai/dragon/dto/api/GenericApiData.java b/backend/src/main/java/ai/dragon/dto/api/GenericApiData.java new file mode 100644 index 00000000..480f4c4d --- /dev/null +++ b/backend/src/main/java/ai/dragon/dto/api/GenericApiData.java @@ -0,0 +1,5 @@ +package ai.dragon.dto.api; + +public interface GenericApiData { + +} diff --git a/backend/src/main/java/ai/dragon/dto/api/GenericApiResponse.java b/backend/src/main/java/ai/dragon/dto/api/GenericApiResponse.java new file mode 100644 index 00000000..bc15e1b3 --- /dev/null +++ b/backend/src/main/java/ai/dragon/dto/api/GenericApiResponse.java @@ -0,0 +1,7 @@ +package ai.dragon.dto.api; + +public interface GenericApiResponse { + public GenericApiData getData(); + public String getCode(); + public String getMsg(); +} diff --git a/backend/src/main/java/ai/dragon/dto/api/SuccessApiResponse.java b/backend/src/main/java/ai/dragon/dto/api/SuccessApiResponse.java new file mode 100644 index 00000000..5427c03f --- /dev/null +++ b/backend/src/main/java/ai/dragon/dto/api/SuccessApiResponse.java @@ -0,0 +1,17 @@ +package ai.dragon.dto.api; + +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +public class SuccessApiResponse implements GenericApiResponse { + @Builder.Default + private GenericApiData data = null; + + @Builder.Default + private String code = "0000"; + + @Builder.Default + private String msg = "OK"; +} diff --git a/backend/src/main/java/ai/dragon/dto/api/app/auth/LoginAuthAppApiData.java b/backend/src/main/java/ai/dragon/dto/api/app/auth/LoginAuthAppApiData.java new file mode 100644 index 00000000..5d592a3e --- /dev/null +++ b/backend/src/main/java/ai/dragon/dto/api/app/auth/LoginAuthAppApiData.java @@ -0,0 +1,12 @@ +package ai.dragon.dto.api.app.auth; + +import ai.dragon.dto.api.GenericApiData; +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +public class LoginAuthAppApiData implements GenericApiData { + private String token; + private String refreshToken; +} diff --git a/backend/src/main/java/ai/dragon/dto/api/app/auth/UserInfoAuthAppApiData.java b/backend/src/main/java/ai/dragon/dto/api/app/auth/UserInfoAuthAppApiData.java new file mode 100644 index 00000000..1a175ad0 --- /dev/null +++ b/backend/src/main/java/ai/dragon/dto/api/app/auth/UserInfoAuthAppApiData.java @@ -0,0 +1,16 @@ +package ai.dragon.dto.api.app.auth; + +import java.util.List; + +import ai.dragon.dto.api.GenericApiData; +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +public class UserInfoAuthAppApiData implements GenericApiData { + private String userId; + private String userName; + private List roles; + private List buttons; +} diff --git a/backend/src/main/java/ai/dragon/dto/api/app/dashboard/NumbersDashboardAppApiData.java b/backend/src/main/java/ai/dragon/dto/api/app/dashboard/NumbersDashboardAppApiData.java new file mode 100644 index 00000000..af39a73a --- /dev/null +++ b/backend/src/main/java/ai/dragon/dto/api/app/dashboard/NumbersDashboardAppApiData.java @@ -0,0 +1,30 @@ +package ai.dragon.dto.api.app.dashboard; + +import ai.dragon.dto.api.GenericApiData; +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +public class NumbersDashboardAppApiData implements GenericApiData { + private Long silos; + private Long farms; + private Long documents; + private Double systemLoadAverage; + private Integer availableProcessors; + private String arch; + private Long usedMemory; + private Long totalMemory; + private Long freeMemory; + private Long usedMemoryPercentage; + private Long freeMemoryPercentage; + private Long uptime; + private Long heapMemoryUsage; + private Long heapMemoryUsagePercentage; + private Long nonHeapMemoryUsage; + private Long heapMemoryMax; + private Long heapMemoryCommitted; + private Long nonHeapMemoryCommitted; + private Long heapMemoryInit; + private Long nonHeapMemoryInit; +} diff --git a/backend/src/main/java/ai/dragon/service/RaagService.java b/backend/src/main/java/ai/dragon/service/RaagService.java index 8ab4be52..29f61ee6 100644 --- a/backend/src/main/java/ai/dragon/service/RaagService.java +++ b/backend/src/main/java/ai/dragon/service/RaagService.java @@ -20,9 +20,11 @@ import ai.dragon.dto.openai.completion.OpenAiRequest; import ai.dragon.dto.openai.model.OpenAiModel; import ai.dragon.entity.FarmEntity; +import ai.dragon.entity.SiloEntity; import ai.dragon.properties.embedding.LanguageModelSettings; import ai.dragon.properties.raag.RetrievalAugmentorSettings; import ai.dragon.repository.FarmRepository; +import ai.dragon.repository.SiloRepository; import ai.dragon.util.KVSettingUtil; import ai.dragon.util.ai.AiAssistant; import ai.dragon.util.spel.MetadataHeaderFilterExpressionParserUtil; @@ -60,6 +62,9 @@ public class RaagService { @Autowired private FarmRepository farmRepository; + @Autowired + private SiloRepository siloRepository; + @Autowired private ChatMessageService chatMessageService; @@ -106,18 +111,20 @@ public List buildRetrieverList(FarmEntity farm, HttpServletReq } farm.getSilos().forEach(siloUuid -> { try { - this.buildRetriever(siloUuid, servletRequest).ifPresent(retrievers::add); + SiloEntity silo = siloRepository.getByUuid(siloUuid).orElseThrow(); + this.buildSiloRetriever(silo, servletRequest).ifPresent(retrievers::add); } catch (Exception ex) { logger.error("Error building Content Retriever for Silo '{}'", siloUuid, ex); } }); + // TODO Granaries return retrievers; } - public Optional buildRetriever(UUID siloUuid, HttpServletRequest servletRequest) + public Optional buildSiloRetriever(SiloEntity silo, HttpServletRequest servletRequest) throws Exception { - EmbeddingModel embeddingModel = embeddingModelService.modelForSilo(siloUuid); - EmbeddingStore embeddingStore = embeddingStoreService.retrieveEmbeddingStore(siloUuid); + EmbeddingModel embeddingModel = embeddingModelService.modelForSilo(silo.getUuid()); + EmbeddingStore embeddingStore = embeddingStoreService.retrieveEmbeddingStore(silo.getUuid()); return Optional.of(EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) diff --git a/backend/src/main/java/ai/dragon/service/SystemMonitoringService.java b/backend/src/main/java/ai/dragon/service/SystemMonitoringService.java new file mode 100644 index 00000000..c410b4a9 --- /dev/null +++ b/backend/src/main/java/ai/dragon/service/SystemMonitoringService.java @@ -0,0 +1,92 @@ +package ai.dragon.service; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; + +import org.springframework.stereotype.Service; + +import jakarta.annotation.PostConstruct; + +@Service +public class SystemMonitoringService { + private OperatingSystemMXBean operatingSystemMXBean; + private RuntimeMXBean runtimeMXBean; + private MemoryMXBean memoryMXBean; + + @PostConstruct + private void postConstruct() { + operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); + runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + memoryMXBean = ManagementFactory.getMemoryMXBean(); + } + + public double getSystemLoadAverage() { + return operatingSystemMXBean.getSystemLoadAverage(); + } + + public int getAvailableProcessors() { + return operatingSystemMXBean.getAvailableProcessors(); + } + + public String getArch() { + return operatingSystemMXBean.getArch(); + } + + public long getUsedMemory() { + return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + } + + public long getTotalMemory() { + return Runtime.getRuntime().totalMemory(); + } + + public long getFreeMemory() { + return Runtime.getRuntime().freeMemory(); + } + + public long getUsedMemoryPercentage() { + return (getUsedMemory() * 100) / getTotalMemory(); + } + + public long getFreeMemoryPercentage() { + return (getFreeMemory() * 100) / getTotalMemory(); + } + + public long getUptime() { + return runtimeMXBean.getUptime(); + } + + public long getHeapMemoryUsagePercentage() { + return (getHeapMemoryUsage() * 100) / getHeapMemoryMax(); + } + + public long getHeapMemoryUsage() { + return memoryMXBean.getHeapMemoryUsage().getUsed(); + } + + public long getNonHeapMemoryUsage() { + return memoryMXBean.getNonHeapMemoryUsage().getUsed(); + } + + public long getHeapMemoryMax() { + return memoryMXBean.getHeapMemoryUsage().getMax(); + } + + public long getHeapMemoryCommitted() { + return memoryMXBean.getHeapMemoryUsage().getCommitted(); + } + + public long getNonHeapMemoryCommitted() { + return memoryMXBean.getNonHeapMemoryUsage().getCommitted(); + } + + public long getHeapMemoryInit() { + return memoryMXBean.getHeapMemoryUsage().getInit(); + } + + public long getNonHeapMemoryInit() { + return memoryMXBean.getNonHeapMemoryUsage().getInit(); + } +} diff --git a/backend/src/main/resources/.env.local.example b/backend/src/main/resources/.env.local.example new file mode 100644 index 00000000..55576297 --- /dev/null +++ b/backend/src/main/resources/.env.local.example @@ -0,0 +1 @@ +# OPENAI_API_KEY= \ No newline at end of file diff --git a/backend/src/main/resources/.env.properties b/backend/src/main/resources/.env.properties new file mode 100644 index 00000000..86c6d080 --- /dev/null +++ b/backend/src/main/resources/.env.properties @@ -0,0 +1 @@ +filename=.env.local \ No newline at end of file diff --git a/backend/src/test/java/ai/dragon/controller/api/raag/OpenAiCompatibleV1ApiControllerTest.java b/backend/src/test/java/ai/dragon/controller/api/raag/OpenAiCompatibleV1ApiControllerTest.java index 866d6fd1..3f97287a 100644 --- a/backend/src/test/java/ai/dragon/controller/api/raag/OpenAiCompatibleV1ApiControllerTest.java +++ b/backend/src/test/java/ai/dragon/controller/api/raag/OpenAiCompatibleV1ApiControllerTest.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.InterruptedIOException; @@ -19,6 +18,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIf; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; @@ -53,11 +53,12 @@ public class OpenAiCompatibleV1ApiControllerTest extends AbstractTest { @BeforeAll static void beforeAll(@Autowired FarmRepository farmRepository, @Autowired SiloRepository siloRepository, - @Autowired IngestorService ingestorService) throws Exception { + @Autowired IngestorService ingestorService, + @Value("${OPENAI_API_KEY:}") String openaiApiKey) throws Exception { cleanUp(farmRepository, siloRepository); // OpenAI settings for RaaG - String apiKeySetting = String.format("apiKey=%s", System.getenv("OPENAI_API_KEY")); + String apiKeySetting = String.format("apiKey=%s", openaiApiKey); String modelNameSetting = "modelName=gpt-3.5-turbo"; // TODO Migrate to gpt-4o-mini when reliable // Farm with no silo @@ -165,7 +166,7 @@ void testModelDoesntExistOpenAI() { .build(); OpenAiHttpException exception = assertThrows(OpenAiHttpException.class, () -> client.completion(request).execute()); - assertTrue(exception.code() == 404); + assertEquals(404, exception.code()); } @Test diff --git a/backend/src/test/java/ai/dragon/junit/AbstractTest.java b/backend/src/test/java/ai/dragon/junit/AbstractTest.java index 5be4ebd2..36af856d 100644 --- a/backend/src/test/java/ai/dragon/junit/AbstractTest.java +++ b/backend/src/test/java/ai/dragon/junit/AbstractTest.java @@ -1,7 +1,15 @@ package ai.dragon.junit; +import org.springframework.beans.factory.annotation.Value; + public abstract class AbstractTest { + @Value("${OPENAI_API_KEY:}") + private String openaiApiKey; + + @Value("${DRAGON_CICD:}") + private Boolean dragonCicd; + protected boolean canRunOpenAiRelatedTests() { - return System.getenv("DRAGON_CICD") != null || System.getenv("OPENAI_API_KEY") != null; + return Boolean.TRUE.equals(dragonCicd) || openaiApiKey != null && !openaiApiKey.isEmpty(); } } diff --git a/backend/src/test/java/ai/dragon/service/SystemMonitoringServiceTest.java b/backend/src/test/java/ai/dragon/service/SystemMonitoringServiceTest.java new file mode 100644 index 00000000..884a486b --- /dev/null +++ b/backend/src/test/java/ai/dragon/service/SystemMonitoringServiceTest.java @@ -0,0 +1,117 @@ +package ai.dragon.service; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest +@ActiveProfiles("test") +public class SystemMonitoringServiceTest { + @Autowired + private SystemMonitoringService systemMonitoringService; + + @Test + public void testGetSystemLoadAverage() { + double loadAverage = systemMonitoringService.getSystemLoadAverage(); + assertTrue(loadAverage >= -1.0); + } + + @Test + public void testGetAvailableProcessors() { + int processors = systemMonitoringService.getAvailableProcessors(); + assertTrue(processors > 0); + } + + @Test + public void testGetArch() { + String arch = systemMonitoringService.getArch(); + assertTrue(arch != null && !arch.isEmpty()); + } + + @Test + public void testGetUsedMemory() { + long usedMemory = systemMonitoringService.getUsedMemory(); + assertTrue(usedMemory > 0); + } + + @Test + public void testGetTotalMemory() { + long totalMemory = systemMonitoringService.getTotalMemory(); + assertTrue(totalMemory > 0); + } + + @Test + public void testGetFreeMemory() { + long freeMemory = systemMonitoringService.getFreeMemory(); + assertTrue(freeMemory >= 0); + } + + @Test + public void testGetUsedMemoryPercentage() { + long usedMemoryPercentage = systemMonitoringService.getUsedMemoryPercentage(); + assertTrue(usedMemoryPercentage >= 0 && usedMemoryPercentage <= 100); + } + + @Test + public void testGetFreeMemoryPercentage() { + long freeMemoryPercentage = systemMonitoringService.getFreeMemoryPercentage(); + assertTrue(freeMemoryPercentage >= 0 && freeMemoryPercentage <= 100); + } + + @Test + public void testGetUptime() { + long uptime = systemMonitoringService.getUptime(); + assertTrue(uptime >= 0); + } + + @Test + public void testGetHeapMemoryUsagePercentage() { + long heapMemoryUsagePercentage = systemMonitoringService.getHeapMemoryUsagePercentage(); + assertTrue(heapMemoryUsagePercentage >= 0 && heapMemoryUsagePercentage <= 100); + } + + @Test + public void testGetHeapMemoryUsage() { + long heapMemoryUsage = systemMonitoringService.getHeapMemoryUsage(); + assertTrue(heapMemoryUsage > 0); + } + + @Test + public void testGetNonHeapMemoryUsage() { + long nonHeapMemoryUsage = systemMonitoringService.getNonHeapMemoryUsage(); + assertTrue(nonHeapMemoryUsage > 0); + } + + @Test + public void testGetHeapMemoryMax() { + long heapMemoryMax = systemMonitoringService.getHeapMemoryMax(); + assertTrue(heapMemoryMax > 0); + } + + @Test + public void testGetHeapMemoryCommitted() { + long heapMemoryCommitted = systemMonitoringService.getHeapMemoryCommitted(); + assertTrue(heapMemoryCommitted > 0); + } + + @Test + public void testGetNonHeapMemoryCommitted() { + long nonHeapMemoryCommitted = systemMonitoringService.getNonHeapMemoryCommitted(); + assertTrue(nonHeapMemoryCommitted > 0); + } + + @Test + public void testGetHeapMemoryInit() { + long heapMemoryInit = systemMonitoringService.getHeapMemoryInit(); + assertTrue(heapMemoryInit > 0); + } + + @Test + public void testGetNonHeapMemoryInit() { + long nonHeapMemoryInit = systemMonitoringService.getNonHeapMemoryInit(); + assertTrue(nonHeapMemoryInit > 0); + } +} diff --git a/frontend/.env b/frontend/.env index a66a24ff..1a216171 100644 --- a/frontend/.env +++ b/frontend/.env @@ -21,7 +21,7 @@ VITE_ICON_LOCAL_PREFIX=icon-local VITE_AUTH_ROUTE_MODE=static # Static auth route home -VITE_ROUTE_HOME=home +VITE_ROUTE_HOME=operations-center # Default menu icon VITE_MENU_ICON=mdi:menu diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 00537e3f..e3a525b9 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -20,5 +20,8 @@ export default defineConfig( ], 'unocss/order-attributify': 'off' } + }, + { + ignores: ['mockoon_dRAGon.json'] } ); diff --git a/frontend/mockoon_dRAGon.json b/frontend/mockoon_dRAGon.json index ba0d4b84..8488d413 100644 --- a/frontend/mockoon_dRAGon.json +++ b/frontend/mockoon_dRAGon.json @@ -11,6 +11,10 @@ "uuid": "c98e1298-8e19-4747-9184-0f9311de3443", "name": "api", "children": [ + { + "type": "folder", + "uuid": "e23b80c8-8487-4342-ba7f-152be018578f" + }, { "type": "folder", "uuid": "059be112-aa2e-4e68-872f-c863fc1fd972" @@ -50,6 +54,30 @@ "uuid": "cd0e8e0b-c130-4d4a-87f7-673246e250d2" } ] + }, + { + "uuid": "e23b80c8-8487-4342-ba7f-152be018578f", + "name": "app", + "children": [ + { + "type": "folder", + "uuid": "583c3361-0cfc-46a5-8352-f80705f04797" + }, + { + "type": "folder", + "uuid": "2e32e415-ffda-4d7e-9fb2-3584131428e8" + } + ] + }, + { + "uuid": "2e32e415-ffda-4d7e-9fb2-3584131428e8", + "name": "dashboard", + "children": [ + { + "type": "route", + "uuid": "65043f0b-02cd-48d3-924b-b9da53428caf" + } + ] } ], "routes": [ @@ -87,7 +115,7 @@ "type": "http", "documentation": "Login Endpoint", "method": "post", - "endpoint": "auth/login", + "endpoint": "api/app/auth/login", "responses": [ { "uuid": "0f069a2b-d27d-4e3f-b343-fe3ed06e08a4", @@ -116,7 +144,7 @@ "type": "http", "documentation": "", "method": "get", - "endpoint": "auth/getUserInfo", + "endpoint": "api/app/auth/getUserInfo", "responses": [ { "uuid": "a2b7f8de-998e-4e33-9302-f0bd0f1c84d4", @@ -139,16 +167,41 @@ } ], "responseMode": null + }, + { + "uuid": "65043f0b-02cd-48d3-924b-b9da53428caf", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/app/dashboard/numbers", + "responses": [ + { + "uuid": "58458bee-0645-43e1-85ea-09518cebb8da", + "body": "{\n \"data\": {\n \"silos\": 2,\n \"farms\": 1,\n \"documents\": 9,\n \"systemLoadAverage\": 1.43017578125,\n \"availableProcessors\": 4,\n \"arch\": \"aarch64\",\n \"usedMemory\": 65131328,\n \"totalMemory\": 88080384,\n \"freeMemory\": 22949056,\n \"usedMemoryPercentage\": 73,\n \"freeMemoryPercentage\": 26,\n \"uptime\": 5710,\n \"heapMemoryUsage\": 64517528,\n \"heapMemoryUsagePercentage\": 4,\n \"nonHeapMemoryUsage\": 62203056,\n \"heapMemoryMax\": 1553989632,\n \"heapMemoryCommitted\": 88080384,\n \"nonHeapMemoryCommitted\": 63438848,\n \"heapMemoryInit\": 98566144,\n \"nonHeapMemoryInit\": 2555904\n },\n \"code\": \"0000\",\n \"msg\": \"OK\"\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null } ], "rootChildren": [ { "type": "folder", "uuid": "c98e1298-8e19-4747-9184-0f9311de3443" - }, - { - "type": "folder", - "uuid": "583c3361-0cfc-46a5-8352-f80705f04797" } ], "proxyMode": false, @@ -204,4 +257,4 @@ } ], "callbacks": [] -} +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index b88acefd..33047ba2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,7 +33,8 @@ "dev": "vite --mode test", "dev:prod": "vite --mode prod", "gen-route": "dRAGon gen-route", - "lint": "eslint . --fix", + "lint": "eslint . --max-warnings 0", + "lint:fix": "eslint . --max-warnings 0 --fix", "preview": "vite preview", "release": "dRAGon release", "typecheck": "vue-tsc --noEmit --skipLibCheck", @@ -59,30 +60,30 @@ "vue": "3.4.35", "vue-draggable-plus": "0.5.2", "vue-i18n": "9.13.1", - "vue-router": "4.4.0" + "vue-router": "4.4.2" }, "devDependencies": { "@dragon/scripts": "workspace:*", - "@elegant-router/vue": "0.3.7", - "@iconify/json": "2.2.232", + "@elegant-router/vue": "0.3.8", + "@iconify/json": "2.2.233", "@sa/uno-preset": "workspace:*", - "@soybeanjs/eslint-config": "1.3.7", + "@soybeanjs/eslint-config": "1.4.0", "@types/lodash-es": "4.17.12", - "@types/node": "22.0.0", + "@types/node": "22.0.3", "@types/nprogress": "0.2.3", - "@unocss/eslint-config": "0.61.8", - "@unocss/preset-icons": "0.61.8", - "@unocss/preset-uno": "0.61.8", - "@unocss/transformer-directives": "0.61.8", - "@unocss/transformer-variant-group": "0.61.8", - "@unocss/vite": "0.61.8", - "@vitejs/plugin-vue": "5.1.1", + "@unocss/eslint-config": "0.61.9", + "@unocss/preset-icons": "0.61.9", + "@unocss/preset-uno": "0.61.9", + "@unocss/transformer-directives": "0.61.9", + "@unocss/transformer-variant-group": "0.61.9", + "@unocss/vite": "0.61.9", + "@vitejs/plugin-vue": "5.1.2", "@vitejs/plugin-vue-jsx": "4.0.0", "eslint": "9.8.0", "eslint-plugin-vue": "9.27.0", "lint-staged": "15.2.7", "sass": "1.77.8", - "tsx": "4.16.3", + "tsx": "4.16.5", "typescript": "5.5.4", "unplugin-icons": "0.19.1", "unplugin-vue-components": "0.27.3", diff --git a/frontend/packages/axios/package.json b/frontend/packages/axios/package.json index ba284079..29ff49cb 100644 --- a/frontend/packages/axios/package.json +++ b/frontend/packages/axios/package.json @@ -11,9 +11,9 @@ }, "dependencies": { "@sa/utils": "workspace:*", - "axios": "1.7.2", - "axios-retry": "4.4.2", - "qs": "6.12.3" + "axios": "1.7.3", + "axios-retry": "4.5.0", + "qs": "6.13.0" }, "devDependencies": { "@types/qs": "6.9.15" diff --git a/frontend/packages/axios/src/index.ts b/frontend/packages/axios/src/index.ts index 08846c24..18444a61 100644 --- a/frontend/packages/axios/src/index.ts +++ b/frontend/packages/axios/src/index.ts @@ -60,7 +60,7 @@ function createCommonRequest( } const backendError = new AxiosError( - 'the backend request error', + 'Error while request to backend', BACKEND_ERROR_CODE, response.config, response.request, diff --git a/frontend/packages/scripts/package.json b/frontend/packages/scripts/package.json index a20fb5cf..ff51fea8 100644 --- a/frontend/packages/scripts/package.json +++ b/frontend/packages/scripts/package.json @@ -14,14 +14,14 @@ }, "devDependencies": { "@soybeanjs/changelog": "0.3.24", - "bumpp": "9.4.1", + "bumpp": "9.4.2", "c12": "1.11.1", "cac": "6.7.14", "consola": "3.2.3", "enquirer": "2.4.1", "execa": "9.3.0", "kolorist": "1.8.0", - "npm-check-updates": "16.14.20", + "npm-check-updates": "17.0.0", "rimraf": "6.0.1" } } diff --git a/frontend/src/assets/svg-icon/mdi--cpu-64-bit.svg b/frontend/src/assets/svg-icon/mdi--cpu-64-bit.svg new file mode 100644 index 00000000..f3c9518c --- /dev/null +++ b/frontend/src/assets/svg-icon/mdi--cpu-64-bit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg-icon/mdi--graph-bell-curve.svg b/frontend/src/assets/svg-icon/mdi--graph-bell-curve.svg new file mode 100644 index 00000000..bd469c7e --- /dev/null +++ b/frontend/src/assets/svg-icon/mdi--graph-bell-curve.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg-icon/mdi--land-plots.svg b/frontend/src/assets/svg-icon/mdi--land-plots.svg new file mode 100644 index 00000000..5d2f09e1 --- /dev/null +++ b/frontend/src/assets/svg-icon/mdi--land-plots.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg-icon/mdi--layers-triple-outline.svg b/frontend/src/assets/svg-icon/mdi--layers-triple-outline.svg new file mode 100644 index 00000000..300debb4 --- /dev/null +++ b/frontend/src/assets/svg-icon/mdi--layers-triple-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg-icon/mdi--memory.svg b/frontend/src/assets/svg-icon/mdi--memory.svg new file mode 100644 index 00000000..ddf5c47a --- /dev/null +++ b/frontend/src/assets/svg-icon/mdi--memory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/locales/langs/en-us.ts b/frontend/src/locales/langs/en-us.ts index 521f0c5d..44eca197 100644 --- a/frontend/src/locales/langs/en-us.ts +++ b/frontend/src/locales/langs/en-us.ts @@ -1,10 +1,24 @@ const local: App.I18n.Schema = { + dRAGon: { + silo: 'Silo', + silos: 'Silos', + farm: 'Farm', + farms: 'Farms', + document: 'Document', + documents: 'Documents', + infrastructure: 'Infrastructure', + infrastructureDescription: 'Overall situation of the dRAGon infrastructure' + }, system: { title: 'dRAGon', updateTitle: 'System Version Update Notification', updateContent: 'A new version of the system has been detected. Do you want to refresh the page immediately?', updateConfirm: 'Refresh immediately', - updateCancel: 'Later' + updateCancel: 'Later', + usedMemory: 'Used Memory', + loadAverage: 'Load Average', + heapMemory: 'Heap Memory', + processors: 'Processors' }, common: { action: 'Action', @@ -148,7 +162,7 @@ const local: App.I18n.Schema = { 404: 'Page Not Found', 500: 'Server Error', 'iframe-page': 'Iframe', - home: 'Operations Center', + 'operations-center': 'Operations Center', 'user-center': 'User Center', about: 'About', help: 'Help', @@ -220,72 +234,6 @@ const local: App.I18n.Schema = { prdDep: 'Frontend Production Dependencies', devDep: 'Frontend Development Dependencies' }, - home: { - greeting: '{userName}, your journey starts now!', - weatherDesc: 'All components are healthy', - projectCount: 'Farms', - todo: 'Silos', - message: 'Documents', - downloadCount: 'Document Count', - registerCount: 'Token Count', - schedule: 'Work and rest Schedule', - study: 'Silo', - work: 'Farm', - rest: 'Granary', - entertainment: 'Harvester', - visitCount: 'Query Count', - turnover: 'Token Used', - dealCount: 'Error Count', - projectNews: { - title: 'Latest Events', - moreNews: 'More Events', - desc1: 'Silo "08C98C" has been successfully built', - desc2: 'User "dRAGon" has been successfully registered', - desc3: 'User "dRAGon" has been successfully logged in', - desc4: 'Farm "F9CF9C" has been successfully built', - desc5: 'Farm "F9CF9C" has been removed' - }, - creativity: 'Other' - }, - function: { - tab: { - tabOperate: { - title: 'Tab Operation', - addTab: 'Add Tab', - addTabDesc: 'To about page', - closeTab: 'Close Tab', - closeCurrentTab: 'Close Current Tab', - closeAboutTab: 'Close "About" Tab', - addMultiTab: 'Add Multi Tab', - addMultiTabDesc1: 'To MultiTab page', - addMultiTabDesc2: 'To MultiTab page(with query params)' - }, - tabTitle: { - title: 'Tab Title', - changeTitle: 'Change Title', - change: 'Change', - resetTitle: 'Reset Title', - reset: 'Reset' - } - }, - multiTab: { - routeParam: 'Route Param', - backTab: 'Back function_tab' - }, - toggleAuth: { - toggleAccount: 'Toggle Account', - authHook: 'Auth Hook Function `hasAuth`', - superAdminVisible: 'Super Admin Visible', - adminVisible: 'Admin Visible', - adminOrUserVisible: 'Admin and User Visible' - }, - request: { - repeatedErrorOccurOnce: 'Repeated Request Error Occurs Once', - repeatedError: 'Repeated Request Error', - repeatedErrorMsg1: 'Custom Request Error 1', - repeatedErrorMsg2: 'Custom Request Error 2' - } - }, manage: { common: { status: { @@ -336,7 +284,6 @@ const local: App.I18n.Schema = { } }, menu: { - home: 'Operations Center', title: 'Menu List', id: 'ID', parentId: 'Parent ID', diff --git a/frontend/src/router/elegant/imports.ts b/frontend/src/router/elegant/imports.ts index dc383cb1..b6cd7dc7 100644 --- a/frontend/src/router/elegant/imports.ts +++ b/frontend/src/router/elegant/imports.ts @@ -22,8 +22,8 @@ export const views: Record Promise import("@/views/_builtin/login/index.vue"), about: () => import("@/views/about/index.vue"), help: () => import("@/views/help/index.vue"), - home: () => import("@/views/home/index.vue"), "infrastructure_silo-detail": () => import("@/views/infrastructure/silo-detail/[id].vue"), "infrastructure_silo-list": () => import("@/views/infrastructure/silo-list/index.vue"), + "operations-center": () => import("@/views/operations-center/index.vue"), "user-center": () => import("@/views/user-center/index.vue"), }; diff --git a/frontend/src/router/elegant/routes.ts b/frontend/src/router/elegant/routes.ts index 78b0aaed..48809c3d 100644 --- a/frontend/src/router/elegant/routes.ts +++ b/frontend/src/router/elegant/routes.ts @@ -62,18 +62,6 @@ export const generatedRoutes: GeneratedRoute[] = [ href: 'https://dragon.okinawa' } }, - { - name: 'home', - path: '/home', - component: 'layout.base$view.home', - meta: { - title: 'home', - i18nKey: 'route.home', - localIcon: 'mdi--monitor-dashboard', - order: 1, - keepAlive: true - } - }, { name: 'iframe-page', path: '/iframe-page/:url', @@ -138,6 +126,18 @@ export const generatedRoutes: GeneratedRoute[] = [ hideInMenu: true } }, + { + name: 'operations-center', + path: '/operations-center', + component: 'layout.base$view.operations-center', + meta: { + title: 'operations-center', + i18nKey: 'route.operations-center', + localIcon: 'mdi--monitor-dashboard', + order: 1, + keepAlive: true + } + }, { name: 'user-center', path: '/user-center', diff --git a/frontend/src/router/elegant/transform.ts b/frontend/src/router/elegant/transform.ts index f40ff71e..1ae64f86 100644 --- a/frontend/src/router/elegant/transform.ts +++ b/frontend/src/router/elegant/transform.ts @@ -97,10 +97,13 @@ function transformElegantRouteToVueRoute( if (component) { if (isSingleLevelRoute(route)) { const { layout, view } = getSingleLevelRouteComponent(component); - + const singleLevelRoute: RouteRecordRaw = { path, component: layouts[layout], + meta: { + title: route.meta?.title || '' + }, children: [ { name, @@ -110,36 +113,35 @@ function transformElegantRouteToVueRoute( } as RouteRecordRaw ] }; - + return [singleLevelRoute]; } - + if (isLayout(component)) { const layoutName = getLayoutName(component); - + vueRoute.component = layouts[layoutName]; } - + if (isView(component)) { const viewName = getViewName(component); - + vueRoute.component = views[viewName]; } - + } } catch (error: any) { console.error(`Error transforming route "${route.name}": ${error.toString()}`); return []; } - // add redirect to child if (children?.length && !vueRoute.redirect) { vueRoute.redirect = { name: children[0].name }; } - + if (children?.length) { const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views)); @@ -170,12 +172,12 @@ const routeMap: RouteMap = { "500": "/500", "about": "/about", "help": "/help", - "home": "/home", "iframe-page": "/iframe-page/:url", "infrastructure": "/infrastructure", "infrastructure_silo-detail": "/infrastructure/silo-detail/:id", "infrastructure_silo-list": "/infrastructure/silo-list", "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?", + "operations-center": "/operations-center", "user-center": "/user-center" }; diff --git a/frontend/src/router/routes/builtin.ts b/frontend/src/router/routes/builtin.ts index 0a13e783..04c87b7d 100644 --- a/frontend/src/router/routes/builtin.ts +++ b/frontend/src/router/routes/builtin.ts @@ -5,7 +5,7 @@ import { getRoutePath, transformElegantRoutesToVueRoutes } from '../elegant/tran export const ROOT_ROUTE: CustomRoute = { name: 'root', path: '/', - redirect: getRoutePath(import.meta.env.VITE_ROUTE_HOME) || '/home', + redirect: getRoutePath(import.meta.env.VITE_ROUTE_HOME) || '/operations-center', meta: { title: 'root', constant: true diff --git a/frontend/src/service/api/auth.ts b/frontend/src/service/api/app-auth.ts similarity index 77% rename from frontend/src/service/api/auth.ts rename to frontend/src/service/api/app-auth.ts index 1ffcf2bf..1a0f8134 100644 --- a/frontend/src/service/api/auth.ts +++ b/frontend/src/service/api/app-auth.ts @@ -8,7 +8,7 @@ import { request } from '../request'; */ export function fetchLogin(userName: string, password: string) { return request({ - url: '/auth/login', + url: '/api/app/auth/login', method: 'post', data: { userName, @@ -19,7 +19,7 @@ export function fetchLogin(userName: string, password: string) { /** Get user info */ export function fetchGetUserInfo() { - return request({ url: '/auth/getUserInfo' }); + return request({ url: '/api/app/auth/getUserInfo' }); } /** @@ -29,7 +29,7 @@ export function fetchGetUserInfo() { */ export function fetchRefreshToken(refreshToken: string) { return request({ - url: '/auth/refreshToken', + url: '/api/app/auth/refreshToken', method: 'post', data: { refreshToken @@ -44,5 +44,5 @@ export function fetchRefreshToken(refreshToken: string) { * @param msg error message */ export function fetchCustomBackendError(code: string, msg: string) { - return request({ url: '/auth/error', params: { code, msg } }); + return request({ url: '/api/app/auth/error', params: { code, msg } }); } diff --git a/frontend/src/service/api/app-dashboard.ts b/frontend/src/service/api/app-dashboard.ts new file mode 100644 index 00000000..738dd129 --- /dev/null +++ b/frontend/src/service/api/app-dashboard.ts @@ -0,0 +1,9 @@ +import { request } from '../request'; + +// Get numbers about the dashboard +export function fetchApiAppDashboardGetNumbers() { + return request({ + url: '/api/app/dashboard/numbers', + method: 'get' + }); +} diff --git a/frontend/src/service/api/route.ts b/frontend/src/service/api/app-route.ts similarity index 54% rename from frontend/src/service/api/route.ts rename to frontend/src/service/api/app-route.ts index 0956a7a8..efe37dc2 100644 --- a/frontend/src/service/api/route.ts +++ b/frontend/src/service/api/app-route.ts @@ -2,12 +2,12 @@ import { request } from '../request'; /** get constant routes */ export function fetchGetConstantRoutes() { - return request({ url: '/route/getConstantRoutes' }); + return request({ url: '/api/app/route/getConstantRoutes' }); } /** get user routes */ export function fetchGetUserRoutes() { - return request({ url: '/route/getUserRoutes' }); + return request({ url: '/api/app/route/getUserRoutes' }); } /** @@ -16,5 +16,5 @@ export function fetchGetUserRoutes() { * @param routeName route name */ export function fetchIsRouteExist(routeName: string) { - return request({ url: '/route/isRouteExist', params: { routeName } }); + return request({ url: '/api/app/route/isRouteExist', params: { routeName } }); } diff --git a/frontend/src/service/api/backend-silo.ts b/frontend/src/service/api/backend-silo.ts new file mode 100644 index 00000000..a7f9c33d --- /dev/null +++ b/frontend/src/service/api/backend-silo.ts @@ -0,0 +1,22 @@ +import { request } from '../request'; + +/** + * get all roles + * + * these roles are all enabled + */ +export function fetchGetAllRoles() { + return request({ + url: '/systemManage/getAllRoles', + method: 'get' + }); +} + +/** get user list */ +export function fetchGetUserList(params?: Api.SystemManage.UserSearchParams) { + return request({ + url: '/systemManage/getUserList', + method: 'get', + params + }); +} diff --git a/frontend/src/service/api/index.ts b/frontend/src/service/api/index.ts index c9d31d11..cd3857b1 100644 --- a/frontend/src/service/api/index.ts +++ b/frontend/src/service/api/index.ts @@ -1,3 +1,4 @@ -export * from './auth'; -export * from './route'; -export * from './system-manage'; +export * from './app-auth'; +export * from './app-route'; +export * from './app-dashboard'; +export * from './backend-silo'; diff --git a/frontend/src/service/api/system-manage.ts b/frontend/src/service/api/system-manage.ts deleted file mode 100644 index cb975b2c..00000000 --- a/frontend/src/service/api/system-manage.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { request } from '../request'; - -/** get role list */ -export function fetchGetRoleList(params?: Api.SystemManage.RoleSearchParams) { - return request({ - url: '/systemManage/getRoleList', - method: 'get', - params - }); -} - -/** - * get all roles - * - * these roles are all enabled - */ -export function fetchGetAllRoles() { - return request({ - url: '/systemManage/getAllRoles', - method: 'get' - }); -} - -/** get user list */ -export function fetchGetUserList(params?: Api.SystemManage.UserSearchParams) { - return request({ - url: '/systemManage/getUserList', - method: 'get', - params - }); -} - -/** get menu list */ -export function fetchGetMenuList() { - return request({ - url: '/systemManage/getMenuList/v2', - method: 'get' - }); -} - -/** get all pages */ -export function fetchGetAllPages() { - return request({ - url: '/systemManage/getAllPages', - method: 'get' - }); -} - -/** get menu tree */ -export function fetchGetMenuTree() { - return request({ - url: '/systemManage/getMenuTree', - method: 'get' - }); -} diff --git a/frontend/src/service/request/shared.ts b/frontend/src/service/request/shared.ts index decd63d0..2e863706 100644 --- a/frontend/src/service/request/shared.ts +++ b/frontend/src/service/request/shared.ts @@ -48,7 +48,9 @@ export function showErrorMsg(state: RequestInstanceState, message: string) { setTimeout(() => { state.errMsgStack = []; }, 5000); - } + }, + duration: 60000, + closable: true }); } } diff --git a/frontend/src/typings/api.d.ts b/frontend/src/typings/api.d.ts index 49864a17..47591cdd 100644 --- a/frontend/src/typings/api.d.ts +++ b/frontend/src/typings/api.d.ts @@ -82,6 +82,46 @@ declare namespace Api { } } + /** + * namespace AppDashboard + * + * backend api module: "appDashboard" + */ + namespace AppDashboard { + type Numbers = { + /** total Farm count */ + farms: number; + /** total Silo count */ + silos: number; + /** total Document count */ + documents: number; + /** system load average */ + systemLoadAverage: number; + /** system processor count */ + availableProcessors: number; + /** system architecture */ + arch: string; + /** used memory */ + usedMemory: number; + /** total memory */ + totalMemory: number; + /** free memory */ + freeMemory: number; + /** used memory percentage */ + usedMemoryPercentage: number; + /** free memory percentage */ + freeMemoryPercentage: number; + /** system uptime */ + uptime: number; + /** heap memory usage */ + heapMemoryUsage: number; + /** heap memory usage percentage */ + heapMemoryUsagePercentage: number; + /** non-heap memory usage */ + nonHeapMemoryUsage: number; + }; + } + /** * namespace SystemManage * diff --git a/frontend/src/typings/app.d.ts b/frontend/src/typings/app.d.ts index 43024bca..ca1e9396 100644 --- a/frontend/src/typings/app.d.ts +++ b/frontend/src/typings/app.d.ts @@ -245,12 +245,26 @@ declare namespace App { }; type Schema = { + dRAGon: { + silo: string; + silos: string; + farm: string; + farms: string; + document: string; + documents: string; + infrastructure: string; + infrastructureDescription: string; + }; system: { title: string; updateTitle: string; updateContent: string; updateConfirm: string; updateCancel: string; + usedMemory: string; + loadAverage: string; + heapMemory: string; + processors: string; }; common: { action: string; @@ -416,72 +430,6 @@ declare namespace App { prdDep: string; devDep: string; }; - home: { - greeting: string; - weatherDesc: string; - projectCount: string; - todo: string; - message: string; - downloadCount: string; - registerCount: string; - schedule: string; - study: string; - work: string; - rest: string; - entertainment: string; - visitCount: string; - turnover: string; - dealCount: string; - projectNews: { - title: string; - moreNews: string; - desc1: string; - desc2: string; - desc3: string; - desc4: string; - desc5: string; - }; - creativity: string; - }; - function: { - tab: { - tabOperate: { - title: string; - addTab: string; - addTabDesc: string; - closeTab: string; - closeCurrentTab: string; - closeAboutTab: string; - addMultiTab: string; - addMultiTabDesc1: string; - addMultiTabDesc2: string; - }; - tabTitle: { - title: string; - changeTitle: string; - change: string; - resetTitle: string; - reset: string; - }; - }; - multiTab: { - routeParam: string; - backTab: string; - }; - toggleAuth: { - toggleAccount: string; - authHook: string; - superAdminVisible: string; - adminVisible: string; - adminOrUserVisible: string; - }; - request: { - repeatedErrorOccurOnce: string; - repeatedError: string; - repeatedErrorMsg1: string; - repeatedErrorMsg2: string; - }; - }; manage: { common: { status: { @@ -532,7 +480,6 @@ declare namespace App { }; }; menu: { - home: string; title: string; id: string; parentId: string; diff --git a/frontend/src/typings/components.d.ts b/frontend/src/typings/components.d.ts index b4946a21..317ba98f 100644 --- a/frontend/src/typings/components.d.ts +++ b/frontend/src/typings/components.d.ts @@ -19,13 +19,10 @@ declare module 'vue' { IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default'] IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default'] IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default'] - 'IconIc:roundPlus': typeof import('~icons/ic/round-plus')['default'] IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default'] IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default'] IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default'] - IconIcRoundRemove: typeof import('~icons/ic/round-remove')['default'] IconIcRoundSearch: typeof import('~icons/ic/round-search')['default'] - IconLocalBanner: typeof import('~icons/local/banner')['default'] IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default'] IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default'] IconMdiDrag: typeof import('~icons/mdi/drag')['default'] @@ -51,7 +48,6 @@ declare module 'vue' { NDrawer: typeof import('naive-ui')['NDrawer'] NDrawerContent: typeof import('naive-ui')['NDrawerContent'] NDropdown: typeof import('naive-ui')['NDropdown'] - NDynamicInput: typeof import('naive-ui')['NDynamicInput'] NEmpty: typeof import('naive-ui')['NEmpty'] NForm: typeof import('naive-ui')['NForm'] NFormItem: typeof import('naive-ui')['NFormItem'] @@ -61,8 +57,6 @@ declare module 'vue' { NInput: typeof import('naive-ui')['NInput'] NInputGroup: typeof import('naive-ui')['NInputGroup'] NInputNumber: typeof import('naive-ui')['NInputNumber'] - NList: typeof import('naive-ui')['NList'] - NListItem: typeof import('naive-ui')['NListItem'] NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider'] NMenu: typeof import('naive-ui')['NMenu'] NMessageProvider: typeof import('naive-ui')['NMessageProvider'] @@ -80,9 +74,7 @@ declare module 'vue' { NTab: typeof import('naive-ui')['NTab'] NTabs: typeof import('naive-ui')['NTabs'] NTag: typeof import('naive-ui')['NTag'] - NThing: typeof import('naive-ui')['NThing'] NTooltip: typeof import('naive-ui')['NTooltip'] - NTree: typeof import('naive-ui')['NTree'] PinToggler: typeof import('./../components/common/pin-toggler.vue')['default'] ReloadButton: typeof import('./../components/common/reload-button.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] diff --git a/frontend/src/typings/elegant-router.d.ts b/frontend/src/typings/elegant-router.d.ts index d9207ad7..70956eb2 100644 --- a/frontend/src/typings/elegant-router.d.ts +++ b/frontend/src/typings/elegant-router.d.ts @@ -26,12 +26,12 @@ declare module "@elegant-router/types" { "500": "/500"; "about": "/about"; "help": "/help"; - "home": "/home"; "iframe-page": "/iframe-page/:url"; "infrastructure": "/infrastructure"; "infrastructure_silo-detail": "/infrastructure/silo-detail/:id"; "infrastructure_silo-list": "/infrastructure/silo-list"; "login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?"; + "operations-center": "/operations-center"; "user-center": "/user-center"; }; @@ -47,7 +47,7 @@ declare module "@elegant-router/types" { /** * custom route key - */ + */ export type CustomRouteKey = Extract< RouteKey, | "root" @@ -60,7 +60,7 @@ declare module "@elegant-router/types" { /** * the generated route key - */ + */ export type GeneratedRouteKey = Exclude; /** @@ -73,10 +73,10 @@ declare module "@elegant-router/types" { | "500" | "about" | "help" - | "home" | "iframe-page" | "infrastructure" | "login" + | "operations-center" | "user-center" >; @@ -102,9 +102,9 @@ declare module "@elegant-router/types" { | "login" | "about" | "help" - | "home" | "infrastructure_silo-detail" | "infrastructure_silo-list" + | "operations-center" | "user-center" >; diff --git a/frontend/src/views/home/index.vue b/frontend/src/views/home/index.vue deleted file mode 100644 index bd69cf5c..00000000 --- a/frontend/src/views/home/index.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - - - diff --git a/frontend/src/views/home/modules/creativity-banner.vue b/frontend/src/views/home/modules/creativity-banner.vue deleted file mode 100644 index c461e467..00000000 --- a/frontend/src/views/home/modules/creativity-banner.vue +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/frontend/src/views/home/modules/header-banner.vue b/frontend/src/views/home/modules/header-banner.vue deleted file mode 100644 index 342ad7e6..00000000 --- a/frontend/src/views/home/modules/header-banner.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - - - diff --git a/frontend/src/views/home/modules/line-chart.vue b/frontend/src/views/home/modules/line-chart.vue deleted file mode 100644 index 9f2d4c7d..00000000 --- a/frontend/src/views/home/modules/line-chart.vue +++ /dev/null @@ -1,151 +0,0 @@ - - - - - diff --git a/frontend/src/views/home/modules/pie-chart.vue b/frontend/src/views/home/modules/pie-chart.vue deleted file mode 100644 index bd24f4f4..00000000 --- a/frontend/src/views/home/modules/pie-chart.vue +++ /dev/null @@ -1,109 +0,0 @@ - - - - - diff --git a/frontend/src/views/home/modules/project-news.vue b/frontend/src/views/home/modules/project-news.vue deleted file mode 100644 index 55615671..00000000 --- a/frontend/src/views/home/modules/project-news.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - - - diff --git a/frontend/src/views/operations-center/index.vue b/frontend/src/views/operations-center/index.vue new file mode 100644 index 00000000..1424b2ca --- /dev/null +++ b/frontend/src/views/operations-center/index.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/frontend/src/views/operations-center/modules/infrastructure-overall.vue b/frontend/src/views/operations-center/modules/infrastructure-overall.vue new file mode 100644 index 00000000..d3b1accd --- /dev/null +++ b/frontend/src/views/operations-center/modules/infrastructure-overall.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/frontend/src/views/home/modules/card-data.vue b/frontend/src/views/operations-center/modules/jvm-cards.vue similarity index 72% rename from frontend/src/views/home/modules/card-data.vue rename to frontend/src/views/operations-center/modules/jvm-cards.vue index 384e6a81..c2b1e8ec 100644 --- a/frontend/src/views/home/modules/card-data.vue +++ b/frontend/src/views/operations-center/modules/jvm-cards.vue @@ -4,14 +4,21 @@ import { createReusableTemplate } from '@vueuse/core'; import { $t } from '@/locales'; defineOptions({ - name: 'CardData' + name: 'JvmCards' }); +interface Props { + numbers: Api.AppDashboard.Numbers; +} +const props = defineProps(); + interface CardData { key: string; title: string; value: number; - unit: string; + prefix?: string; + suffix?: string; + decimals?: number; color: { start: string; end: string; @@ -22,47 +29,46 @@ interface CardData { const cardData = computed(() => [ { key: 'visitCount', - title: $t('page.home.visitCount'), - value: 9725, - unit: '', + title: $t('system.usedMemory'), + value: props.numbers.usedMemoryPercentage, + suffix: '%', color: { start: '#ec4786', end: '#b955a4' }, - icon: 'ant-design:bar-chart-outlined' + icon: 'mdi--memory' }, { key: 'turnover', - title: $t('page.home.turnover'), - value: 1026, - unit: '$', + title: $t('system.loadAverage'), + value: props.numbers.systemLoadAverage, + decimals: 2, color: { start: '#865ec0', end: '#5144b4' }, - icon: 'ant-design:money-collect-outlined' + icon: 'mdi--graph-bell-curve' }, { key: 'downloadCount', - title: $t('page.home.downloadCount'), - value: 970925, - unit: '', + title: $t('system.heapMemory'), + value: props.numbers.heapMemoryUsagePercentage, + suffix: '%', color: { start: '#56cdf3', end: '#719de3' }, - icon: 'carbon:document-download' + icon: 'mdi--layers-triple-outline' }, { key: 'dealCount', - title: $t('page.home.dealCount'), - value: 9527, - unit: '', + title: $t('system.processors'), + value: props.numbers.availableProcessors, color: { start: '#fcbc25', end: '#f68057' }, - icon: 'ant-design:trademark-circle-outlined' + icon: 'mdi--cpu-64-bit' } ]); @@ -94,9 +100,11 @@ function getGradientColor(color: CardData['color']) {