Netty 서버 상태 추적 및 목업 라우트 관리 기능 개선
This commit is contained in:
@@ -14,25 +14,66 @@
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<form role="form" class="row g-3" name="searchForm" th:action="@{/mgmt/routes/list_view.do}" method="get" th:object="${modelSearch}">
|
||||
<form role="form" class="row g-3 align-items-center" name="searchForm" th:action="@{/mgmt/routes/list_view.do}" method="get" th:object="${modelSearch}">
|
||||
<input name="page" type="hidden" value="1"/>
|
||||
<div class="col-auto">
|
||||
<select class="form-select" name="group" th:value="${modelSearch.group}">
|
||||
<option value="" th:selected="${#strings.isEmpty(modelSearch.group)}">선택</option>
|
||||
<input name="size" type="hidden" th:value="${pageable.pageSize}"/>
|
||||
<input name="sort" type="hidden" th:value="${#strings.defaultString(param.sort, 'id,desc')}"/>
|
||||
|
||||
<!-- 그룹 목록 반복 -->
|
||||
<option th:each="group : ${groupList}"
|
||||
th:value="${group.id}"
|
||||
th:text="${group.name}"
|
||||
th:selected="${modelSearch.group} == ${group.id}">
|
||||
</option>
|
||||
</select>
|
||||
<!-- Group 복수 선택 드롭다운 -->
|
||||
<div class="col-auto">
|
||||
<div class="dropdown multi-select-dropdown">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" id="groupDropdownBtn">
|
||||
Group
|
||||
</button>
|
||||
<ul class="dropdown-menu p-2" style="min-width: 200px;">
|
||||
<li th:each="group : ${groupList}">
|
||||
<label class="dropdown-item d-flex align-items-center" style="cursor: pointer;">
|
||||
<input type="checkbox" class="form-check-input me-2 group-check"
|
||||
th:value="${group.id}"
|
||||
th:checked="${modelSearch.group != null AND #lists.contains(modelSearch.group, '' + group.id)}"/>
|
||||
<span th:text="${group.name}"></span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Method 복수 선택 드롭다운 -->
|
||||
<div class="col-auto">
|
||||
<div class="dropdown multi-select-dropdown">
|
||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" id="methodDropdownBtn">
|
||||
Method
|
||||
</button>
|
||||
<ul class="dropdown-menu p-2" style="min-width: 180px;">
|
||||
<li th:each="m : ${methodList}">
|
||||
<label class="dropdown-item d-flex align-items-center" style="cursor: pointer;">
|
||||
<input type="checkbox" class="form-check-input me-2 method-check"
|
||||
th:value="${m}"
|
||||
th:checked="${modelSearch.method != null AND #lists.contains(modelSearch.method, m)}"/>
|
||||
<span th:text="${m}"></span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<input class="form-control" name="name" placeholder="이름" type="text" size="35" th:title="#{title.search}+ '' + #{input.input}"
|
||||
th:value="${modelSearch.name}"
|
||||
maxlength="255"/>
|
||||
</div>
|
||||
|
||||
<!-- 정렬 드롭다운 -->
|
||||
<div class="col-auto">
|
||||
<select class="form-select" id="sortSelect">
|
||||
<option value="id,desc" th:selected="${#strings.defaultString(param.sort, 'id,desc') == 'id,desc'}">최신순</option>
|
||||
<option value="group,asc" th:selected="${param.sort == 'group,asc'}">Group</option>
|
||||
<option value="method,asc" th:selected="${param.sort == 'method,asc'}">Method</option>
|
||||
<option value="pathPattern,asc" th:selected="${param.sort == 'pathPattern,asc'}">Path</option>
|
||||
<option value="name,asc" th:selected="${param.sort == 'name,asc'}">이름</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary btn_search" th:value="#{button.inquire}"><i class="fa-sharp fa-solid fa-magnifying-glass"></i> [[#{title.inquire}]]
|
||||
</button>
|
||||
@@ -41,10 +82,10 @@
|
||||
<button type="button" class="btn btn-primary btn_delete"><i class="fa-sharp fa-solid fa-trash"></i> [[#{title.delete}]]</button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary btn_add"><i class="fa-sharp fa-solid fa-plus"></i> [[#{button.create}]]</button><!-- 등록 -->
|
||||
<button type="button" class="btn btn-primary btn_add"><i class="fa-sharp fa-solid fa-plus"></i> [[#{button.create}]]</button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-primary btn_add_group"><i class="fa-sharp fa-solid fa-plus"></i>그룹 관리</button><!-- 등록 -->
|
||||
<button type="button" class="btn btn-primary btn_add_group"><i class="fa-sharp fa-solid fa-plus"></i>그룹 관리</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -123,7 +164,7 @@
|
||||
</tr>
|
||||
<!-- 글이 없는 경우 -->
|
||||
<tr th:if="${page.content.size() == 0 }">
|
||||
<td colspan="6" th:text="#{common.nodata.msg}"></td>
|
||||
<td colspan="7" th:text="#{common.nodata.msg}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -133,7 +174,17 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"></div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"></div>
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="me-2" style="white-space: nowrap;">페이지당:</label>
|
||||
<select class="form-select form-select-sm" id="pageSizeSelect" style="width: auto;">
|
||||
<option value="20" th:selected="${pageable.pageSize == 20}">20</option>
|
||||
<option value="50" th:selected="${pageable.pageSize == 50}">50</option>
|
||||
<option value="100" th:selected="${pageable.pageSize == 100}">100</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -145,9 +196,12 @@
|
||||
|
||||
<th:block layout:fragment="contentScript">
|
||||
<script>
|
||||
function fn_select_page(pageNo) {
|
||||
function fn_select_page(pageNo, size) {
|
||||
document.searchForm.page.value = pageNo;
|
||||
document.searchForm.submit();
|
||||
if (size) {
|
||||
document.searchForm.size.value = size;
|
||||
}
|
||||
syncFiltersAndSubmit();
|
||||
}
|
||||
|
||||
function fnSelectItem(id) {
|
||||
@@ -159,17 +213,91 @@
|
||||
document.cloneForm.submit();
|
||||
}
|
||||
|
||||
$(function () {
|
||||
let fnSearch = document.querySelector(".btn_search");
|
||||
let fnDelete = document.querySelector(".btn_delete");
|
||||
let fnAdd = document.querySelector(".btn_add");
|
||||
let fnAddGroup = document.querySelector(".btn_add_group");
|
||||
function updateDropdownLabel(checkClass, btnId, defaultLabel) {
|
||||
var checks = document.querySelectorAll('.' + checkClass + ':checked');
|
||||
var btn = document.getElementById(btnId);
|
||||
if (checks.length > 0) {
|
||||
btn.textContent = defaultLabel + ' (' + checks.length + ')';
|
||||
} else {
|
||||
btn.textContent = defaultLabel;
|
||||
}
|
||||
}
|
||||
|
||||
fnSearch.addEventListener("click", function () {
|
||||
document.searchForm.page.value = 1;
|
||||
document.searchForm.submit();
|
||||
function syncFiltersAndSubmit() {
|
||||
var form = document.searchForm;
|
||||
|
||||
// 기존 group, method hidden input 제거
|
||||
var oldGroups = form.querySelectorAll('input[name="group"]');
|
||||
oldGroups.forEach(function(el) { el.remove(); });
|
||||
var oldMethods = form.querySelectorAll('input[name="method"]');
|
||||
oldMethods.forEach(function(el) { el.remove(); });
|
||||
|
||||
// 선택된 group 값 추가
|
||||
var groupChecks = document.querySelectorAll('.group-check:checked');
|
||||
groupChecks.forEach(function(cb) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'group';
|
||||
input.value = cb.value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
// 선택된 method 값 추가
|
||||
var methodChecks = document.querySelectorAll('.method-check:checked');
|
||||
methodChecks.forEach(function(cb) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'method';
|
||||
input.value = cb.value;
|
||||
form.appendChild(input);
|
||||
});
|
||||
|
||||
// sort 값 동기화
|
||||
form.sort.value = document.getElementById('sortSelect').value;
|
||||
|
||||
form.submit();
|
||||
}
|
||||
|
||||
$(function () {
|
||||
var fnSearch = document.querySelector(".btn_search");
|
||||
var fnDelete = document.querySelector(".btn_delete");
|
||||
var fnAdd = document.querySelector(".btn_add");
|
||||
var fnAddGroup = document.querySelector(".btn_add_group");
|
||||
|
||||
// localStorage에서 페이지 크기 복원
|
||||
var savedPageSize = localStorage.getItem('mockRouteListPageSize');
|
||||
if (savedPageSize) {
|
||||
var pageSizeSelect = document.getElementById('pageSizeSelect');
|
||||
pageSizeSelect.value = savedPageSize;
|
||||
// 현재 서버 페이지 크기와 다르면 반영
|
||||
if (document.searchForm.size.value !== savedPageSize) {
|
||||
document.searchForm.size.value = savedPageSize;
|
||||
}
|
||||
}
|
||||
|
||||
// 드롭다운 라벨 초기화
|
||||
updateDropdownLabel('group-check', 'groupDropdownBtn', 'Group');
|
||||
updateDropdownLabel('method-check', 'methodDropdownBtn', 'Method');
|
||||
|
||||
// 체크박스 변경 시 라벨 업데이트
|
||||
document.querySelectorAll('.group-check').forEach(function(cb) {
|
||||
cb.addEventListener('change', function() {
|
||||
updateDropdownLabel('group-check', 'groupDropdownBtn', 'Group');
|
||||
});
|
||||
});
|
||||
document.querySelectorAll('.method-check').forEach(function(cb) {
|
||||
cb.addEventListener('change', function() {
|
||||
updateDropdownLabel('method-check', 'methodDropdownBtn', 'Method');
|
||||
});
|
||||
});
|
||||
|
||||
// 조회 버튼
|
||||
fnSearch.addEventListener("click", function () {
|
||||
document.searchForm.page.value = 1;
|
||||
syncFiltersAndSubmit();
|
||||
});
|
||||
|
||||
// 삭제 버튼
|
||||
fnDelete.addEventListener("click", function () {
|
||||
var checkField = document.listForm.checkField;
|
||||
var id = document.listForm.checkId;
|
||||
@@ -206,6 +334,21 @@
|
||||
fnAddGroup.addEventListener("click", function () {
|
||||
location.href = '[[@{/mgmt/routes/group/list_view.do}]]';
|
||||
});
|
||||
|
||||
// 정렬 변경 시 자동 submit
|
||||
document.getElementById('sortSelect').addEventListener('change', function() {
|
||||
document.searchForm.page.value = 1;
|
||||
syncFiltersAndSubmit();
|
||||
});
|
||||
|
||||
// 페이지 크기 변경
|
||||
document.getElementById('pageSizeSelect').addEventListener('change', function() {
|
||||
var newSize = this.value;
|
||||
localStorage.setItem('mockRouteListPageSize', newSize);
|
||||
document.searchForm.size.value = newSize;
|
||||
document.searchForm.page.value = 1;
|
||||
syncFiltersAndSubmit();
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -59,6 +59,46 @@
|
||||
<label class="form-check-label" for="active">Active</label>
|
||||
</div>
|
||||
|
||||
<!-- Message Format Settings -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">Message Format</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="form-floating">
|
||||
<input type="number" class="form-control"
|
||||
th:field="*{prefixSize}" min="0" max="20"/>
|
||||
<label for="prefixSize" class="form-label">Prefix Size</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-check mt-3">
|
||||
<input type="checkbox" class="form-check-input"
|
||||
th:field="*{includePrefixLength}" id="includePrefixLength"/>
|
||||
<label class="form-check-label" for="includePrefixLength">Include Prefix in Length</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-floating">
|
||||
<input type="number" class="form-control"
|
||||
th:field="*{serviceIdLength}" min="0" max="100"/>
|
||||
<label for="serviceIdLength" class="form-label">Service ID Length</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-floating">
|
||||
<select class="form-select" th:field="*{serviceIdPosition}">
|
||||
<option value="NONE">None</option>
|
||||
<option value="START">Start</option>
|
||||
<option value="END">End</option>
|
||||
</select>
|
||||
<label for="serviceIdPosition" class="form-label">Service ID Position</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Script Editor -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">Server Script</div>
|
||||
|
||||
@@ -92,6 +92,8 @@
|
||||
<td class="text-center">
|
||||
<span th:class="${row.active ? 'badge bg-success' : 'badge bg-danger'}"
|
||||
th:text="${row.active ? 'Active' : 'Inactive'}"></span>
|
||||
<span th:if="${runningStatus[row.port]}" class="badge bg-primary">Started</span>
|
||||
<span th:unless="${runningStatus[row.port]}" class="badge bg-secondary">Stopped</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span th:text="${#temporals.format(row.lastModified, 'yyyy-MM-dd HH:mm:ss')}"></span>
|
||||
|
||||
Reference in New Issue
Block a user