From 9af1581da36d2b106a636ef59a2a0ee537c30ce4 Mon Sep 17 00:00:00 2001 From: jade Date: Thu, 9 Jul 2026 23:04:37 +0900 Subject: [PATCH] feat: Add Developer Portal Web UI for scaffolding to Gateway --- .../axhub/common}/util/PodScaffolder.java | 36 ++-- .../axhub/common}/util/ToolScaffolder.java | 27 +-- .../presentation/ScaffoldingController.java | 45 +++++ .../main/resources/static/admin/scaffold.html | 157 ++++++++++++++++++ 4 files changed, 237 insertions(+), 28 deletions(-) rename {axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool => axhub-common/src/main/java/io/shinhanlife/axhub/common}/util/PodScaffolder.java (88%) rename {axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool => axhub-common/src/main/java/io/shinhanlife/axhub/common}/util/ToolScaffolder.java (87%) create mode 100644 axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/presentation/ScaffoldingController.java create mode 100644 axhub-gateway/src/main/resources/static/admin/scaffold.html diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/util/PodScaffolder.java b/axhub-common/src/main/java/io/shinhanlife/axhub/common/util/PodScaffolder.java similarity index 88% rename from axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/util/PodScaffolder.java rename to axhub-common/src/main/java/io/shinhanlife/axhub/common/util/PodScaffolder.java index 14a4555..c95a89c 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/util/PodScaffolder.java +++ b/axhub-common/src/main/java/io/shinhanlife/axhub/common/util/PodScaffolder.java @@ -1,4 +1,4 @@ -package io.shinhanlife.axhub.biz.mcp.tool.util; +package io.shinhanlife.axhub.common.util; import java.io.IOException; import java.nio.file.Files; @@ -31,7 +31,8 @@ public class PodScaffolder { String createDate = getOrAsk(args, 3, scanner, "4. 작성일 (엔터 입력 시 '" + defaultDate + "'): "); if (createDate.trim().isEmpty()) createDate = defaultDate; - scaffoldPod(moduleName, portStr, shortName, author, createDate); + String result = scaffoldPod(moduleName, portStr, shortName, author, createDate); + System.out.println(result); } private static String getOrAsk(String[] args, int index, Scanner scanner, String prompt) { @@ -42,17 +43,17 @@ public class PodScaffolder { return scanner.nextLine().trim(); } - private static void scaffoldPod(String moduleName, String portStr, String shortName, String author, String createDate) throws IOException { + public static String scaffoldPod(String moduleName, String portStr, String shortName, String author, String createDate) throws IOException { Path modulePath = Paths.get(moduleName); if (Files.exists(modulePath)) { - System.out.println("[오류] 이미 존재하는 모듈입니다: " + moduleName); - return; + return "[오류] 이미 존재하는 모듈입니다: " + moduleName; } - System.out.println("\n[1/6] 모듈 디렉터리 생성 중..."); + StringBuilder log = new StringBuilder(); + log.append("[1/6] 모듈 디렉터리 생성 중...\n"); Files.createDirectories(modulePath); - System.out.println("[2/6] build.gradle 생성 중..."); + log.append("[2/6] build.gradle 생성 중...\n"); String buildGradle = """ plugins { id 'org.springframework.boot' @@ -67,7 +68,7 @@ public class PodScaffolder { """; Files.writeString(modulePath.resolve("build.gradle"), buildGradle); - System.out.println("[3/6] Dockerfile 생성 중..."); + log.append("[3/6] Dockerfile 생성 중...\n"); String dockerfile = """ FROM eclipse-temurin:21-jdk-alpine WORKDIR /app @@ -76,7 +77,7 @@ public class PodScaffolder { """.formatted(moduleName); Files.writeString(modulePath.resolve("Dockerfile"), dockerfile); - System.out.println("[4/6] Application 클래스 및 설정 파일 생성 중..."); + log.append("[4/6] Application 클래스 및 설정 파일 생성 중...\n"); Path srcPath = modulePath.resolve("src/main/java/io/shinhanlife/axhub/biz/mcp/tool/" + shortName); Files.createDirectories(srcPath); @@ -200,7 +201,7 @@ public class PodScaffolder { """.formatted(moduleName, moduleName); Files.writeString(resPath.resolve("logback-spring.xml"), logbackXml); - System.out.println("[5/6] settings.gradle 에 모듈 등록 중..."); + log.append("[5/6] settings.gradle 에 모듈 등록 중...\n"); Path settingsPath = Paths.get("settings.gradle"); if (Files.exists(settingsPath)) { String settings = Files.readString(settingsPath); @@ -209,7 +210,7 @@ public class PodScaffolder { } } - System.out.println("[6/6] docker-compose.yml 에 서비스 추가 중..."); + log.append("[6/6] docker-compose.yml 에 서비스 추가 중...\n"); Path dockerComposePath = Paths.get("docker-compose.yml"); if (Files.exists(dockerComposePath)) { String compose = Files.readString(dockerComposePath); @@ -241,12 +242,13 @@ public class PodScaffolder { } } - System.out.println("\\n========================================="); - System.out.println(" 🎉 Pod Scaffolding Complete! "); - System.out.println("========================================="); - System.out.println("1. [새로운 모듈] " + moduleName + " 폴더가 생성되었습니다."); - System.out.println("2. [ToolScaffolder]를 사용해 이 모듈 안에 툴을 추가하세요."); - System.out.println("3. 실행 전 Gradle 동기화(Sync)를 한 번 진행해 주세요."); + log.append("\n=========================================\n"); + log.append(" 🎉 Pod Scaffolding Complete! \n"); + log.append("=========================================\n"); + log.append("1. [새로운 모듈] ").append(moduleName).append(" 폴더가 생성되었습니다.\n"); + log.append("2. [ToolScaffolder]를 사용해 이 모듈 안에 툴을 추가하세요.\n"); + log.append("3. 실행 전 Gradle 동기화(Sync)를 한 번 진행해 주세요.\n"); + return log.toString(); } private static String capitalize(String str) { diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/util/ToolScaffolder.java b/axhub-common/src/main/java/io/shinhanlife/axhub/common/util/ToolScaffolder.java similarity index 87% rename from axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/util/ToolScaffolder.java rename to axhub-common/src/main/java/io/shinhanlife/axhub/common/util/ToolScaffolder.java index 53a90a0..0a41694 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/util/ToolScaffolder.java +++ b/axhub-common/src/main/java/io/shinhanlife/axhub/common/util/ToolScaffolder.java @@ -1,4 +1,4 @@ -package io.shinhanlife.axhub.biz.mcp.tool.util; +package io.shinhanlife.axhub.common.util; import java.io.IOException; import java.nio.file.Files; @@ -68,7 +68,8 @@ public class ToolScaffolder { String createDate = getOrAsk(args, 7, scanner, "8. 작성일 (엔터 입력 시 '" + defaultDate + "'): "); if (createDate.trim().isEmpty()) createDate = defaultDate; - scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, createDate); + String result = scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, createDate); + System.out.println(result); } private static String getOrAsk(String[] args, int index, Scanner scanner, String prompt) { @@ -79,13 +80,15 @@ public class ToolScaffolder { return scanner.nextLine().trim(); } - private static void scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate) throws IOException { + public static String scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate) throws IOException { Path serviceDir = Paths.get(moduleName, BASE_PACKAGE_PATH, "service"); Path dtoDir = Paths.get(moduleName, BASE_PACKAGE_PATH, "dto"); Files.createDirectories(serviceDir); Files.createDirectories(dtoDir); + StringBuilder log = new StringBuilder(); + // Generate Req DTO String reqContent = """ package %s.dto; @@ -212,16 +215,18 @@ public class ToolScaffolder { """.formatted(toolName, description, description + " 해줘.", interfaceId); yamlContent = yamlContent.replaceFirst("functions:", "functions:" + newFunctionYaml); Files.writeString(yamlPath, yamlContent); - System.out.println("[YAML] " + yamlPath + " (함수 설정 자동 등록됨)"); + log.append("[YAML] ").append(yamlPath).append(" (함수 설정 자동 등록됨)\n"); } } - System.out.println("\n========================================="); - System.out.println(" Scaffolding Complete!"); - System.out.println("========================================="); - System.out.println("[Service] " + serviceDir.resolve(baseName + "Service.java")); - System.out.println("[Req DTO] " + dtoDir.resolve(baseName + "Req.java")); - System.out.println("[Res DTO] " + dtoDir.resolve(baseName + "Res.java")); - System.out.println("\n Tip: " + interfaceId + " 목업 데이터를 mock-responses.json에 추가하세요."); + log.append("\n=========================================\n"); + log.append(" Scaffolding Complete!\n"); + log.append("=========================================\n"); + log.append("[Service] ").append(serviceDir.resolve(baseName + "Service.java")).append("\n"); + log.append("[Req DTO] ").append(dtoDir.resolve(baseName + "Req.java")).append("\n"); + log.append("[Res DTO] ").append(dtoDir.resolve(baseName + "Res.java")).append("\n"); + log.append("\n Tip: ").append(interfaceId).append(" 목업 데이터를 mock-responses.json에 추가하세요.\n"); + + return log.toString(); } } \ No newline at end of file diff --git a/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/presentation/ScaffoldingController.java b/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/presentation/ScaffoldingController.java new file mode 100644 index 0000000..3af0cfc --- /dev/null +++ b/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/presentation/ScaffoldingController.java @@ -0,0 +1,45 @@ +package io.shinhanlife.axhub.biz.mcp.gateway.presentation; + +import io.shinhanlife.axhub.common.util.PodScaffolder; +import io.shinhanlife.axhub.common.util.ToolScaffolder; +import org.springframework.web.bind.annotation.*; +import java.util.Map; + +@RestController +@RequestMapping("/api/v1/scaffold") +public class ScaffoldingController { + + @PostMapping("/pod") + public String scaffoldPod(@RequestBody Map req) { + try { + String moduleName = req.getOrDefault("moduleName", "axhub-tool-other"); + if (!moduleName.startsWith("axhub-tool-")) moduleName = "axhub-tool-" + moduleName; + String port = req.getOrDefault("port", "8085"); + String shortName = moduleName.replace("axhub-tool-", "").replace("-", ""); + String author = req.getOrDefault("author", "System"); + String date = req.getOrDefault("date", java.time.LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy.MM.dd"))); + + return PodScaffolder.scaffoldPod(moduleName, port, shortName, author, date); + } catch (Exception e) { + return "오류 발생: " + e.getMessage(); + } + } + + @PostMapping("/tool") + public String scaffoldTool(@RequestBody Map req) { + try { + String baseName = req.get("baseName"); + String interfaceId = req.get("interfaceId"); + String description = req.get("description"); + String group = req.getOrDefault("group", "COMMON"); + String routingType = req.getOrDefault("routingType", "HTTP"); + String moduleName = req.getOrDefault("moduleName", "axhub-tool-other"); + String author = req.getOrDefault("author", "System"); + String date = req.getOrDefault("date", java.time.LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy.MM.dd"))); + + return ToolScaffolder.scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, date); + } catch (Exception e) { + return "오류 발생: " + e.getMessage(); + } + } +} diff --git a/axhub-gateway/src/main/resources/static/admin/scaffold.html b/axhub-gateway/src/main/resources/static/admin/scaffold.html new file mode 100644 index 0000000..e369b67 --- /dev/null +++ b/axhub-gateway/src/main/resources/static/admin/scaffold.html @@ -0,0 +1,157 @@ + + + + + AXHUB Developer Portal - Scaffolding + + + + + + +
+

🚀 AXHUB Developer Portal

+
+
+ +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+
+ +
+
+ + + + + + +