Netty 서버 상태 추적 및 목업 라우트 관리 기능 개선
This commit is contained in:
@@ -39,3 +39,8 @@ out/
|
|||||||
ApiTestManager/node_modules
|
ApiTestManager/node_modules
|
||||||
ApiTestManager/dist
|
ApiTestManager/dist
|
||||||
.cache
|
.cache
|
||||||
|
.env
|
||||||
|
CLAUDE.md
|
||||||
|
|
||||||
|
src/main/generated
|
||||||
|
.claude
|
||||||
@@ -8,6 +8,7 @@ import com.eactive.testmaster.api.entity.MockRoute;
|
|||||||
import com.eactive.testmaster.api.mapper.MockRouteMapper;
|
import com.eactive.testmaster.api.mapper.MockRouteMapper;
|
||||||
import com.eactive.testmaster.api.repository.MockRouteGroupRepository;
|
import com.eactive.testmaster.api.repository.MockRouteGroupRepository;
|
||||||
import com.eactive.testmaster.api.service.MockRouteService;
|
import com.eactive.testmaster.api.service.MockRouteService;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -66,6 +67,7 @@ public class MockRouteMgmtController {
|
|||||||
mav.addObject("pageable", pageable);
|
mav.addObject("pageable", pageable);
|
||||||
mav.addObject("modelSearch", mockRouteSearch);
|
mav.addObject("modelSearch", mockRouteSearch);
|
||||||
mav.addObject("groupList", mockRouteGroupRepository.findAll());
|
mav.addObject("groupList", mockRouteGroupRepository.findAll());
|
||||||
|
mav.addObject("methodList", Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"));
|
||||||
|
|
||||||
return mav;
|
return mav;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,16 @@ public class MockRouteSearch implements BaseSearch<MockRoute> {
|
|||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private String group;
|
private List<String> group;
|
||||||
|
|
||||||
|
private List<String> method;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SearchModel> buildSearchCondition() {
|
public List<SearchModel> buildSearchCondition() {
|
||||||
List<SearchModel> models = new ArrayList<>();
|
List<SearchModel> models = new ArrayList<>();
|
||||||
models.add(new ColumnSearchModel("name", SearchCondition.LIKE, name));
|
models.add(new ColumnSearchModel("name", SearchCondition.LIKE, name));
|
||||||
models.add(new ColumnSearchModel("group", SearchCondition.EQUAL, group));
|
models.add(new ColumnSearchModel("group", SearchCondition.IN, group));
|
||||||
|
models.add(new ColumnSearchModel("method", SearchCondition.IN, method));
|
||||||
return models;
|
return models;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,11 +33,19 @@ public class MockRouteSearch implements BaseSearch<MockRoute> {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGroup() {
|
public List<String> getGroup() {
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGroup(String group) {
|
public void setGroup(List<String> group) {
|
||||||
this.group = group;
|
this.group = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMethod(List<String> method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,12 @@ public class Console {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Console.class);
|
private static final Logger logger = LoggerFactory.getLogger(Console.class);
|
||||||
|
|
||||||
public void log(String message) {
|
public void log(Object... args) {
|
||||||
logger.info(message);
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (i > 0) sb.append(" ");
|
||||||
|
sb.append(String.valueOf(args[i]));
|
||||||
|
}
|
||||||
|
logger.info(sb.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import com.eactive.testmaster.loadtest.service.Console;
|
||||||
|
import javax.script.Bindings;
|
||||||
import javax.script.Invocable;
|
import javax.script.Invocable;
|
||||||
|
import javax.script.ScriptContext;
|
||||||
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngine;
|
||||||
import javax.script.ScriptEngineManager;
|
import javax.script.ScriptEngineManager;
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
@@ -23,6 +26,9 @@ public class NettySocketHandler extends ChannelInboundHandlerAdapter {
|
|||||||
ScriptEngineManager manager = new ScriptEngineManager();
|
ScriptEngineManager manager = new ScriptEngineManager();
|
||||||
this.engine = manager.getEngineByName("nashorn");
|
this.engine = manager.getEngineByName("nashorn");
|
||||||
try {
|
try {
|
||||||
|
Bindings bindings = engine.createBindings();
|
||||||
|
bindings.put("console", new Console());
|
||||||
|
engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
|
||||||
engine.eval(scriptContent);
|
engine.eval(scriptContent);
|
||||||
} catch (ScriptException e) {
|
} catch (ScriptException e) {
|
||||||
logger.error("Failed to initialize script", e);
|
logger.error("Failed to initialize script", e);
|
||||||
|
|||||||
+7
@@ -2,7 +2,9 @@ package com.eactive.testmaster.socket.controller;
|
|||||||
|
|
||||||
import com.eactive.testmaster.socket.entity.NettyServerEntity;
|
import com.eactive.testmaster.socket.entity.NettyServerEntity;
|
||||||
import com.eactive.testmaster.socket.service.NettyServerService;
|
import com.eactive.testmaster.socket.service.NettyServerService;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@@ -40,9 +42,14 @@ public class NettyTCPServerMgmtController {
|
|||||||
Pageable pageable) {
|
Pageable pageable) {
|
||||||
ModelAndView mav = new ModelAndView(NETTY_SERVER_LIST_VIEW);
|
ModelAndView mav = new ModelAndView(NETTY_SERVER_LIST_VIEW);
|
||||||
Page<NettyServerEntity> page = nettyServerService.findAll(serverSearch.buildSpecification(), pageable);
|
Page<NettyServerEntity> page = nettyServerService.findAll(serverSearch.buildSpecification(), pageable);
|
||||||
|
Map<Integer, Boolean> runningStatus = new HashMap<>();
|
||||||
|
for (NettyServerEntity server : page.getContent()) {
|
||||||
|
runningStatus.put(server.getPort(), nettyServerService.isServerRunning(server.getPort()));
|
||||||
|
}
|
||||||
mav.addObject("page", page);
|
mav.addObject("page", page);
|
||||||
mav.addObject("pageable", pageable);
|
mav.addObject("pageable", pageable);
|
||||||
mav.addObject("modelSearch", serverSearch);
|
mav.addObject("modelSearch", serverSearch);
|
||||||
|
mav.addObject("runningStatus", runningStatus);
|
||||||
return mav;
|
return mav;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,11 @@ public class NettyServerService {
|
|||||||
existingServer.setPort(updatedServer.getPort());
|
existingServer.setPort(updatedServer.getPort());
|
||||||
existingServer.setActive(updatedServer.getActive());
|
existingServer.setActive(updatedServer.getActive());
|
||||||
existingServer.setName(updatedServer.getName());
|
existingServer.setName(updatedServer.getName());
|
||||||
existingServer.setScript(updatedServer.getScript()); // Add this line to update the script
|
existingServer.setScript(updatedServer.getScript());
|
||||||
|
existingServer.setPrefixSize(updatedServer.getPrefixSize());
|
||||||
|
existingServer.setIncludePrefixLength(updatedServer.getIncludePrefixLength());
|
||||||
|
existingServer.setServiceIdLength(updatedServer.getServiceIdLength());
|
||||||
|
existingServer.setServiceIdPosition(updatedServer.getServiceIdPosition());
|
||||||
|
|
||||||
return repository.save(existingServer);
|
return repository.save(existingServer);
|
||||||
}
|
}
|
||||||
@@ -116,10 +120,9 @@ public class NettyServerService {
|
|||||||
// return repository.findByActive(true);
|
// return repository.findByActive(true);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// @Transactional(readOnly = true)
|
public boolean isServerRunning(Integer port) {
|
||||||
// public boolean isServerRunning(Integer port) {
|
return activeServers.containsKey(port);
|
||||||
// return activeServers.containsKey(port);
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// @Transactional(readOnly = true)
|
// @Transactional(readOnly = true)
|
||||||
// public Map<Integer, Boolean> getServerStatuses(List<Integer> ports) {
|
// public Map<Integer, Boolean> getServerStatuses(List<Integer> ports) {
|
||||||
|
|||||||
@@ -14,25 +14,66 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="d-flex justify-content-between">
|
<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"/>
|
<input name="page" type="hidden" value="1"/>
|
||||||
<div class="col-auto">
|
<input name="size" type="hidden" th:value="${pageable.pageSize}"/>
|
||||||
<select class="form-select" name="group" th:value="${modelSearch.group}">
|
<input name="sort" type="hidden" th:value="${#strings.defaultString(param.sort, 'id,desc')}"/>
|
||||||
<option value="" th:selected="${#strings.isEmpty(modelSearch.group)}">선택</option>
|
|
||||||
|
|
||||||
<!-- 그룹 목록 반복 -->
|
<!-- Group 복수 선택 드롭다운 -->
|
||||||
<option th:each="group : ${groupList}"
|
<div class="col-auto">
|
||||||
th:value="${group.id}"
|
<div class="dropdown multi-select-dropdown">
|
||||||
th:text="${group.name}"
|
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" id="groupDropdownBtn">
|
||||||
th:selected="${modelSearch.group} == ${group.id}">
|
Group
|
||||||
</option>
|
</button>
|
||||||
</select>
|
<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>
|
</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">
|
<div class="col-auto">
|
||||||
<input class="form-control" name="name" placeholder="이름" type="text" size="35" th:title="#{title.search}+ '' + #{input.input}"
|
<input class="form-control" name="name" placeholder="이름" type="text" size="35" th:title="#{title.search}+ '' + #{input.input}"
|
||||||
th:value="${modelSearch.name}"
|
th:value="${modelSearch.name}"
|
||||||
maxlength="255"/>
|
maxlength="255"/>
|
||||||
</div>
|
</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">
|
<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 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>
|
</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>
|
<button type="button" class="btn btn-primary btn_delete"><i class="fa-sharp fa-solid fa-trash"></i> [[#{title.delete}]]</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<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>
|
||||||
<div class="col-auto">
|
<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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -123,7 +164,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<!-- 글이 없는 경우 -->
|
<!-- 글이 없는 경우 -->
|
||||||
<tr th:if="${page.content.size() == 0 }">
|
<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>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -133,7 +174,17 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -145,9 +196,12 @@
|
|||||||
|
|
||||||
<th:block layout:fragment="contentScript">
|
<th:block layout:fragment="contentScript">
|
||||||
<script>
|
<script>
|
||||||
function fn_select_page(pageNo) {
|
function fn_select_page(pageNo, size) {
|
||||||
document.searchForm.page.value = pageNo;
|
document.searchForm.page.value = pageNo;
|
||||||
document.searchForm.submit();
|
if (size) {
|
||||||
|
document.searchForm.size.value = size;
|
||||||
|
}
|
||||||
|
syncFiltersAndSubmit();
|
||||||
}
|
}
|
||||||
|
|
||||||
function fnSelectItem(id) {
|
function fnSelectItem(id) {
|
||||||
@@ -159,17 +213,91 @@
|
|||||||
document.cloneForm.submit();
|
document.cloneForm.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$(function () {
|
function updateDropdownLabel(checkClass, btnId, defaultLabel) {
|
||||||
let fnSearch = document.querySelector(".btn_search");
|
var checks = document.querySelectorAll('.' + checkClass + ':checked');
|
||||||
let fnDelete = document.querySelector(".btn_delete");
|
var btn = document.getElementById(btnId);
|
||||||
let fnAdd = document.querySelector(".btn_add");
|
if (checks.length > 0) {
|
||||||
let fnAddGroup = document.querySelector(".btn_add_group");
|
btn.textContent = defaultLabel + ' (' + checks.length + ')';
|
||||||
|
} else {
|
||||||
|
btn.textContent = defaultLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fnSearch.addEventListener("click", function () {
|
function syncFiltersAndSubmit() {
|
||||||
document.searchForm.page.value = 1;
|
var form = document.searchForm;
|
||||||
document.searchForm.submit();
|
|
||||||
|
// 기존 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 () {
|
fnDelete.addEventListener("click", function () {
|
||||||
var checkField = document.listForm.checkField;
|
var checkField = document.listForm.checkField;
|
||||||
var id = document.listForm.checkId;
|
var id = document.listForm.checkId;
|
||||||
@@ -206,6 +334,21 @@
|
|||||||
fnAddGroup.addEventListener("click", function () {
|
fnAddGroup.addEventListener("click", function () {
|
||||||
location.href = '[[@{/mgmt/routes/group/list_view.do}]]';
|
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>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,46 @@
|
|||||||
<label class="form-check-label" for="active">Active</label>
|
<label class="form-check-label" for="active">Active</label>
|
||||||
</div>
|
</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 -->
|
<!-- Script Editor -->
|
||||||
<div class="card mt-3">
|
<div class="card mt-3">
|
||||||
<div class="card-header">Server Script</div>
|
<div class="card-header">Server Script</div>
|
||||||
|
|||||||
@@ -92,6 +92,8 @@
|
|||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<span th:class="${row.active ? 'badge bg-success' : 'badge bg-danger'}"
|
<span th:class="${row.active ? 'badge bg-success' : 'badge bg-danger'}"
|
||||||
th:text="${row.active ? 'Active' : 'Inactive'}"></span>
|
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>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<span th:text="${#temporals.format(row.lastModified, 'yyyy-MM-dd HH:mm:ss')}"></span>
|
<span th:text="${#temporals.format(row.lastModified, 'yyyy-MM-dd HH:mm:ss')}"></span>
|
||||||
|
|||||||
Reference in New Issue
Block a user