feat(scaffold): add native folder picker for Workspace Path selection
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 39s

- Added /api/v1/scaffold/browse-folder endpoint in ScaffoldingController to open native JFileChooser.
- Updated scaffold.html to use the new API for both Pod Module and Tool Function.
- Fixed JS event listener bug where workspacePath changes only affected Pod Module.
This commit is contained in:
jade
2026-08-20 09:22:03 +09:00
parent d2fb804e5c
commit 64c422378f
2 changed files with 95 additions and 9 deletions

View File

@@ -67,12 +67,37 @@ public class ScaffoldingController {
String date = req.get("date");
if (date == null || date.trim().isEmpty()) date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd"));
return PodScaffolder.scaffoldPod(moduleName, port, shortName, author, date);
String defaultWorkspace = "c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dap-was-dapmt";
String workspacePath = req.getOrDefault("workspacePath", defaultWorkspace);
if (workspacePath.trim().isEmpty()) {
workspacePath = defaultWorkspace;
}
System.setProperty("AXHUB_SOURCE_DIR", workspacePath);
return PodScaffolder.scaffoldPod(moduleName, port, shortName, author, date);
} catch (Exception e) {
return "오류 발생: " + e.getMessage();
}
}
@GetMapping("/browse-folder")
public String browseFolder() {
try {
System.setProperty("java.awt.headless", "false");
javax.swing.JFileChooser chooser = new javax.swing.JFileChooser();
chooser.setCurrentDirectory(new java.io.File("c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov"));
chooser.setDialogTitle("Select Workspace Folder");
chooser.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
if (chooser.showOpenDialog(null) == javax.swing.JFileChooser.APPROVE_OPTION) {
return chooser.getSelectedFile().getAbsolutePath();
}
} catch (Exception e) {
return "Error: " + e.getMessage();
}
return "";
}
@PostMapping("/tool")
public String scaffoldTool(@RequestBody Map<String, String> req) {
try {

View File

@@ -797,6 +797,35 @@
<!-- Pod Creation Form -->
<div class="tab-pane fade show active" id="pod" role="tabpanel">
<form id="podForm">
<div class="row mb-4">
<div class="col-md-12">
<label class="form-label">Workspace Path</label>
<div class="input-group">
<input type="text" class="form-control" name="workspacePath" id="podWorkspacePathInput" placeholder="직접 폴더 경로를 입력(붙여넣기) 하거나 선택하세요" value="c:\eGovFrameDev-4.3.1-64bit\workspace-egov\dap-was-dapmt">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false" style="border-color: #3f3f46; color: #a1a1aa;">폴더 선택</button>
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-dark" style="background-color: #18181b; border-color: #3f3f46;">
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('podWorkspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dap-was-dapmt'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">dap-was-dapmt (기본)</a></li>
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('podWorkspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\axhub-backend-main'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">axhub-backend-main</a></li>
<li><hr class="dropdown-divider" style="border-color: #3f3f46;"></li>
<li><a class="dropdown-item" href="#" onclick="openServerFolderPicker('podWorkspacePathInput'); return false;" style="color: #f4f4f5;">폴더 선택(+)</a></li>
</ul>
</div>
<input type="file" id="podFolderPicker" webkitdirectory directory style="display: none;" onchange="
if(this.files && this.files.length > 0) { alert("Path: " + this.files[0].path + ", webkitRel: " + this.files[0].webkitRelativePath + ", name: " + this.files[0].name); if(this.files[0].path) {
let firstFilePath = this.files[0].path;
let relPath = this.files[0].webkitRelativePath;
let folderPath = firstFilePath.substring(0, firstFilePath.length - relPath.length);
if (folderPath.endsWith('\\') || folderPath.endsWith('/')) {
folderPath = folderPath.substring(0, folderPath.length - 1);
}
const input = document.getElementById('podWorkspacePathInput');
input.value = folderPath;
input.dispatchEvent(new Event('change'));
}
">
<div class="input-hint">직접 경로를 붙여넣거나 우측 <b>[폴더 선택]</b> 버튼을 눌러 선택하세요.</div>
</div>
</div>
<div class="mb-4">
<label class="form-label">Module Name</label>
<input type="text" class="form-control" name="moduleName" placeholder="e.g. hr (will be prefixed with axhub-tool-)" required>
@@ -834,8 +863,23 @@
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-dark" style="background-color: #18181b; border-color: #3f3f46;">
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('workspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\dap-was-dapmt'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">dap-was-dapmt (기본)</a></li>
<li><a class="dropdown-item" href="#" onclick="const input = document.getElementById('workspacePathInput'); input.value='c:\\eGovFrameDev-4.3.1-64bit\\workspace-egov\\axhub-backend-main'; input.dispatchEvent(new Event('change')); return false;" style="color: #f4f4f5;">axhub-backend-main</a></li>
<li><hr class="dropdown-divider" style="border-color: #3f3f46;"></li>
<li><a class="dropdown-item" href="#" onclick="openServerFolderPicker('workspacePathInput'); return false;" style="color: #f4f4f5;">폴더 선택(+)</a></li>
</ul>
</div>
<input type="file" id="toolFolderPicker" webkitdirectory directory style="display: none;" onchange="
if(this.files && this.files.length > 0) { alert("Path: " + this.files[0].path + ", webkitRel: " + this.files[0].webkitRelativePath + ", name: " + this.files[0].name); if(this.files[0].path) {
let firstFilePath = this.files[0].path;
let relPath = this.files[0].webkitRelativePath;
let folderPath = firstFilePath.substring(0, firstFilePath.length - relPath.length);
if (folderPath.endsWith('\\') || folderPath.endsWith('/')) {
folderPath = folderPath.substring(0, folderPath.length - 1);
}
const input = document.getElementById('workspacePathInput');
input.value = folderPath;
input.dispatchEvent(new Event('change'));
}
">
<div class="input-hint">직접 경로를 붙여넣거나 우측 <b>[폴더 선택]</b> 버튼을 눌러 선택하세요.</div>
</div>
</div>
@@ -1536,7 +1580,7 @@
document.addEventListener('DOMContentLoaded', function() {
const loadModules = () => {
const workspacePath = document.querySelector('input[name="workspacePath"]')?.value || '';
const workspacePath = document.querySelector('#toolForm input[name="workspacePath"]')?.value || document.querySelector('input[name="workspacePath"]')?.value || '';
fetch(`/api/v1/scaffold/modules?workspacePath=${encodeURIComponent(workspacePath)}`)
.then(res => res.json())
.then(modules => {
@@ -1551,11 +1595,11 @@
.catch(err => console.error('Failed to load modules:', err));
};
loadModules();
const wsInput = document.querySelector('input[name="workspacePath"]');
if (wsInput) {
wsInput.addEventListener('change', loadModules);
wsInput.addEventListener('blur', loadModules); // Typing custom path and clicking away
}
const wsInputs = document.querySelectorAll('input[name="workspacePath"]');
wsInputs.forEach(wsInput => {
wsInput.addEventListener('change', loadModules);
wsInput.addEventListener('blur', loadModules);
});
});
handleFormSubmit('podForm', '/api/v1/scaffold/pod');
@@ -1626,7 +1670,7 @@
async function loadUseCasesForSelection() {
const moduleName = document.getElementById('targetModuleSelect').value;
const workspacePath = document.querySelector('input[name="workspacePath"]')?.value || '';
const workspacePath = document.querySelector('#toolForm input[name="workspacePath"]')?.value || document.querySelector('input[name="workspacePath"]')?.value || '';
const categoryKey = document.querySelector('#toolForm [name="categoryKey"]').value.trim();
const select = document.getElementById('toolGroupUseCaseSelect');
select.innerHTML = '<option value="">새 UseCase 생성</option>';
@@ -1643,7 +1687,7 @@
async function loadCategoriesForSelection() {
const moduleName = document.getElementById('targetModuleSelect').value;
const workspacePath = document.querySelector('input[name="workspacePath"]')?.value || '';
const workspacePath = document.querySelector('#toolForm input[name="workspacePath"]')?.value || document.querySelector('input[name="workspacePath"]')?.value || '';
const select = document.getElementById('domainCategorySelect');
const nameInput = document.getElementById('domainCategoryName');
select.innerHTML = '<option value="">새 도메인 생성</option>';
@@ -2348,5 +2392,22 @@
}
</script>
<script>
function openServerFolderPicker(inputId) {
fetch("/api/v1/scaffold/browse-folder")
.then(res => res.text())
.then(path => {
if(path && !path.startsWith("Error:") && path !== "") {
const input = document.getElementById(inputId);
input.value = path;
input.dispatchEvent(new Event("change"));
} else if (path.startsWith("Error:")) {
alert(path);
}
})
.catch(err => console.error(err));
}
</script>
</body>
</html>