기본 골격

This commit is contained in:
현성필
2023-05-08 10:16:00 +09:00
parent 169ba6448c
commit 2412d289c3
606 changed files with 214162 additions and 114780 deletions
+21
View File
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>Error Page</title>
<meta http-equiv="refresh" content="5;url=/">
<style>
.error-code {
font-size: 100vh;
font-weight: bold;
color: #ff8484;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<div class="error-code" th:text="${status}">400</div>
</body>
</html>
@@ -1,10 +1,22 @@
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<footer th:fragment="footerFragment" class="main-footer">
<strong>Copyright &copy; 2014-2021 <a
href="https://adminlte.io">AdminLTE.io</a>.
</strong> All rights reserved.
<div class="float-right d-none d-sm-inline-block">
<b>Version</b> 3.1.0
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
</head>
<body>
<footer th:fragment="footerFragment">
<div class="container footer">
<!-- <button type="button" id="top" class="scroll_top"></button>-->
<div class="foot_info">
<span>서울본사 : 서울특별시 영등포구 양평로 157, 1411호 (〒07207)</span><br/>
<span>대전지사 : 대전광역시 유성구 온천로 60 사이언스타운 728호 (〒34187)</span><br/>
<span>판교지사 : 경기도 성남시 분당구 삼평동 662 판교타워 506호 (〒13524)</span><br/>
<span>TEL : (02) 6335-0010 FAX : (02) 6335-0707 E-mail : sales@eactive.co.kr</span><br/>
<span>Copyright © 2020 (주)이액티브. All rights reserved.</span><br/>
</div>
</div>
</footer>
</html>
</body>
</html>
@@ -0,0 +1,52 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head th:fragment="headFragment">
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta content="max-age=0, public" http-equiv="Cache-Control"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta content="index, follow" name="robots"/>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta content="https://www.eactive.co.kr/" property="og:url"/>
<title th:text="#{title.html}">개발자 포털</title>
<link href="/css/font.css" rel="stylesheet" type="text/css"/>
<link rel="stylesheet" href="/css/fontawesome/css/all.min.css" type="text/css"/>
<link href="/css/bootstrap/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="/css/custom.css" rel="stylesheet" type="text/css"/>
<link rel="stylesheet" href="/css/codemirror/codemirror.css" type="text/css"/>
<link rel="stylesheet" href="/css/codemirror/theme/monokai.css" type="text/css"/>
<link rel="stylesheet" href="/js/summernote/summernote-lite.min.css" type="text/css" />
<link rel="stylesheet" href="/css/dataTables/datatables.min.css" />
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<script src="/js/jquery/jquery.min.js"></script>
<script src="/js/bootstrap/bootstrap.bundle.min.js"></script>
<script src="/js/underscore/underscore-min.js"></script>
<script src="/js/lodash.js"></script>
<script src="/js/util.js"></script>
<script src="/js/http.js"></script>
<script src="/js/jquery-serialize-object/jquery.serialize-object.js"></script>
<script src="/js/jquery-validation/jquery.validate.min.js"></script>
<script src="/js/jquery-validation/additional-methods.min.js"></script>
<script src="/js/jquery-validation/localization/messages_ko.min.js"></script>
<script src="/js/common.js"></script>
<script src="/js/codemirror/codemirror.js"></script>
<script src="/js/codemirror/mode/javascript/javascript.js"></script>
<script src="/js/codemirror/addon/display/fullscreen.js"></script>
<script src="/js/codemirror/addon/display/placeholder.js"></script>
<script src="/js/summernote/summernote-lite.min.js"></script>
<script src="/js/dataTables/datatables.min.js"></script>
<script type="text/javascript">
</script>
</head>
</html>
@@ -0,0 +1,78 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="utf-8">
</head>
<body>
<header th:fragment="headerFragment" class="p-3 border-bottom" id="header">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
<span class="fs-4">API Simulator</span>
</a>
<div class="row container">
<div class="col text-center">
<div class="nav-item dropdown">
<a class="dropdown-label" href="/mgmt/routes/list_view.do">API 관리</a>
</div>
</div>
<div class="col">
<a class="dropdown-label" href="/mgmt/api_tester.do">API Tester</a>
</div>
<div class="col-md-6 d-flex justify-content-between">
<div class="col text-center">
<div sec:authorize="hasRole('ROLE_ADMIN')" class="nav-item dropdown">
<a class="nav-link" href="#" id="mgmtDropDown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
관리
</a>
<ul class="dropdown-menu" aria-labelledby="mgmtDropDown">
<li><a class="dropdown-item" href="/mgmt/users/list_view.do">사용자 관리</a></li>
<li><a class="dropdown-item" href="/mgmt/servers/list_view.do">서버 관리</a></li>
</ul>
</div>
</div>
<div class="col text-end">
<ul sec:authorize="isAnonymous()" class="nav">
<li class="nav-item"><a href="/login" class="nav-link">로그인</a></li>
</ul>
<ul sec:authorize="isAuthenticated()" class="nav">
<li class="nav-item"><a href="/mypage.do" class="nav-link">비밀번호 변경</a></li>
<li class="nav-item"><a href="/actionLogout.do" class="nav-link">로그 아웃</a></li>
</ul>
</div>
</div>
</div>
</div>
</header>
<nav th:fragment="navFragment" class="d-flex flex-wrap align-items-center justify-content-center border-bottom" id="nav_bar">
</nav>
<!-- header script -->
<th:block th:fragment="headerScript">
<script th:inline="javascript">
$(function () {
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader("X-XSRF-TOKEN", /*[[${_csrf.token}]]*/ );
}
});
$(window).scroll(function () {
var offsetHeight = document.getElementById("header").offsetHeight;
if ($(window).scrollTop() > offsetHeight) {
$('#nav_bar').addClass('fixed_top');
}
if ($(window).scrollTop() < offsetHeight) {
$('#nav_bar').removeClass('fixed_top');
}
});
});
</script>
</th:block>
</body>
</html>
@@ -0,0 +1,46 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="utf-8">
</head>
<body>
<aside class="pt-sidebar" th:fragment="menuFragment">
<div class="collapse pt-links" id="side_menubar" aria-label="sidebar menu">
<ul class="list-unstyled mb-0 py-3 pt-md-1">
<li><a href="/mypage.do" class="d-inline-flex align-items-center rounded">내 정보 변경</a></li>
<li><a href="/change_password.do" class="d-inline-flex align-items-center rounded">비밀번호 변경</a></li>
<li><a href="/mgmt/routes/list_view.do" class="d-inline-flex align-items-center rounded">API 관리</a></li>
<li><a href="/mgmt/api_tester.do" class="d-inline-flex align-items-center rounded">API 테스터</a></li>
<th:block sec:authorize="hasRole('ROLE_ADMIN')">
<li class="mb-1">
<button class="btn d-inline-flex align-items-center rounded" data-bs-toggle="collapse" data-bs-target="#system-collapse" aria-expanded="false" aria-current="false">
시스템 관리
</button>
<div class="collapse" id="system-collapse">
<ul class="list-unstyled fw-normal pb-1 small">
<li><a href="/mgmt/users/list_view.do" class="d-inline-flex align-items-center rounded">사용자 관리</a></li>
<li><a href="/mgmt/servers/list_view.do" class="d-inline-flex align-items-center rounded">서버 관리</a></li>
</ul>
</div>
</li>
</th:block>
</ul>
</div>
</aside>
<!-- menu script -->
<th:block th:fragment="menuScript">
<script th:inline="javascript">
$(function () {
});
</script>
</th:block>
</body>
</html>
@@ -0,0 +1,58 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
</head>
<body>
<div th:fragment="pagination">
<nav aria-label="Page navigation">
<ul class="pagination"
th:with="index_diff=0,offset_minus2=${ page.getNumber() - 1}, offset_minus1=${ page.getNumber() }, current_page=${ page.getNumber() + 1 }, offset_plus1=${ page.getNumber() + 2 }, offset_plus2=${ page.getNumber() + 3}">
<li class="page-item" th:classappend="${ current_page == 1}?'disabled'">
<a class="page-link" href="#" th:attr="onclick=|${jsFunction}(1,${page.getSize()});|">
<span aria-hidden="true">처음</span>
</a>
</li>
<li class="page-item" th:if="${ offset_minus2 > 0}">
<a class="page-link" href="#" th:attr="onclick=|${jsFunction}(${offset_minus2 - index_diff },${page.getSize()});|">
<span aria-hidden="true" th:text="${offset_minus2}"></span>
</a>
</li>
<li class="page-item" th:if="${ offset_minus1 > 0}">
<a class="page-link" href="#" th:attr="onclick=|${jsFunction}(${offset_minus1 - index_diff },${page.getSize()});|">
<span aria-hidden="true" th:text="${offset_minus1 }"></span>
</a>
</li>
<li class="page-item disabled">
<a class="page-link" style="font-weight: bold" th:text="${current_page}"></a>
</li>
<li class="page-item" th:if="${offset_plus1 <= page.getTotalPages()}">
<a class="page-link" href="#" th:attr="onclick=|${jsFunction}(${offset_plus1 - index_diff },${page.getSize()});|">
<span aria-hidden="true" th:text="${offset_plus1}"></span>
</a>
</li>
<li class="page-item" th:if="${offset_plus2 <= page.getTotalPages()}">
<a class="page-link" href="#" th:attr="onclick=|${jsFunction}(${offset_plus2 - index_diff },${page.getSize()});|">
<span aria-hidden="true" th:text="${offset_plus2}"></span>
</a>
</li>
<li class="page-item" th:classappend="${ (page.getTotalPages()) - current_page <= 0 } ? 'disabled'">
<a class="page-link" href="#" th:attr="onclick=|${jsFunction}(${page.getTotalPages() - index_diff },${page.getSize()});|">
<span aria-hidden="true">마지막</span>
</a>
</li>
</ul>
<div>
[총 <span th:text="${page.getTotalElements()}"></span>건, <span th:text="${page.getNumber() + 1}"></span>/<span th:text="${page.getTotalPages()}"></span>페이지]
</div>
</nav>
<script>
</script>
</div>
</body>
</html>
@@ -0,0 +1,28 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head th:replace="fragment/head :: headFragment">
<meta charset="utf-8">
</head>
<body>
<!-- header -->
<header th:replace="fragment/header :: headerFragment"></header>
<th:block th:replace="fragment/header :: headerScript"></th:block>
<nav th:replace="fragment/header :: navFragment"></nav>
<div>
<section layout:fragment="contentFragment">
</section>
<th:block layout:fragment="contentScript">
</th:block>
</div>
<footer th:replace="fragment/footer :: footerFragment"></footer>
</body>
</html>
@@ -1,76 +1,33 @@
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Http Mock Server</title>
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<th:block th:replace="fragment/config :: configFragment"></th:block>
<head th:replace="fragment/head :: headFragment">
<meta charset="utf-8">
</head>
<body class="hold-transition sidebar-mini">
<div class="wrapper">
<!-- partial:partials/_navbar.html -->
<th:block th:replace="fragment/navbar :: navbarFragment"></th:block>
<!-- partial:partials/_sidebar.html -->
<th:block th:replace="fragment/sidebar :: sidebarFragment"></th:block>
<body>
<!-- content-wrapper-->
<!-- content fragment(현재 layout을 사용하는 content fragment의 내용을 삽입) -->
<th:block layout:fragment="content"></th:block>
<!-- header -->
<header th:replace="fragment/header :: headerFragment"></header>
<th:block th:replace="fragment/header :: headerScript"></th:block>
<nav th:replace="fragment/header :: navFragment"></nav>
<!-- partial:partials/_footer.html -->
<th:block th:replace="fragment/footer :: footerFragment">
</th:block>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Control sidebar content goes here -->
</aside>
<!-- /.control-sidebar -->
</div>
<!-- jQuery -->
<script th:src="@{/plugins/jquery/jquery.min.js}"></script>
<!-- jQuery UI 1.11.4 -->
<script th:src="@{/plugins/jquery-ui/jquery-ui.min.js}"></script>
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
<script>
$.widget.bridge('uibutton', $.ui.button)
</script>
<!-- Bootstrap 4 -->
<script th:src="@{/plugins/bootstrap/js/bootstrap.bundle.min.js}"></script>
<!-- csrf -->
<input id="csrf" type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<div class="">
<section class="" layout:fragment="contentFragment">
</section>
<th:block layout:fragment="contentScript">
</th:block>
</div>
<footer th:replace="fragment/footer :: footerFragment"></footer>
<!-- AdminLTE App -->
<script th:src="@{/dist/js/adminlte.js}"></script>
<!-- AdminLTE for demo purposes -->
<script th:src="@{/dist/js/demo.js}"></script>
<!-- Content Page의 script fragment 삽입 -->
<th:block layout:fragment="script"></th:block>
<script>
$(function () {
var url = window.location;
if (typeof urlForSidebar !== 'undefined') {
url = window.location.protocol + '//' + window.location.host + urlForSidebar;
}
// for single sidebar menu
$('ul.nav-sidebar a').filter(function () {
return this.href == url;
}).addClass('active');
// for sidebar menu and treeview
$('ul.nav-treeview a').filter(function () {
return this.href == url;
}).parentsUntil(".nav-sidebar > .nav-treeview")
.css({'display': 'block'})
.addClass('menu-open').prev('a')
.addClass('active');
});
</script>
</body>
</html>
</html>
@@ -1,79 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 th:if="${apiServiceKey.id == null}">API 서비스키 등록</h1>
<h1 th:unless="${apiServiceKey.id == null}">API 서비스키 수정</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">API 관리</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<form class="inputForm" action="#" th:action="@{/manage/saveApiKey}"
th:object="${apiServiceKey}" method="post">
<div class="card card-default">
<!--
<div class="card-header">
<h3 class="card-title">Basic Info</h3>
</div>
-->
<div class="card-body">
<div class="form-group">
<label for="url">URL<small>(*)</small></label> <input type="text"
th:field="*{url}" class="form-control"
placeholder="ex) /mock-api/~~"> <span
th:if="${#fields.hasErrors('url')}" th:errors="*{url}"
class="text-danger"></span>
</div>
<div class="form-group">
<label for="serviceType">서비스키타입<small>(*)</small></label> <select
class="form-control" th:field="*{serviceType}">
<option value="header">header</option>
<option value="parameter">parameter</option>
<option value="json-body">json-body</option>
<option value="flat-data">flat-data(서비스키명:
start-position, length)</option>
</select>
</div>
<div class="form-group">
<label for="serviceKey">서비스키명<small>(*)</small></label> <input
type="text" th:field="*{serviceKey}" class="form-control">
<span th:if="${#fields.hasErrors('serviceKey')}"
th:errors="*{serviceKey}" class="text-danger"></span>
</div>
</div>
</div>
<input type="hidden" th:field="*{id}">
<button type="submit" class="btn btn-primary mr-2">Submit</button>
<button type="button" class="btn btn-secondary"
onclick="history.go(-1);">Cancel</button>
</form>
</div>
</section>
</div>
<th:block layout:fragment="script">
<script th:inline="javascript">
var urlForSidebar = "/manage/findAllApiKeys";
</script>
</th:block>
</html>
@@ -1,308 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 th:if="${apiInfo.id == null}">API 정보 등록</h1>
<h1 th:unless="${apiInfo.id == null}">API 정보 수정</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">API 관리</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content" style="padding-bottom: 1rem;">
<div class="container-fluid">
<form id="form" class="inputForm" action="#" th:action="@{/manage/saveApi}"
th:object="${apiInfo}" method="post">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Basic Info</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">API 그룹명<small>(*)</small></label>
<div class="col-sm-9">
<input type="text" th:field="*{apiGroupName}"
class="form-control"> <span
th:if="${#fields.hasErrors('apiGroupName')}"
th:errors="*{apiGroupName}" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">API 명<small>(*)</small></label>
<div class="col-sm-9">
<input type="text" th:field="*{apiName}" class="form-control">
<span th:if="${#fields.hasErrors('apiName')}"
th:errors="*{apiName}" class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">URL<small>(*)</small></label>
<div class="col-sm-9">
<input type="text" th:field="*{url}" class="form-control"
placeholder="ex) /mock-api/~~"> <span
th:if="${#fields.hasErrors('url')}" th:errors="*{url}"
class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Method<small>(*)</small></label>
<div class="col-sm-2">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{method}" class="form-check-input" value="POST">
POST <i class="input-helper"></i></label>
</div>
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{method}" class="form-check-input" value="">
ALL <i class="input-helper"></i></label>
</div>
</div>
<div class="col-sm-2">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{method}" class="form-check-input" value="GET">
GET <i class="input-helper"></i></label>
</div>
</div>
<div class="col-sm-2">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{method}" class="form-check-input" value="PUT">
PUT <i class="input-helper"></i></label>
</div>
</div>
<div class="col-sm-3">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{method}" class="form-check-input" value="DELETE">
DELETE <i class="input-helper"></i></label>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">서비스코드</label>
<div class="col-sm-9">
<input type="text" th:field="*{serviceValue}"
class="form-control">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Response Info</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Content Type</label>
<div class="col-sm-9">
<input type="text" th:field="*{responseContentType}"
class="form-control">
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">응답 Headers</label>
<div class="col-sm-9">
<input type="text" th:field="*{responseHeaders}"
class="form-control"
placeholder="ex) api-code=A10,user-id=James,...">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">응답 Status Code</label>
<div class="col-sm-9">
<input type="text" th:field="*{responseStatusCode}"
class="form-control"> <span
th:if="${#fields.hasErrors('responseStatusCode')}" th:errors="*{responseStatusCode}"
class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Sleep 타임(초)</label>
<div class="col-sm-9">
<input type="text" th:field="*{sleepSeconds}"
class="form-control"> <span
th:if="${#fields.hasErrors('sleepSeconds')}" th:errors="*{sleepSeconds}"
class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">스크립트 명</label>
<div class="col-sm-9">
<select class="form-control select2bs4" th:field="*{scriptName}" th:value="*{scriptName}"> style="width: 100%;" >
<option value="">선택안함</option>
<option th:each="scriptInfo : ${scriptInfos}" th:value="${scriptInfo.scriptName}" th:text="${scriptInfo.scriptName + '(' + scriptInfo.scriptDesc + ')'}"></option>
</select>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Syntax 응답 Body1</label>
<div class="col-sm-9">
<select class="form-control" th:field="*{syntaxResponseBody1}" th:value="*{syntaxResponseBody1}">
<option th:value="'plaintext'" th:text="TEXT"></option>
<option th:value="'json'" th:text="JSON"></option>
<option th:value="'xml'" th:text="XML"></option>
</select>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Syntax 응답 Body2</label>
<div class="col-sm-9">
<select class="form-control" th:field="*{syntaxResponseBody2}" th:value="*{syntaxResponseBody2}">
<option th:value="'plaintext'" th:text="TEXT"></option>
<option th:value="'json'" th:text="JSON"></option>
<option th:value="'xml'" th:text="XML"></option>
</select>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">응답 Body1</label>
<div class="col-sm-9">
<div id="responseBody1" class="shadow-sm" style="height: 200px; border: 1px solid #ced4da; border-radius: .25rem;"></div>
<!-- <textarea class="form-control" th:field="*{responseBody1}"-->
<!-- rows="3"></textarea>-->
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">응답 Body2</label>
<div class="col-sm-9">
<!-- <textarea class="form-control" th:field="*{responseBody2}"-->
<!-- rows="3"></textarea>-->
<div id="responseBody2" class="shadow-sm" style="height: 200px; border: 1px solid #ced4da; border-radius: .25rem;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<input type="hidden" th:field="*{id}">
<button type="submit" class="btn btn-primary mr-2">Submit</button>
<button type="button" class="btn btn-secondary"
onclick="history.go(-1);">Cancel</button>
</form>
</div>
</section>
</div>
<th:block layout:fragment="script">
<link rel="stylesheet" data-name="vs/editor/editor.main"
th:href="@{/plugins/vs/editor/editor.main.css}">
<link rel="stylesheet" th:href="@{/plugins/select2/css/select2.min.css}">
<link rel="stylesheet" th:href="@{/plugins/select2-bootstrap4-theme/select2-bootstrap4.min.css}">
<script th:src="@{/plugins/select2/js/select2.full.js}"></script>
<script>var require = { paths: { 'vs': '../../plugins/vs' } };</script>
<script th:src="@{/plugins/vs/loader.js}"></script>
<script th:src="@{/plugins/vs/editor/editor.main.nls.js}"></script>
<script th:src="@{/plugins/vs/editor/editor.main.js}"></script>
<script th:inline="javascript">
var urlForSidebar = "/manage/findAllApis";
//Initialize Select2 Elements
$('.select2bs4').select2({
theme: 'bootstrap4'
})
function setMonaco(elementId, selectId, initValue){
var targetDivElement = document.getElementById(elementId);
var lang = $("#"+selectId+" option:selected").val();
var editor = monaco.editor.create(targetDivElement, {
value: initValue,
language: lang,
minimap: {
enabled: false
},
wordWrap: 'on',
wrappingStrategy: 'simple',
wordWrapBreakAfterCharacters: '',
wordWrapBreakBeforeCharacters: '',
wrappingIndent: 'none',
automaticLayout: true
});
$("#"+selectId).change(function(){
var selectLang = $("#"+selectId+" option:selected").val();
monaco.editor.setModelLanguage(editor.getModel(), selectLang);
});
$('#form').submit(function(eventObj) {
$("<input />").attr("type", "hidden")
.attr("name", elementId)
.attr("value", editor.getValue())
.appendTo("#form");
return true;
});
}
setMonaco('responseBody1', 'syntaxResponseBody1', /*[[${apiInfo.responseBody1}]]*/);
setMonaco('responseBody2', 'syntaxResponseBody2', /*[[${apiInfo.responseBody2}]]*/);
</script>
</th:block>
</html>
@@ -1,239 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 th:if="${bytesMessageSpec.id == null}">Bytes Message 정보 등록</h1>
<h1 th:unless="${bytesMessageSpec.id == null}">Bytes Message 정보 수정</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">Echo Socket</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content" style="padding-bottom: 1rem;">
<div class="container-fluid">
<form id="bytesMessageSpecForm" name="bytesMessageSpecForm" class="inputForm" action="#" th:action="@{/manage/saveBytesMessageSpec}"
th:object="${bytesMessageSpec}" method="post">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Basic Info</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Message명<small>(*)</small></label>
<div class="col-sm-9">
<input type="text" th:field="*{specName}"
class="form-control"> <span
th:if="${#fields.hasErrors('specName')}"
th:errors="*{specName}" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Encode</label>
<div class="col-sm-9">
<input type="text" th:field="*{encode}" class="form-control" placeholder="EUC-KR">
<span th:if="${#fields.hasErrors('encode')}"
th:errors="*{encode}" class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">LL길이<small>(*)</small></label>
<div class="col-sm-9">
<input type="text" th:field="*{llFieldLength}" class="form-control"> <span
th:if="${#fields.hasErrors('llFieldLength')}" th:errors="*{llFieldLength}"
class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">LL길이포함</label>
<div class="col-sm-4">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{llFieldLengthInclude}" class="form-check-input" value="Y">
Y <i class="input-helper"></i></label>
</div>
</div>
<div class="col-sm-5">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{llFieldLengthInclude}" class="form-check-input" value="N">
N <i class="input-helper"></i></label>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Port<small>(*)</small></label>
<div class="col-sm-9">
<input type="text" th:field="*{port}" class="form-control" placeholder="38181"> <span
th:if="${#fields.hasErrors('port')}" th:errors="*{port}"
class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">사용여부</label>
<div class="col-sm-4">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{useYn}" class="form-check-input" value="Y">
Y <i class="input-helper"></i></label>
</div>
</div>
<div class="col-sm-5">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{useYn}" class="form-check-input" value="N">
N <i class="input-helper"></i></label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Response Message Info</h3>
<br>
<small>&nbsp; <font color="blue">- 응답 메시지를 입력할 경우 아래 Acho Message Info 정보는 무시 됩니다.</font></small>
</div>
<div class="card-body">
<div class="form-group">
<label for="url">응답 메시지</label>
<textarea class="form-control" th:field="*{responseBody}" rows="3"></textarea>
</div>
</div>
</div>
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Acho Message Info</h3>
<button type="button" id="addReplaceRule" class="btn btn-sm btn-success float-right">
<i class="fas fa-plus"></i>
</button>
</div>
<div class="card-body">
<table id="replaceRuleTable" class="table">
<thead>
<tr>
<th>No.</th>
<th>Replace 시작 Index</th>
<th>Replace 스트링</th>
<th>삭제</th>
</tr>
</thead>
<tbody id="dynamicTableContents">
<tr th:fragment="echoReplaceString"
th:each="echoReplaceString,rowStat : ${bytesMessageSpec.getEchoReplaceStringList()}">
<td th:text="${rowStat.count}">1</td>
<td>
<input type="hidden" th:field="${bytesMessageSpec.echoReplaceStringList[__${rowStat.index}__].id}">
<input type="text" th:field="${bytesMessageSpec.echoReplaceStringList[__${rowStat.index}__].position}" class="form-control"> <span
th:if="${#fields.hasErrors('${bytesMessageSpec.echoReplaceStringList[__${rowStat.index}__].position}')}"
th:errors="${bytesMessageSpec.echoReplaceStringList[__${rowStat.index}__].position}"
class="text-danger"></span>
</td>
<td>
<input type="text" th:field="${bytesMessageSpec.echoReplaceStringList[__${rowStat.index}__].replaceString}" class="form-control"> <span
th:if="${#fields.hasErrors('echoReplaceStringList[__${rowStat.index}__].replaceString')}"
th:errors="*{echoReplaceStringList[__${rowStat.index}__].replaceString}"
class="text-danger"></span>
</td>
<td>
<div class="btn-group">
<button type="button" id="removeDynamicRow" class="btn btn-sm btn-danger">
<i class="far fa-trash-alt"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<input type="hidden" th:field="*{id}">
<button type="button" id="submitButton" class="btn btn-primary mr-2">Submit</button>
<button type="button" class="btn btn-secondary"
onclick="history.go(-1);">Cancel</button>
</form>
</div>
</section>
</div>
<th:block layout:fragment="script">
<script th:inline="javascript">
var urlForSidebar = "/manage/findAllBytesMessageSpecs";
$("#addReplaceRule").click(function() {
var idx = $('#dynamicTableContents tr:last td:first').text();
try {
if (idx) {
idx++;
} else {
idx = 1;
}
} catch (error) {}
var trStr = '<tr><td>' + idx + '</td>'
+ '<td><input type="text" class="form-control"></td>'
+ '<td><input type="text" class="form-control"></td>'
+ '<td><div class="btn-group"><button type="button" id="removeDynamicRow" class="btn btn-sm btn-danger"><i class="far fa-trash-alt"></i></button></div></td></tr>';
$("#dynamicTableContents").append(trStr);
});
$("#replaceRuleTable").on("click", "#removeDynamicRow", function() {
$(this).closest("tr").remove();
});
$("#submitButton").click(function() {
$('#dynamicTableContents > tr').each(function(index, tr) {
var idValue = $(tr).find("td:eq(1) input[type=hidden]").val();
if (idValue) {
$(tr).find("td:eq(1) input[type=hidden]").attr('name', 'echoReplaceStringList[' + index + '].id');
}
$(tr).find("td:eq(1) input[type=text]").attr('name', 'echoReplaceStringList[' + index + '].position');
$(tr).find("td:eq(2) input[type=text]").attr('name', 'echoReplaceStringList[' + index + '].replaceString');
});
$(this).css({'cursor':'wait'});
$(this).attr('disabled', true);
document.body.style.cursor = 'wait';
document.bytesMessageSpecForm.submit();
});
</script>
</th:block>
</html>
@@ -1,321 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 th:if="${httpRequestInfo.id == null}">HTTP 요청 정보 등록</h1>
<h1 th:unless="${httpRequestInfo.id == null}">HTTP 요청 정보 수정</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">부가 기능</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content" style="padding-bottom: 1rem;">
<div class="container-fluid">
<form id="form" class="inputForm" action="#" th:action="@{/manage/saveHttpRequestInfo}"
th:object="${httpRequestInfo}" method="post">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Basic Info</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">요청 그룹명<small>(*)</small></label>
<div class="col-sm-9">
<input type="text" th:field="*{requestGroupName}"
class="form-control"> <span
th:if="${#fields.hasErrors('requestGroupName')}"
th:errors="*{requestGroupName}" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">요청명<small>(*)</small></label>
<div class="col-sm-9">
<input type="text" th:field="*{requestName}" class="form-control">
<span th:if="${#fields.hasErrors('requestName')}"
th:errors="*{requestName}" class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">URL<small>(*)</small></label>
<div class="col-sm-9">
<input type="text" th:field="*{url}" class="form-control"
placeholder="ex) http://~~:8080/api/~~"> <span
th:if="${#fields.hasErrors('url')}" th:errors="*{url}"
class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Method<small>(*)</small></label>
<div class="col-sm-2">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{method}" class="form-check-input" value="POST">
POST <i class="input-helper"></i></label>
</div>
</div>
<div class="col-sm-2">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{method}" class="form-check-input" value="GET">
GET <i class="input-helper"></i></label>
</div>
</div>
<div class="col-sm-2">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{method}" class="form-check-input" value="PUT">
PUT <i class="input-helper"></i></label>
</div>
</div>
<div class="col-sm-3">
<div class="form-radio">
<label class="form-check-label"> <input type="radio"
th:field="*{method}" class="form-check-input" value="DELETE">
DELETE <i class="input-helper"></i></label>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">URL Query Params</label>
<div class="col-sm-9">
<input type="text" th:field="*{queryParams}"
class="form-control"
placeholder="ex) apiCode=A10&userId=James&...">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Request Info</h3>
</div>
<div class="card-body">
<div class="form-group row">
<label class="col-sm-3 col-form-label">Content Type</label>
<div class="col-sm-9">
<select class="form-control" th:field="*{requestContentType}" th:value="*{requestContentType}">
<option th:value="'application/json'" th:text="'application/json'"></option>
<option th:value="'application/xml'" th:text="'application/xml'"></option>
<option th:value="'application/x-www-form-urlencoded'" th:text="'application/x-www-form-urlencoded'"></option>
<option th:value="'text/plain'" th:text="'text/plain'"></option>
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Encode</label>
<div class="col-sm-9">
<input type="text" th:field="*{encode}"
class="form-control">
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">요청 Headers</label>
<div class="col-sm-9">
<input type="text" th:field="*{requestHeaders}"
class="form-control"
placeholder="ex) api-code=A10,user-id=James,...">
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">요청 Body</label>
<div class="col-sm-9">
<div id="requestBodyDiv" class="shadow-sm" style="height: 200px; border: 1px solid #ced4da; border-radius: .25rem;"></div>
<input type="hidden" th:field="*{requestBody}">
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Response Data</h3>
</div>
<div class="card-body">
<div class="form-group row">
<label class="col-sm-3 col-form-label">응답 Status Code</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="responseStatusCode" name="responseStatusCode">
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">응답 Headers</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="responseHeaders" name="responseHeaders">
</div>
</div>
<div class="row" style="margin-top: 2em;">
<label class="col-sm-3 col-form-label">&nbsp;</label>
<div class="col-sm-9">
<button type="button" class="btn btn-default btn-sm" id="btn_json" style="float: right;">Format JSON</button>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">응답 Body</label>
<div class="col-sm-9">
<div id="responseBody" class="shadow-sm" style="height: 200px; border: 1px solid #ced4da; border-radius: .25rem;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<input type="hidden" th:field="*{id}">
<button type="submit" class="btn btn-primary mr-2">Submit</button>
<button type="button" class="btn btn-secondary"
onclick="history.go(-1);">Cancel</button>
<button type="button" class="btn btn-primary mr-2" id="execBtn" style="float: right;">Execute</button>
</form>
</div>
</section>
</div>
<th:block layout:fragment="script">
<link rel="stylesheet" data-name="vs/editor/editor.main"
th:href="@{/plugins/vs/editor/editor.main.css}">
<link rel="stylesheet" th:href="@{/plugins/select2/css/select2.min.css}">
<link rel="stylesheet" th:href="@{/plugins/select2-bootstrap4-theme/select2-bootstrap4.min.css}">
<script th:src="@{/plugins/select2/js/select2.full.js}"></script>
<script>var require = { paths: { 'vs': '../../plugins/vs' } };</script>
<script th:src="@{/plugins/vs/loader.js}"></script>
<script th:src="@{/plugins/vs/editor/editor.main.nls.js}"></script>
<script th:src="@{/plugins/vs/editor/editor.main.js}"></script>
<script th:inline="javascript">
var urlForSidebar = "/manage/findAllHttpRequestInfos";
//Initialize Select2 Elements
$('.select2bs4').select2({
theme: 'bootstrap4'
})
$("#execBtn").click(function() {
$("#requestBody").val(window.editor.getValue());
var postData = $('#form').serializeArray();
$.ajax({
url : "/manage/executeHttpRequest",
data : postData,
dataType : "json",
type : "POST",
success : function(raw) {
$("#responseStatusCode").val(raw.statusCode);
$("#responseHeaders").val(raw.headers);
window.editor2.setValue(raw.body);
},
error : function(e) {
alert("전송에 실패했습니다.\n" + e.responseText);
}
});
});
$("#btn_json").click(function() {
try {
var jsonObj = JSON.parse(window.editor2.getValue());
window.editor2.setValue(JSON.stringify(jsonObj, null, 4));
} catch (e) {}
});
function setMonaco(elementId, selectId, initValue){
var targetDivElement = document.getElementById(elementId);
var lang = $("#"+selectId+" option:selected").val();
window.editor = monaco.editor.create(targetDivElement, {
value: initValue,
language: lang,
minimap: {
enabled: false
},
wordWrap: 'on',
wrappingStrategy: 'simple',
wordWrapBreakAfterCharacters: '',
wordWrapBreakBeforeCharacters: '',
wrappingIndent: 'none',
automaticLayout: true
});
$("#"+selectId).change(function(){
var selectLang = $("#"+selectId+" option:selected").val();
if (selectLang == "application/x-www-form-urlencoded") {
alert("요청 Body에 'userId=aaa&userName=이링크' 형식으로 입력해야 합니다.");
}
monaco.editor.setModelLanguage(editor.getModel(), selectLang);
});
$('#form').submit(function(eventObj) {
$("#requestBody").val(window.editor.getValue());
return true;
});
}
function setMonaco2(elementId, selectId, initValue){
var targetDivElement = document.getElementById(elementId);
var lang = $("#"+selectId+" option:selected").val();
window.editor2 = monaco.editor.create(targetDivElement, {
value: initValue,
language: lang,
minimap: {
enabled: false
},
wordWrap: 'on',
wrappingStrategy: 'simple',
wordWrapBreakAfterCharacters: '',
wordWrapBreakBeforeCharacters: '',
wrappingIndent: 'none',
automaticLayout: true
});
$("#"+selectId).change(function(){
var selectLang = $("#"+selectId+" option:selected").val();
monaco.editor.setModelLanguage(window.editor2.getModel(), selectLang);
});
}
setMonaco('requestBodyDiv', 'requestContentType', /*[[${httpRequestInfo.requestBody}]]*/);
setMonaco2('responseBody', 'requestContentType', "");
</script>
</th:block>
</html>
@@ -1,215 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 th:if="${scriptInfo.id == null}">스크립트 정보 등록</h1>
<h1 th:unless="${scriptInfo.id == null}">스크립트 정보 수정</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">스크립트 관리</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content" style="padding-bottom: 1rem;">
<div class="container-fluid">
<form id="form" class="inputForm" action="#" th:action="@{/manage/saveScriptInfo}"
th:object="${scriptInfo}" method="post">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Basic Info</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">스크립트 명<small>(*)</small></label>
<div class="col-sm-9">
<input id="scriptName" type="text" th:field="*{scriptName}" class="form-control">
<span th:if="${#fields.hasErrors('scriptName')}"
th:errors="*{scriptName}" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group row">
<label class="col-sm-3 col-form-label">스크립트 설명<small>(*)</small></label>
<div class="col-sm-9">
<input id="scriptName" type="text" th:field="*{scriptDesc}" class="form-control">
<span th:if="${#fields.hasErrors('scriptDesc')}"
th:errors="*{scriptDesc}" class="text-danger"></span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Script Code</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="form-group row">
<label class="col-sm-1 col-form-label">Script Editor</label>
<div class="col-sm-11">
<div id="scriptCode" class="shadow-sm" style="height:400px"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<input type="hidden" th:field="*{id}">
<button type="submit" class="btn btn-primary mr-2">Submit</button>
<button type="button" class="btn btn-secondary"
onclick="history.go(-1);">Cancel</button>
</form>
</div>
</section>
</div>
<th:block layout:fragment="script">
<link rel="stylesheet" data-name="vs/editor/editor.main"
th:href="@{/plugins/vs/editor/editor.main.css}">
<link rel="stylesheet"
th:href="@{/plugins/vs/plugin/constrained-editor-plugin/constrainedEditorPlugin.css}">
<script th:src="@{/plugins/vs/plugin/constrained-editor-plugin/constrainedEditorPlugin.js}"></script>
<script>var require = { paths: { 'vs': '../../plugins/vs' } };</script>
<script th:src="@{/plugins/vs/loader.js}"></script>
<script th:src="@{/plugins/vs/editor/editor.main.nls.js}"></script>
<script th:src="@{/plugins/vs/editor/editor.main.js}"></script>
<script th:inline="javascript">
var urlForSidebar = "/manage/findAllScriptInfos";
let editor;
let constrainedInstance;
const isNew = /*[[${scriptInfo.id == null}]]*/ false;
function findLineNumberByText(text){
const linesContent = editor.getModel().getLinesContent();
for (let i = 0; i < linesContent.length; i++) {
const lineText = linesContent[i].trim();
if(lineText.startsWith(text)){
return i + 1
}
}
return 14;
}
function setMainRestrictions(isNew){
const model = editor.getModel();
let editableLine = findLineNumberByText('public String postProcess') + 1;
const linesContent = editor.getModel().getLinesContent();
const editableLineTextLength = linesContent[editableLine - 1].length + 1;
const mainRestrictions = [{
// range : [ startLine, startColumn, endLine, endColumn ]
range: [3, 1, 3, 26], // Range of Util Variable name
label: 'import',
allowMultiline: true
}, {
range: [editableLine, 1, editableLine, editableLineTextLength], // Range of Function definition
allowMultiline: true,
label: 'funcDefinition'
}]
if(isNew){
constrainedInstance.addRestrictionsTo(model, mainRestrictions);
}else{
model.updateRestrictions(mainRestrictions);
}
}
function setMonaco(elementId, initValue){
var targetDivElement = document.getElementById(elementId);
editor = monaco.editor.create(targetDivElement, {
value: initValue,
language: 'java',
minimap: {
enabled: false
},
wordWrap: 'on',
wrappingStrategy: 'simple',
wordWrapBreakAfterCharacters: '',
wordWrapBreakBeforeCharacters: '',
wrappingIndent: 'none',
automaticLayout: true
});
constrainedInstance = constrainedEditor(monaco);
constrainedInstance.initializeIn(editor);
const model = editor.getModel();
setMainRestrictions(true);
// model.toggleHighlightOfEditableAreas();
$('#form').submit(function(eventObj) {
$('#scriptName').prop( "disabled", false );
$("<input />").attr("type", "hidden")
.attr("name", elementId)
.attr("value", editor.getValue())
.appendTo("#form");
return true;
});
}
setMonaco('scriptCode', /*[[${scriptInfo.scriptCode}]]*/);
if(isNew == false){
$('#scriptName').prop( "disabled", true );
}
$('#scriptName').on("change keyup paste", function(){
const scriptName = $('#scriptName').val();
const classLine = findLineNumberByText('public class')
const model = editor.getModel();
const linesContent = model.getLinesContent();
const currentTextLength = linesContent[classLine - 1].length + 1;
// model.toggleHighlightOfEditableAreas();
model.updateRestrictions([
{
range: [classLine, 1, classLine, currentTextLength],
label: "classLine"
}
]);
const newText = `public class ${scriptName} implements Script{`;
editor.executeEdits("my-source", [{ range: new monaco.Range(classLine, 1, classLine, currentTextLength), text: newText, forceMoveMarkers: true }]);
setMainRestrictions(false);
// model.toggleHighlightOfEditableAreas();
console.log(model.getCurrentEditableRanges());
// constrainedInstance.addRestrictionsTo(model, [{
// // range : [ startLine, startColumn, endLine, endColumn ]
// range: [classLine, 1, classLine, currentTextLength], // Range of Util Variable name
// label: 'className',
// allowMultiline: false
// }]);
});
</script>
</th:block>
</html>
@@ -0,0 +1,364 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/layout/default_layout}">
<body>
<section layout:fragment="contentFragment">
<div class="container mt-1">
<div class="card col-md">
<div class="card-header">
<h3>API Tester</h3>
</div>
<div class="card-body">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<div class="d-flex align-items-center">
<div class="form-floating col-md-2 me-1">
<select name="method" class="form-select" id="method">
<option value="POST">POST</option>
<option value="GET">GET</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
<option value="CONNECT">CONNECT</option>
<option value="HEAD">HEAD</option>
<option value="OPTIONS">OPTIONS</option>
<option value="TRACE">TRACE</option>
<option value="PATCH">PATCH</option>
</select>
<label for="method" class="form-label must">Method</label>
</div>
<div class="form-floating col-md-3 me-1">
<select name="server" class="form-select">
<option th:each="server : ${servers}" th:value="${server.id}"
th:text="${server.name}"></option>
</select>
<label for="method" class="form-label must">Server</label>
</div>
<div class="input-group col-md">
<div class="form-floating col-md">
<input type="text" name="path" class="form-control" id="path" placeholder="호출할 경로 (/부터 입력)"
required>
<label for="path" class="must">Path</label>
</div>
<button class="btn btn-primary" id="send">Send</button>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md">
<div class="card">
<div class="card-header">
<h4>요청</h4>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">Header 이름</th>
<th scope="col"></th>
<th>
<button type="button" class="btn btn-secondary btn-sm btn_add_header">추가
</button>
</th>
</tr>
</thead>
<tbody id="headers">
</tbody>
</table>
</div>
<div class="card-body">
<table class="table" id="requestMatcherParamTable">
<thead>
<tr>
<th scope="col">Query 파라미터 이름</th>
<th></th>
<th>
<button type="button" class="btn btn-secondary btn-sm btn_add_query">추가
</button>
</th>
</tr>
</thead>
<tbody id="queryParams">
</tbody>
</table>
</div>
<div class="card-body">
<label class="form-label must">Request Body</label>
<div class="">
<textarea class="form-control" style="height: 200px;" id="requestBody">
</textarea>
</div>
</div>
</div>
</div>
<div class="col-md">
<div class="card">
<div class="card-header">
<h4>응답</h4>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">Header 이름</th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="responseHeaders">
</tbody>
</table>
</div>
<div class="card-body">
<label class="form-label must">Response Body</label>
<div class="form-floating">
<textarea class="form-control" style="height: 200px;" id="responseBody"></textarea>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="inputTemplate" style="visibility: hidden; height: 0;">
<table>
<tbody>
<tr>
<td>
<input class="form-control" type="text" name="key"/>
</td>
<td>
<input class="form-control" type="text" name="value"/>
</td>
<td>
<button type="button" class="btn btn-secondary btn-sm btn_remove">제거</button>
</td>
</tr>
</tbody>
</table>
</div>
<div id="responseHeaderTemplate" style="visibility: hidden; height: 0;">
<table>
<tbody>
<tr>
<td>
<input class="form-control" type="text" name="key"/>
</td>
<td>
<input class="form-control" type="text" name="value"/>
</td>
</tr>
</tbody>
</table>
</div>
</section>
</body>
<th:block layout:fragment="contentScript">
<script>
$(function () {
let model = {
server: null,
method: 'POST',
path: '',
headers: [],
queryParams: [],
requestBody: ''
};
let view = {
init: function () {
$('.btn_add_header').click(function () {
controller.addHeader();
});
$('.btn_add_query').click(function () {
controller.addQueryParam('','');
});
$('#requestBody').on('input', function () {
model.requestBody = $(this).val();
})
model.server = $('#server').val();
model.method = $('#method').val();
$('#server').on('change', function(){
model.server = $(this).val();
});
$('#method').on('change', function(){
model.method = $(this).val();
});
$('#path').on('input', function(){
model.path = $(this).val();
//parse querystring from path and set to model.queryParams
model.queryParams = [];
let queryString = model.path.split('?')[1];
if(queryString){
let queryParamArray = queryString.split('&');
for(let i = 0; i < queryParamArray.length; i++){
let queryParam = queryParamArray[i].split('=');
controller.addQueryParam(queryParam[0], queryParam[1]);
}
}
});
$('#headers').on('click', 'button', function () {
if ($(this).hasClass('btn_remove')) {
controller.removeHeader($(this).attr("data-id"));
}
});
$('#queryParams').on('click', 'button', function () {
if ($(this).hasClass('btn_remove')) {
controller.removeQueryParam($(this).attr("data-id"));
}
});
$('#send').click(function () {
controller.send();
});
this.render();
},
render: function () {
$('#headers').empty();
$('#queryParams').empty();
$('#path').val(model.path);
for (let i = 0; i < model.headers.length; i++) {
let template = $('#inputTemplate').children().find('tr').clone();
template.find('input[name="key"]').val(model.headers[i].key);
template.find('input[name="value"]').val(model.headers[i].value);
template.find('button').attr("data-id", i);
template.find('input[name="key"]').on('input', function () {
model.headers[i].key = $(this).val();
});
template.find('input[name="value"]').on('input', function () {
model.headers[i].value = $(this).val();
});
$('#headers').append(template);
}
for (let i = 0; i < model.queryParams.length; i++) {
let template = $('#inputTemplate').children().find('tr').clone();
template.find('input[name="key"]').val(model.queryParams[i].key);
template.find('input[name="value"]').val(model.queryParams[i].value);
template.find('button').attr("data-id", i);
template.find('input[name="key"]').on('input', function () {
model.queryParams[i].key = $(this).val();
controller.buildPath();
});
template.find('input[name="value"]').on('input', function () {
model.queryParams[i].value = $(this).val();
controller.buildPath();
});
$('#queryParams').append(template);
}
}
}
let controller = {
init: function () {
view.init();
},
addHeader: function () {
model.headers.push({
key: '',
value: ''
});
console.log(model.headers);
view.render();
},
removeHeader: function (idx) {
model.headers.splice(idx, 1);
view.render();
},
addQueryParam: function (key, value) {
model.queryParams.push({
key: key,
value: value
});
view.render();
},
buildPath : function(){
let queryString = model.path.split('?')[0];
if (model.queryParams.length>0){
queryString += '?';
}
for(let i = 0; i < model.queryParams.length; i++){
queryString += model.queryParams[i].key + '=' + model.queryParams[i].value;
if(i < model.queryParams.length - 1){
queryString += '&';
}
}
model.path = queryString;
view.render();
},
removeQueryParam: function (idx) {
model.queryParams.splice(idx, 1);
this.buildPath();
view.render();
},
send: function () {
$('#responseHeaders').empty();
$('#responseBody').val('');
console.log(model);
// $.ajax({
// url: '/api/test',
// type: 'POST',
// contentType: 'application/json',
// data: JSON.stringify(model),
// success: function (data) {
// console.log(data);
// $('#responseBody').val(JSON.stringify(data, null, 4));
// for (let key in data.headers) {
// let template = $('#responseHeaderTemplate').children().find('tr').clone();
// template.find('input[name="key"]').val(key);
// template.find('input[name="value"]').val(data.headers[key]);
// $('#responseHeaders').append(template);
// }
// }
// });
}
}
controller.init();
CodeMirror.fromTextArea(document.getElementById('requestBody'), {
lineNumbers: true,
mode: "application/json",
extraKeys: {
"Ctrl-Enter": function (cm) {
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
},
"Esc": function (cm) {
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
}
}
});
CodeMirror.fromTextArea(document.getElementById('responseBody'), {
lineNumbers: true,
mode: "application/json",
extraKeys: {
"Ctrl-Enter": function (cm) {
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
},
"Esc": function (cm) {
if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false);
}
}
});
});
</script>
</th:block>
</html>
@@ -1,149 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<th:block layout:fragment="css">
<link rel="stylesheet"
th:href="@{/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-responsive/css/responsive.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-buttons/css/buttons.bootstrap4.min.css}">
</th:block>
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>API 서비스키 관리</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">API 관리</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">전달받은 메세지에서 서비스키를 확보 해야할 경우 추출룰을 관리합니다.</h3>
</div>
<div class="card-body">
<table id="listApiKeyTable"
class="table table-bordered table-striped">
<thead>
<tr>
<th>URL</th>
<th>서비스키타입</th>
<th>서비스키</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
</section>
</div>
<th:block layout:fragment="script">
<script th:src="@{/plugins/datatables/jquery.dataTables.min.js}"></script>
<script
th:src="@{/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/dataTables.responsive.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/responsive.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/dataTables.buttons.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.bootstrap4.min.js}"></script>
<script th:src="@{/plugins/jszip/jszip.min.js}"></script>
<script th:src="@{/plugins/pdfmake/pdfmake.min.js}"></script>
<script th:src="@{/plugins/pdfmake/vfs_fonts.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.html5.min.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.print.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>
<script th:inline="javascript">
$.fn.dataTable.ext.buttons.new = {
text: 'New',
action: function ( e, dt, node, config ) {
location.href = "/manage/getApiKeyForm";
}
};
var listApiKeyTable = $('#listApiKeyTable').DataTable({
"responsive": true, "lengthChange": false, "autoWidth": false,
"processing": true,
"serverSide": true,
"stateSave": true,
"pageLength": 50,
"searchDelay": 1200,
"ajax": {
"url": "/manage/rest/apiKeys",
"type": "POST",
"dataType": "json",
"contentType": "application/json",
"data": function (d) {
return JSON.stringify(d);
}
},
"columns": [
{"data": "url"},
{"data": "serviceType"},
{"data": "serviceKey"},
{"data": "id", "searchable": false, "orderable": false},
{"data": "id", "searchable": false, "orderable": false}
],
"columnDefs": [
{
"render": function (data, type, row) {
return '<a href="/manage/getApiKeyForm/' + row.id + '">' + data + '</a>';
},
"targets": 0
},
{
"render": function (data, type, row) {
return '<a href="/manage/getApiKeyForm/' + data + '" class="btn btn-primary"><i class="fas fa-edit"></i></a>';
},
"targets": -2
},
{
"render": function (data, type, row) {
var msg = "'정말로 데이터를 삭제 하시겠습니까?'";
return '<a href="/manage/deleteApiKey/' + data + '" onclick="return confirm(' + msg +');" class="btn btn-primary"><i class="fas fa-trash"></i></a>';
},
"targets": -1
}
],
"buttons": ["new", "excel"],
"initComplete": function() {
listApiKeyTable.buttons().container().appendTo('#listApiKeyTable_wrapper .col-md-6:eq(0)');
}
});
</script>
</th:block>
</html>
@@ -1,191 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<th:block layout:fragment="css">
<link rel="stylesheet"
th:href="@{/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-responsive/css/responsive.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-buttons/css/buttons.bootstrap4.min.css}">
<style type="text/css" class="init">
.search-ext {
float:right;
}
</style>
</th:block>
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>API 관리</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">API 관리</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">하나의 URL로 여러개의 API를 사용할 경우 [서비스코드]가 필요하고, [API 서비스키 관리]에서 키추출 정보를 추가로 등록 해야함.</h3>
</div>
<div class="card-body">
<table id="listApiTable"
class="table table-bordered table-striped">
<thead>
<tr>
<th>API그룹</th>
<th>API명</th>
<th>URL</th>
<th>서비스코드</th>
<th>Method</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
<div id="api_search" style="display:none">
<label>
&nbsp; &nbsp; API그룹:
<select id="searchApiGroupName" style="margin-left: 0.5em; height: calc(1.8125rem + 2px); border: 1px solid #ced4da; border-radius: 0.2rem;">
<option value="">-- 선택 --</option>
<option th:each="apiGroupName : ${apiGroupNameList}" th:value="${apiGroupName}" th:text="${apiGroupName}"></option>
</select>
</label>
</div>
</section>
</div>
<th:block layout:fragment="script">
<script th:src="@{/plugins/datatables/jquery.dataTables.min.js}"></script>
<script
th:src="@{/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/dataTables.responsive.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/responsive.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/dataTables.buttons.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.bootstrap4.min.js}"></script>
<script th:src="@{/plugins/jszip/jszip.min.js}"></script>
<script th:src="@{/plugins/pdfmake/pdfmake.min.js}"></script>
<script th:src="@{/plugins/pdfmake/vfs_fonts.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.html5.min.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.print.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>
<script th:inline="javascript">
$.fn.dataTable.ext.buttons.new = {
text: 'New',
action: function ( e, dt, node, config ) {
location.href = "/manage/getApiForm";
}
};
var listApiTable = $("#listApiTable").DataTable({
"responsive": true, "lengthChange": false, "autoWidth": false,
"processing": true,
"serverSide": true,
"stateSave": true,
"pageLength": 50,
"searchDelay": 1200,
"dom": '<"row"<"col-sm-12 col-md-6"B><"col-sm-12 col-md-6"<"search-ext">f>>rt<"row"<"col-sm-12 col-md-6"i><"col-sm-12 col-md-6"p>>',
"ajax": {
"url": "/manage/rest/apis",
"type": "POST",
"dataType": "json",
"contentType": "application/json",
"data": function (d) {
return JSON.stringify(d);
}
},
"columns": [
{"data": "apiGroupName"},
{"data": "apiName"},
{"data": "url"},
{"data": "serviceValue"},
{"data": "method", "searchable": false, "orderable": false},
{"data": "id", "searchable": false, "orderable": false},
{"data": "id", "searchable": false, "orderable": false}
],
"columnDefs": [
{
"render": function (data, type, row) {
return '<a href="/manage/getApiForm/' + row.id + '">' + data + '</a>';
},
"targets": 1
},
{
"render": function (data, type, row) {
return '<a href="/manage/getApiForm/' + data + '" class="btn btn-primary"><i class="fas fa-edit"></i></a>';
},
"targets": -2
},
{
"render": function (data, type, row) {
var msg = "'정말로 데이터를 삭제 하시겠습니까?'";
return '<a href="/manage/deleteApi/' + data + '" onclick="return confirm(' + msg +');" class="btn btn-primary"><i class="fas fa-trash"></i></a>';
},
"targets": -1
}
],
"buttons": ["new", "excel", "colvis"],
"initComplete": function() {
//listApiTable.buttons().container().appendTo('#listApiTable_wrapper .col-md-6:eq(0)');
}
});
$('.search-ext').html($('#api_search').html());
$("#searchApiGroupName").change(function(){
listApiTable.columns(0).search(this.value).draw(); // apiGroupName
});
/*
$(".dataTables_filter input").unbind().bind("input", function(e) { // Bind our desired behavior
// If the length is 3 or more characters, or the user pressed ENTER, search
if(this.value.length >= 3 || e.keyCode == 13) {
// Call the API search function
listApiTable.search(this.value).draw();
}
// Ensure we clear the search if they backspace far enough
if(this.value == "") {
listApiTable.search("").draw();
}
return;
});
*/
</script>
</th:block>
</html>
@@ -1,168 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<th:block layout:fragment="css">
<link rel="stylesheet"
th:href="@{/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-responsive/css/responsive.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-buttons/css/buttons.bootstrap4.min.css}">
</th:block>
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>Bytes Message 관리</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">Echo Socket</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">목록중 하나의 메세지만 사용할 수 있습니다.</h3>
</div>
<div class="card-body">
<table id="listBytesMessageSpecTable"
class="table table-bordered table-striped">
<thead>
<tr>
<th>메시지명</th>
<th>LL길이</th>
<th>LL길이포함</th>
<th>Port</th>
<th>사용여부</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
</section>
</div>
<th:block layout:fragment="script">
<script th:src="@{/plugins/datatables/jquery.dataTables.min.js}"></script>
<script
th:src="@{/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/dataTables.responsive.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/responsive.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/dataTables.buttons.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.bootstrap4.min.js}"></script>
<script th:src="@{/plugins/jszip/jszip.min.js}"></script>
<script th:src="@{/plugins/pdfmake/pdfmake.min.js}"></script>
<script th:src="@{/plugins/pdfmake/vfs_fonts.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.html5.min.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.print.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>
<script th:inline="javascript">
$.fn.dataTable.ext.buttons.new = {
text: 'New',
action: function ( e, dt, node, config ) {
location.href = "/manage/getBytesMessageSpecForm";
}
};
var listBytesMessageSpecTable = $("#listBytesMessageSpecTable").DataTable({
"responsive": true, "lengthChange": false, "autoWidth": false,
"processing": true,
"serverSide": true,
"stateSave": true,
"pageLength": 50,
"searchDelay": 1200,
"ajax": {
"url": "/manage/rest/bytesMessageSpecs",
"type": "POST",
"dataType": "json",
"contentType": "application/json",
"data": function (d) {
return JSON.stringify(d);
}
},
"columns": [
{"data": "specName"},
{"data": "llFieldLength", "searchable": false, "orderable": false},
{"data": "llFieldLengthInclude", "searchable": false, "orderable": false},
{"data": "port", "searchable": false, "orderable": false},
{"data": "useYn", "searchable": false},
{"data": "id", "searchable": false, "orderable": false},
{"data": "id", "searchable": false, "orderable": false}
],
"columnDefs": [
{
"render": function (data, type, row) {
return '<a href="/manage/getBytesMessageSpecForm/' + row.id + '">' + data + '</a>';
},
"targets": 0
},
{
"render": function (data, type, row) {
return '<a href="/manage/getBytesMessageSpecForm/' + data + '" class="btn btn-primary"><i class="fas fa-edit"></i></a>';
},
"targets": -2
},
{
"render": function (data, type, row) {
var msg = "'정말로 데이터를 삭제 하시겠습니까?'";
return '<a href="/manage/deleteBytesMessageSpec/' + data + '" onclick="return confirm(' + msg +');" class="btn btn-primary"><i class="fas fa-trash"></i></a>';
},
"targets": -1
}
],
"buttons": ["new", "excel", "colvis"],
"initComplete": function() {
listBytesMessageSpecTable.buttons().container().appendTo('#listBytesMessageSpecTable_wrapper .col-md-6:eq(0)');
}
});
/*
$(".dataTables_filter input").unbind().bind("input", function(e) { // Bind our desired behavior
// If the length is 3 or more characters, or the user pressed ENTER, search
if(this.value.length >= 3 || e.keyCode == 13) {
// Call the API search function
listBytesMessageSpecTable.search(this.value).draw();
}
// Ensure we clear the search if they backspace far enough
if(this.value == "") {
listBytesMessageSpecTable.search("").draw();
}
return;
});
*/
</script>
</th:block>
</html>
@@ -1,166 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<th:block layout:fragment="css">
<link rel="stylesheet"
th:href="@{/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-responsive/css/responsive.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-buttons/css/buttons.bootstrap4.min.css}">
</th:block>
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>HTTP 요청 관리</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">부가 기능</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">HTTP API 호출 테스트를 수행할 수 있다.</h3>
</div>
<div class="card-body">
<table id="listHttpRequestInfoTable"
class="table table-bordered table-striped">
<thead>
<tr>
<th>요청그룹</th>
<th>요청명</th>
<th>URL</th>
<th>Method</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
</section>
</div>
<th:block layout:fragment="script">
<script th:src="@{/plugins/datatables/jquery.dataTables.min.js}"></script>
<script
th:src="@{/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/dataTables.responsive.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/responsive.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/dataTables.buttons.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.bootstrap4.min.js}"></script>
<script th:src="@{/plugins/jszip/jszip.min.js}"></script>
<script th:src="@{/plugins/pdfmake/pdfmake.min.js}"></script>
<script th:src="@{/plugins/pdfmake/vfs_fonts.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.html5.min.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.print.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>
<script th:inline="javascript">
$.fn.dataTable.ext.buttons.new = {
text: 'New',
action: function ( e, dt, node, config ) {
location.href = "/manage/getHttpRequestInfoForm";
}
};
var listHttpRequestInfoTable = $("#listHttpRequestInfoTable").DataTable({
"responsive": true, "lengthChange": false, "autoWidth": false,
"processing": true,
"serverSide": true,
"stateSave": true,
"pageLength": 50,
"searchDelay": 1200,
"ajax": {
"url": "/manage/rest/httpRequestInfos",
"type": "POST",
"dataType": "json",
"contentType": "application/json",
"data": function (d) {
return JSON.stringify(d);
}
},
"columns": [
{"data": "requestGroupName"},
{"data": "requestName"},
{"data": "url"},
{"data": "method", "searchable": false, "orderable": false},
{"data": "id", "searchable": false, "orderable": false},
{"data": "id", "searchable": false, "orderable": false}
],
"columnDefs": [
{
"render": function (data, type, row) {
return '<a href="/manage/getHttpRequestInfoForm/' + row.id + '">' + data + '</a>';
},
"targets": 1
},
{
"render": function (data, type, row) {
return '<a href="/manage/getHttpRequestInfoForm/' + data + '" class="btn btn-primary"><i class="fas fa-edit"></i></a>';
},
"targets": -2
},
{
"render": function (data, type, row) {
var msg = "'정말로 데이터를 삭제 하시겠습니까?'";
return '<a href="/manage/deleteHttpRequestInfo/' + data + '" onclick="return confirm(' + msg +');" class="btn btn-primary"><i class="fas fa-trash"></i></a>';
},
"targets": -1
}
],
"buttons": ["new", "excel", "colvis"],
"initComplete": function() {
listHttpRequestInfoTable.buttons().container().appendTo('#listHttpRequestInfoTable_wrapper .col-md-6:eq(0)');
}
});
/*
$(".dataTables_filter input").unbind().bind("input", function(e) { // Bind our desired behavior
// If the length is 3 or more characters, or the user pressed ENTER, search
if(this.value.length >= 3 || e.keyCode == 13) {
// Call the API search function
listHttpRequestInfoTable.search(this.value).draw();
}
// Ensure we clear the search if they backspace far enough
if(this.value == "") {
listHttpRequestInfoTable.search("").draw();
}
return;
});
*/
</script>
</th:block>
</html>
@@ -1,162 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout/default_layout">
<th:block layout:fragment="css">
<link rel="stylesheet"
th:href="@{/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-responsive/css/responsive.bootstrap4.min.css}">
<link rel="stylesheet"
th:href="@{/plugins/datatables-buttons/css/buttons.bootstrap4.min.css}">
</th:block>
<div class="content-wrapper" layout:fragment="content">
<!-- Content Header (Page header) -->
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>스크립트 관리</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">부가 기능</li>
</ol>
</div>
</div>
</div>
<!-- /.container-fluid -->
</section>
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">응답 송신 전 수행 할 스크립트, GUID 복제나 특정 예외전문등의 처리 수행 로직을 작성한다(java)</h3>
</div>
<div class="card-body">
<table id="listApiTable"
class="table table-bordered table-striped">
<thead>
<tr>
<th>스크립트 명</th>
<th>스크립트 설명</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
</table>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.container-fluid -->
</section>
</div>
<th:block layout:fragment="script">
<script th:src="@{/plugins/datatables/jquery.dataTables.min.js}"></script>
<script
th:src="@{/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/dataTables.responsive.min.js}"></script>
<script
th:src="@{/plugins/datatables-responsive/js/responsive.bootstrap4.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/dataTables.buttons.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.bootstrap4.min.js}"></script>
<script th:src="@{/plugins/jszip/jszip.min.js}"></script>
<script th:src="@{/plugins/pdfmake/pdfmake.min.js}"></script>
<script th:src="@{/plugins/pdfmake/vfs_fonts.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.html5.min.js}"></script>
<script th:src="@{/plugins/datatables-buttons/js/buttons.print.min.js}"></script>
<script
th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>
<script th:inline="javascript">
$.fn.dataTable.ext.buttons.new = {
text: 'New',
action: function ( e, dt, node, config ) {
location.href = "/manage/getScriptInfoForm";
}
};
var listApiTable = $("#listApiTable").DataTable({
"responsive": true, "lengthChange": false, "autoWidth": false,
"processing": true,
"serverSide": true,
"stateSave": true,
"pageLength": 50,
"searchDelay": 1200,
"ajax": {
"url": "/manage/rest/scriptInfos",
"type": "POST",
"dataType": "json",
"contentType": "application/json",
"data": function (d) {
return JSON.stringify(d);
}
},
"columns": [
{"data": "scriptName"},
{"data": "scriptDesc"},
{"data": "id", "searchable": false, "orderable": false},
{"data": "id", "searchable": false, "orderable": false}
],
"columnDefs": [
{
"render": function (data, type, row) {
return '<a href="/manage/getScriptInfoForm/' + row.id + '">' + data +'</a>';
},
"targets": 0
},
{
"render": function (data, type, row) {
return '<a href="/manage/getScriptInfoForm/' + data + '" class="btn btn-primary"><i class="fas fa-edit"></i></a>';
},
"targets": -2
},
{
"render": function (data, type, row) {
var msg = "'정말로 데이터를 삭제 하시겠습니까?'";
return '<a href="/manage/deleteScriptInfo/' + data + '" onclick="return confirm(' + msg +');" class="btn btn-primary"><i class="fas fa-trash"></i></a>';
},
"targets": -1
}
],
"buttons": ["new", "excel", "colvis"],
"initComplete": function() {
listApiTable.buttons().container().appendTo('#listApiTable_wrapper .col-md-6:eq(0)');
}
});
/*
$(".dataTables_filter input").unbind().bind("input", function(e) { // Bind our desired behavior
// If the length is 3 or more characters, or the user pressed ENTER, search
if(this.value.length >= 3 || e.keyCode == 13) {
// Call the API search function
listApiTable.search(this.value).draw();
}
// Ensure we clear the search if they backspace far enough
if(this.value == "") {
listApiTable.search("").draw();
}
return;
});
*/
</script>
</th:block>
</html>
@@ -0,0 +1,144 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/layout/base_layout}">
<body>
<section layout:fragment="contentFragment">
<style>
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
</style>
<!-- javascript warning tag -->
<div class="container">
<div class="mt-2">
<h1 class="text-center">로그인</h1>
</div>
<main class="form-signin mt-2">
<form role="form" name="loginForm" id="loginForm" th:action="@{/actionLogin.do}" method="post">
<input type="hidden" id="message" name="message" th:value="${loginMessage}">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div class="form-floating">
<input type="email" class="form-control" name="id" id="id" maxlength="20" required>
<label for="id" class="form-label">이메일</label>
</div>
<div class="form-floating">
<input type="password" class="form-control mb-3" name="password" id="password" minlength="8" maxlength="20" required>
<label for="password" class="form-label">비밀번호</label>
</div>
<div class="checkbox mb-3">
<input type="checkbox" class="form-check-input" name="checkId" id="checkId">
<label class="form-check-label" for="checkId">아이디 저장</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="button" id="actionLogin" th:text="#{loginForm.login}">Sign in</button>
</form>
<div class="mt-2 text-center">
<span class="text-muted"><a href="/forgot_password.do">비밀번호 초기화</a></span>
</div>
</main>
</div>
</section>
</body>
<th:block layout:fragment="contentScript">
<script>
function setCookie(name, value, expires) {
let cookie = `${name}=${encodeURIComponent(value)}; path=/`;
if (expires instanceof Date) {
cookie += `; expires=${expires.toUTCString()}`;
}
document.cookie = cookie;
}
function getCookie(name) {
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length + 1, c.length);
}
}
return null;
}
function getId(form) {
form.checkId.checked = ((form.id.value = getCookie("saveid")) !== null);
}
function fnInit() {
getId(document.loginForm);
if (document.loginForm.message !== null && document.loginForm.message.value !== "") {
alert(document.loginForm.message.value);
}
}
$(function () {
fnInit();
let actionLogin = function (event) {
let form = document.loginForm;
if ($('#checkId').is(':checked')) {
setCookie("saveid", $('#id').val(), new Date(new Date().getTime() + 1000 * 3600 * 24 * 30));
} else {
setCookie("saveid", $('#id').val(), new Date(new Date().getTime() - 1));
}
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
} else {
if (form.password.value.length < 8) {
alert("[[#{validate.passLengthShort}]]");
} else {
form.submit();
}
}
form.classList.add('was-validated');
};
$('#actionLogin').on('click', actionLogin);
$('#password').on("keydown", function (event) {
if (event.key === "Enter" || event.keyCode === 13 || event.code === 'Enter') {
event.preventDefault();
actionLogin(event);
}
});
});
</script>
</th:block>
</html>
@@ -0,0 +1,538 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/layout/default_layout}">
<body>
<section layout:fragment="contentFragment">
<div class="container mt-1">
<form name="mockRouteForm" id="mockRouteForm"
th:action="${mockRoute.id == null}?@{/mgmt/routes/register.do}:@{/mgmt/routes/update.do}"
th:object='${mockRoute}' method="post">
<div class="card col-md">
<div class="card-header">
<h3 th:if="${mockRoute.id == null}">API 정보 등록</h3>
<h3 th:if="${mockRoute.id != null}">API 정보 수정</h3>
</div>
<div class="card-body">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="hidden" name="id" th:value="${mockRoute.id}">
<div class="form-floating mt-2">
<input type="text" class="form-control"
th:classappend="${#fields.hasErrors('name')}? 'is-invalid'" th:field="*{name}"
name="username" id="name" required/>
<label for="name" class="form-label must" th:text="#{name}">이름</label>
<th:block th:each="err : ${#fields.errors('name')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div class="d-flex align-items-center mt-2">
<div class="form-floating col-md-1 me-2">
<select name="method" class="form-select"
th:classappend="${#fields.hasErrors('method')}? 'is-invalid'" id="method">
<option th:selected="${mockRoute.method == 'POST'}" value="POST">POST</option>
<option th:selected="${mockRoute.method == 'GET'}" value="GET">GET</option>
<option th:selected="${mockRoute.method == 'PUT'}" value="PUT">PUT</option>
<option th:selected="${mockRoute.method == 'DELETE'}" value="DELETE">DELETE</option>
<option th:selected="${mockRoute.method == 'CONNECT'}" value="CONNECT">CONNECT</option>
<option th:selected="${mockRoute.method == 'HEAD'}" value="HEAD">HEAD</option>
<option th:selected="${mockRoute.method == 'OPTIONS'}" value="OPTIONS">OPTIONS</option>
<option th:selected="${mockRoute.method == 'TRACE'}" value="TRACE">TRACE</option>
<option th:selected="${mockRoute.method == 'PATCH'}" value="PATCH">PATCH</option>
</select>
<label for="method" class="form-label must">Method</label>
</div>
<div class="form-floating col-md">
<input type="text" name="pathPattern" class="form-control" id="pathPattern"
th:field="*{pathPattern}"
placeholder="서버에 노출 되는 경로 (/부터 입력)" required>
<label for="pathPattern" class="must">Path</label>
</div>
</div>
</div>
</div>
<div class="card mt-2">
<div class="card-header">
<div class="d-flex justify-content-between">
<h3>응답</h3>
<button type="button" class="btn btn-sm btn-primary btn_add_case"><i
class="fa-sharp fa-solid fa-plus"></i>케이스 추가
</button>
</div>
</div>
<div class="card-body" id="responses">
<div class="card" th:each="response, status: *{responses}">
<div class="card-header">
<div class="d-flex justify-content-between">
<span th:text="'조건 ' + ${status.index + 1}"></span>
<button type="button" class="btn btn-secondary btn-sm btn_remove_case"
th:data-id="${status.index+1}">제거
</button>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<span>조건 항목</span>
</div>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">종류</th>
<th scope="col"></th>
<th scope="col"></th>
<th>
<button type="button" class="btn btn-secondary btn-sm">추가
</button>
</th>
</tr>
</thead>
<tbody class="response_conditions">
<tr th:each="condition, status: *{responses[__${status.index}__].conditions}">
<td>
<select>
<option value="query"
th:selected="${condition.type == 'query'}">Query
</option>
<option value="header"
th:selected="${condition.type == 'header'}">Header
</option>
<option value="path"
th:selected="${condition.type == 'path'}">Path
</option>
<option value="jsonpath"
th:selected="${condition.type == 'jsonpath'}">JSON
Path
</option>
</select>
</td>
<td>
<input class="form-control" type="text" name="key"
th:value="${condition.key}"/>
</td>
<td><input class="form-control" type="text" name="value"
th:value="${condition.value}"/></td>
<td>
<button type="button" class="btn btn-secondary btn-sm">제거
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md">
<div class="card">
<div class="card-header">응답 내용</div>
<div class="card-body">
<div class="form-floating mt-2">
<input class="form-control" type="number"
th:field="*{responses[__${status.index}__].statusCode}"
required/>
<label class="form-label must">Status</label>
</div>
<div class="form-floating mt-2">
<input class="form-control" type="number"
th:field="*{responses[__${status.index}__].delay}" required/>
<label class="form-label must">응답 지연 시간(ms)</label>
</div>
<div class="form-floating mt-2">
<textarea class="form-control" style="height: 200px;"
th:field="*{responses[__${status.index}__].body}">
</textarea>
<label class="form-label must">응답 Body</label>
</div>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">Header 이름</th>
<th></th>
<th>
<button type="button" class="btn btn-secondary btn-sm">추가
</button>
</th>
</tr>
</thead>
<tbody class="response_headers">
<tr th:each="header, status: *{responses[__${status.index}__].headers}">
<td>
<input class="form-control" type="text" name="key"
th:value="${header.key}"/>
</td>
<td><input class="form-control" type="text" name="value"
th:value="${header.value}"/></td>
<td>
<button type="button" class="btn btn-secondary btn-sm">제거
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer">
<button type="button" class="btn btn-primary btn_list"><i class="fa-solid fa-list"></i> 목록</button>
<button type="button" class="btn btn-primary btn_register"><i
class="fa-sharp fa-solid fa-floppy-disk"></i> 저장
</button>
</div>
</div>
</form>
<div id="template" style="visibility: hidden; height: 0;">
<div class="card mt-1">
<div class="card-header">
<div class="d-flex justify-content-between">
<span class="condition_name"></span>
<button type="button" class="btn btn-secondary btn-sm btn_remove_case">제거</button>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md">
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between">
<span>조건 항목</span>
</div>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">종류</th>
<th scope="col"></th>
<th scope="col"></th>
<th>
<button type="button" class="btn btn-secondary btn-sm btn_add_condition">추가</button>
</th>
</tr>
</thead>
<tbody class="response_conditions">
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md">
<div class="card">
<div class="card-header">응답 내용</div>
<div class="card-body">
<div class="form-floating mt-1">
<input class="form-control" type="number" name="statusCode" required/>
<label class="form-label must">Status</label>
</div>
<div class="form-floating mt-1">
<input class="form-control" type="number" name="delay" required/>
<label class="form-label must">응답 지연 시간(ms)</label>
</div>
<div class="form-floating mt-1">
<textarea class="form-control" style="height: 200px;" name="body">
</textarea>
<label class="form-label must">응답 Body</label>
</div>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">Header 이름</th>
<th></th>
<th>
<button type="button" class="btn btn-secondary btn-sm btn_add_header">추가</button>
</th>
</tr>
</thead>
<tbody class="response_headers">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="conditionTemplate" style="visibility: hidden; height: 0;">
<table>
<tbody>
<tr>
<td>
<select class="form-select">
<option value="query">Query</option>
<option value="header">Header</option>
<option value="path">Path</option>
<option value="jsonpath">JSON Path</option>
</select>
</td>
<td>
<input class="form-control" type="text" name="key"/>
</td>
<td>
<input class="form-control" type="text" name="value"/>
</td>
<td>
<button type="button" class="btn btn-secondary btn-sm btn_remove_condition">제거</button>
</td>
</tr>
</tbody>
</table>
</div>
<div id="responseHeaderTemplate" style="visibility: hidden; height: 0;">
<table>
<tbody>
<tr>
<td>
<input class="form-control" type="text" name="key"/>
</td>
<td>
<input class="form-control" type="text" name="value"/>
</td>
<td>
<button type="button" class="btn btn-secondary btn-sm btn_remove_header">제거</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</body>
<th:block layout:fragment="contentScript">
<script>
$(function () {
let model = {
name: '',
method: '',
pathPattern: '',
responses: [
]
};
let view = {
init: function () {
$('.btn_add_case').on('click', function () {
controller.addResponse();
});
$('#responses').on('click', 'button', function () {
if ($(this).hasClass('btn_remove_case')) {
controller.removeResponse($(this).attr("data-id"));
}
if ($(this).hasClass('btn_add_condition')) {
controller.addResponseCondition($(this).attr("data-id"));
}
if ($(this).hasClass('btn_add_header')) {
controller.addResponseHeader($(this).attr("data-id"));
}
if ($(this).hasClass('btn_remove_condition')) {
controller.removeResponseCondition($(this).attr("data-id"), $(this).attr("data-row"));
}
if ($(this).hasClass('btn_remove_header')) {
controller.removeResponseHeader($(this).attr("data-id"), $(this).attr("data-row"));
}
});
$('#responses').children().each(function(){
let response = {
conditions: [],
headers: []
};
response.statusCode = $(this).find('input[name$="statusCode"]').val();
response.delay = $(this).find('input[name$="delay"]').val();
response.body = $(this).find('textarea[name$="body"]').val();
$(this).find('.response_conditions').children().each(function(){
let condition = {
type: $(this).find('select').val(),
key: $(this).find('input[name$="key"]').val(),
value: $(this).find('input[name$="value"]').val()
};
response.conditions.push(condition);
});
model.responses.push(response);
$(this).find('.response_headers').children().each(function(){
let header = {
key: $(this).find('input[name$="key"]').val(),
value: $(this).find('input[name$="value"]').val()
};
response.headers.push(header);
});
});
this.render();
},
render: function () {
$('#responses').empty();
for (let i = 0; i < model.responses.length; i++) {
let nodeCopy = $('#template').children().clone();
nodeCopy.find('.condition_name').text('조건 ' + (i + 1));
nodeCopy.find('.btn_remove_case').attr("data-id", i);
nodeCopy.find('.btn_add_condition').attr("data-id", i);
nodeCopy.find('.btn_add_header').attr("data-id", i);
nodeCopy.find('input[name="statusCode"]').attr('name', 'responses[' + i + '].statusCode');
nodeCopy.find('input[name="delay"]').attr('name', 'responses[' + i + '].delay');
nodeCopy.find('textarea[name="body"]').attr('name', 'responses[' + i + '].body');
nodeCopy.find('input[name="responses[' + i + '].statusCode"]').val(model.responses[i].statusCode);
nodeCopy.find('input[name="responses[' + i + '].delay"]').val(model.responses[i].delay);
nodeCopy.find('textarea[name="responses[' + i + '].body"]').val(model.responses[i].body);
nodeCopy.find('input[name="responses[' + i + '].statusCode"]').on('input', function() {
model.responses[i].statusCode = $(this).val();
});
nodeCopy.find('input[name="responses[' + i + '].delay"]').on('input', function() {
model.responses[i].delay = $(this).val();
});
nodeCopy.find('textarea[name="responses[' + i + '].body"]').on('input', function() {
model.responses[i].body = $(this).val();
});
let conditions = nodeCopy.find('.response_conditions');
for(let c = 0; c < model.responses[i].conditions.length; c++){
let conditionTemplate = $('#conditionTemplate').children().find('tr').clone();
conditionTemplate.find('select').attr('name', 'responses[' + i + '].conditions[' + c + '].type');
conditionTemplate.find('input[name="key"]').attr('name', 'responses[' + i + '].conditions[' + c + '].key');
conditionTemplate.find('input[name="value"]').attr('name', 'responses[' + i + '].conditions[' + c + '].value');
conditionTemplate.find('button').attr("data-id", i);
conditionTemplate.find('button').attr("data-row", c);
conditionTemplate.find('select').val(model.responses[i].conditions[c].type);
conditionTemplate.find('input[name="responses[' + i + '].conditions[' + c + '].key"]').val(model.responses[i].conditions[c].key);
conditionTemplate.find('input[name="responses[' + i + '].conditions[' + c + '].value"]').val(model.responses[i].conditions[c].value);
conditionTemplate.find('select').on('change', function() {
model.responses[i].conditions[c].type = $(this).val();
});
conditionTemplate.find('input[name="responses[' + i + '].conditions[' + c + '].key"]').on('input', function() {
model.responses[i].conditions[c].key = $(this).val();
});
conditionTemplate.find('input[name="responses[' + i + '].conditions[' + c + '].value"]').on('input', function() {
model.responses[i].conditions[c].value = $(this).val();
});
$(conditions).append(conditionTemplate);
}
for(let j = 0; j < model.responses[i].headers.length; j++){
let headerTemplate = $('#responseHeaderTemplate').children().find('tr').clone();
headerTemplate.find('input[name="key"]').attr('name', 'responses[' + i + '].headers[' + j + '].key');
headerTemplate.find('input[name="value"]').attr('name', 'responses[' + i + '].headers[' + j + '].value');
headerTemplate.find('button').attr("data-id", i);
headerTemplate.find('button').attr("data-row",j);
headerTemplate.find('input[name="responses[' + i + '].headers[' + j + '].key"]').val(model.responses[i].headers[j].key);
headerTemplate.find('input[name="responses[' + i + '].headers[' + j + '].value"]').val(model.responses[i].headers[j].value);
headerTemplate.find('input[name="responses[' + i + '].headers[' + j + '].key"]').on('input', function() {
model.responses[i].headers[j].key = $(this).val();
});
headerTemplate.find('input[name="responses[' + i + '].headers[' + j + '].value"]').on('input', function() {
model.responses[i].headers[j].value = $(this).val();
});
$(nodeCopy).find('.response_headers').append(headerTemplate);
}
$('#responses').append(nodeCopy);
}
}
};
let controller = {
init: function () {
view.init();
},
addResponse: function () {
model.responses.push({
statusCode: 200,
delay: 0,
headers: [],
body: '',
conditions: [],
});
view.render();
},
removeResponse: function (idx) {
if (model.responses.length === 1) {
alert('최소 1개의 응답이 필요합니다.');
return;
}
model.responses.splice(idx, 1);
view.render();
},
addResponseCondition: function (idx) {
model.responses[idx].conditions.push({
type: 'query',
key: '',
value: ''
});
view.render();
},
removeResponseCondition: function (idx, conditionIdx) {
model.responses[idx].conditions.splice(conditionIdx, 1);
view.render();
},
addResponseHeader : function(idx) {
model.responses[idx].headers.push({
key: '',
value: ''
});
view.render();
},
removeResponseHeader : function(idx, headerIdx) {
model.responses[idx].headers.splice(headerIdx, 1);
view.render();
}
}
controller.init();
let fnRegister = document.querySelector(".btn_register");
fnRegister.addEventListener("click", function (event) {
let form = document.getElementById("mockRouteForm");
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
} else {
form.submit();
}
form.classList.add('was-validated');
});
let fnList = document.querySelector(".btn_list");
fnList.addEventListener("click", function () {
location.href = "[[@{/mgmt/routes/list_view.do}]]";
});
});
</script>
</th:block>
</html>
@@ -0,0 +1,161 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/layout/default_layout}">
<body>
<section layout:fragment="contentFragment">
<div class="container">
<div class="card mt-1">
<div class="card-header">
<h1>API 목록</h1>
</div>
<div class="card-body">
<div class="d-flex justify-content-between">
<form role="form" name="searchForm" th:action="@{/mgmt/routes/list_view.do}" method="get" th:object="${modelSearch}">
<input name="page" type="hidden" value="1"/>
<div class="search_field">
<div class="search_field_input">
<input class="form-control" name="username" placeholder="이름" type="text" size="35" th:title="#{title.search}+ '' + #{input.input}"
th:value="${modelSearch.name}"
maxlength="255"/>
</div>
<div class="search_field_button">
<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 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_add"><i class="fa-sharp fa-solid fa-plus"></i> [[#{button.create}]]</button><!-- 등록 -->
</div>
</div>
</form>
</div>
</div>
</div>
<div class="card mt-2">
<div class="card-body">
<form name="listForm">
<input name="id" type="hidden"/>
<input name="checkedIdForDel" type="hidden"/>
<input id="csrf" type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<table class="table table-hover pt-table">
<colgroup>
<col style="width: 5%;">
<col style="width: 5%;">
<col>
<col>
<col style="width: 7%;">
<col>
</colgroup>
<thead>
<tr>
<th class="no">번호</th>
<th><input type="checkbox" name="checkAll" class="check2" onclick="fncCheckAll()" th:title="#{input.selectAll.title}"></th><!-- 전체선택 -->
<th>method</th>
<th>path</th>
<th>이름</th>
<th>비고</th>
</tr>
</thead>
<tbody>
<tr th:each="row, status : ${page.content}">
<td class="no" th:text="${page.getTotalElements() - (page.getNumber() * page.getSize() + status.count ) + 1}"></td>
<td>
<input type="checkbox" name="checkField" class="check2" title="선택"/>
<input name="checkId" type="hidden" th:value="${row.id}"/>
</td>
<td>
<span th:text="${row.method}"></span>
</td>
<td>
<span th:text="${row.pathPattern}"></span>
</td>
<td>
<span th:text="${row.name}"></span>
</td>
<td>
<button type="button" class="btn btn-primary btn-sm" th:onclick="fnSelectItem([[${row.id}]])" th:value="#{button.update}"><i class="fa-sharp fa-solid fa-edit"></i>
[[#{button.update}]]
</button>
</td>
</tr>
<!-- 글이 없는 경우 -->
<tr th:if="${page.content.size() == 0 }">
<td colspan="6" th:text="#{common.nodata.msg}"></td>
</tr>
</tbody>
</table>
</form>
</div>
<div class="card-footer">
<div th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"></div>
</div>
</div>
</div>
</section>
</body>
<th:block layout:fragment="contentScript">
<script>
function fn_select_page(pageNo) {
document.searchForm.page.value = pageNo;
document.searchForm.submit();
}
function fnSelectItem(id) {
location.href = "[[@{/mgmt/routes/update_view.do}]]" + "?id=" + id;
}
$(function () {
let fnSearch = document.querySelector(".btn_search");
let fnDelete = document.querySelector(".btn_delete");
let fnAdd = document.querySelector(".btn_add");
fnSearch.addEventListener("click", function () {
document.searchForm.page.value = 1;
document.searchForm.submit();
});
fnDelete.addEventListener("click", function () {
var checkField = document.listForm.checkField;
var id = document.listForm.checkId;
var checkedIds = "";
var checkedCount = 0;
if (checkField) {
if (checkField.length > 1) {
for (var i = 0; i < checkField.length; i++) {
if (checkField[i].checked) {
checkedIds += ((checkedCount == 0 ? "" : ",") + id[i].value);
checkedCount++;
}
}
} else {
if (checkField.checked) {
checkedIds = id.value;
}
}
}
if (checkedIds.length > 0) {
if (confirm("[[#{common.delete.msg}]]")) {
document.listForm.checkedIdForDel.value = checkedIds;
document.listForm.method = 'POST';
document.listForm.action = '[[@{/mgmt/routes/delete.do}]]';
document.listForm.submit();
}
}
});
fnAdd.addEventListener("click", function () {
location.href = '[[@{/mgmt/routes/create_view.do}]]';
});
})
</script>
</th:block>
</html>
@@ -0,0 +1,80 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/layout/base_layout}">
<body>
<section layout:fragment="contentFragment">
<div class="container mt-2">
<div class="card">
<div class="card-header">
<h1>최초 Admin 등록</h1>
</div>
<div class="card-body">
<form name="staffUserForm" id="staffUserForm" th:action="@{/admin_register.do}" th:object='${user}' method="post">
<input id="csrf" type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<div class="form-floating mt-2">
<input type="email" class="form-control" th:classappend="${#fields.hasErrors('userId')}? 'is-invalid'" th:field="*{userId}" name="userId" id="userId" required th:readonly="${user.esntlId != null}"/>
<label for="userId" class="form-label must" th:text="#{email}">이메일</label>
<th:block th:each="err : ${#fields.errors('userId')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div class="form-floating mt-2" th:if="${user.esntlId == null}">
<input type="password" class="form-control" th:classappend="${#fields.hasErrors('password')}? 'is-invalid'" th:field="*{password}" name="password" id="password" required/>
<label for="password" class="form-label must" th:text="#{password}">비밀번호</label>
<th:block th:each="err : ${#fields.errors('password')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div class="form-floating mt-2" th:if="${user.esntlId == null}">
<input type="password" class="form-control" th:classappend="${#fields.hasErrors('password2')}? 'is-invalid'" th:field="*{password2}" name="password2" id="password2" required/>
<label for="password2" class="form-label must">비밀번호 확인</label>
<th:block th:each="err : ${#fields.errors('password2')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div class="form-floating mt-2">
<input type="text" class="form-control" th:classappend="${#fields.hasErrors('username')}? 'is-invalid'" th:field="*{username}" name="username" id="username" required/>
<label for="username" class="form-label must" th:text="#{username}">이름</label>
<th:block th:each="err : ${#fields.errors('username')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div class="form-floating mt-2">
<input type="text" class="form-control" th:classappend="${#fields.hasErrors('mobilePhone')}? 'is-invalid'" th:field="*{mobilePhone}" name="mobilePhone" id="mobilePhone" required/>
<label for="mobilePhone" class="form-label must" th:text="#{mobilePhone}">연락처</label>
<th:block th:each="err : ${#fields.errors('mobilePhone')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
</form>
</div>
<div class="card-footer">
<button type="button" class="btn btn-primary btn_register"><i class="fa-sharp fa-solid fa-floppy-disk"></i> 저장</button>
</div>
</div>
</div>
</section>
</body>
<th:block layout:fragment="contentScript">
<script>
$(function () {
let fnRegister = document.querySelector(".btn_register");
fnRegister.addEventListener("click", function (event) {
let form = document.getElementById("staffUserForm");
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
} else {
form.submit();
}
form.classList.add('was-validated');
});
});
</script>
</th:block>
</html>
@@ -0,0 +1,128 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/layout/default_layout}">
<body>
<section layout:fragment="contentFragment">
<div class="container m-0">
<div class="card">
<div class="card-header">
<h1 th:if="${user.esntlId == null}">내부 사용자 등록</h1>
<h1 th:if="${user.esntlId != null}">내부 사용자 수정</h1>
</div>
<div class="card-body">
<form name="staffUserForm" id="staffUserForm" th:action="${user.esntlId==null}?@{/mgmt/users/register.do}:@{/mgmt/users/update.do}" th:object='${user}' method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<div th:if="${user.esntlId != null}" class="form-floating">
<input type="text" class="form-control" th:classappend="${#fields.hasErrors('esntlId')}? 'is-invalid'" th:field="*{esntlId}" name="esntlId" id="esntlId" readonly/>
<label for="esntlId" class="form-label must" th:text="#{esntlId}">고유ID</label>
</div>
<div class="form-floating mt-2">
<input type="email" class="form-control" th:classappend="${#fields.hasErrors('userId')}? 'is-invalid'" th:field="*{userId}" name="userId" id="userId" required th:readonly="${user.esntlId != null}"/>
<label for="userId" class="form-label must" th:text="#{email}">이메일</label>
<th:block th:each="err : ${#fields.errors('userId')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div class="form-floating mt-2" th:if="${user.esntlId == null}">
<input type="password" class="form-control" th:classappend="${#fields.hasErrors('password')}? 'is-invalid'" th:field="*{password}" name="password" id="password" required/>
<label for="password" class="form-label must" th:text="#{password}">비밀번호</label>
<th:block th:each="err : ${#fields.errors('password')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div class="form-floating mt-2" th:if="${user.esntlId == null}">
<input type="password" class="form-control" th:classappend="${#fields.hasErrors('password2')}? 'is-invalid'" th:field="*{password2}" name="password2" id="password2" required/>
<label for="password2" class="form-label must">비밀번호 확인</label>
<th:block th:each="err : ${#fields.errors('password2')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div class="form-floating mt-2">
<input type="text" class="form-control" th:classappend="${#fields.hasErrors('username')}? 'is-invalid'" th:field="*{username}" name="username" id="username" required/>
<label for="username" class="form-label must" th:text="#{username}">이름</label>
<th:block th:each="err : ${#fields.errors('username')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div class="form-floating mt-2">
<input type="text" class="form-control" th:classappend="${#fields.hasErrors('mobilePhone')}? 'is-invalid'" th:field="*{mobilePhone}" name="mobilePhone" id="mobilePhone" required/>
<label for="mobilePhone" class="form-label must" th:text="#{mobilePhone}">연락처</label>
<th:block th:each="err : ${#fields.errors('mobilePhone')}">
<div th:text="${err}" class="invalid-feedback"></div>
</th:block>
</div>
<div th:if="${user.esntlId != null}" class="form-floating mt-2">
<select name="status" class="form-select" th:classappend="${#fields.hasErrors('status')}? 'is-invalid'" id="status">
<option th:selected="${user.status.toString() == 'WAITING'}" value="WAITING">가입승인 대기</option>
<option th:selected="${user.status.toString() == 'ACTIVE'}" value="ACTIVE">활성화</option>
<option th:selected="${user.status.toString() == 'INACTIVE'}" value="INACTIVE">비활성화</option>
<option th:selected="${user.status.toString() == 'LOCKED'}" value="LOCKED">잠김</option>
</select>
<label for="status" class="form-label must" th:text="#{status}">상태</label>
</div>
<div th:if="${user.esntlId != null}" class="form-floating mt-2">
<select name="lockAt" class="form-select" th:classappend="${#fields.hasErrors('lockAt')}? 'is-invalid'" id="lockAt">
<option th:selected="${user.lockAt.toString() == 'N'}" value="N">해제</option>
<option th:selected="${user.lockAt.toString() == 'Y'}" value="Y">잠금</option>
</select>
<label for="lockAt" class="form-label must" th:text="#{lockAt}">잠금여부</label>
</div>
<div th:if="${user.esntlId != null}" class="form-floating mt-2">
<input type="text" class="form-control" th:classappend="${#fields.hasErrors('lockCnt')}? 'is-invalid'" th:field="*{lockCnt}" name="lockCnt" id="lockCnt" readonly/>
<label for="lockCnt" class="form-label must" th:text="#{lockCnt}">잠금 횟수</label>
</div>
<div th:if="${user.esntlId != null}" class="form-floating mt-2">
<input type="text" class="form-control" th:classappend="${#fields.hasErrors('createdAt')}? 'is-invalid'" th:field="*{createdAt}" name="createdAt" id="createdAt" readonly/>
<label for="createdAt" class="form-label must">등록 일시</label>
</div>
<div th:if="${user.esntlId != null}" class="form-floating mt-2">
<input type="text" class="form-control" th:classappend="${#fields.hasErrors('lastLockDate')}? 'is-invalid'" th:field="*{lastLockDate}" name="lastLockDate"
id="lastLockDate" readonly/>
<label for="lastLockDate" class="form-label must" th:text="#{lastLockDate}">잠금최종시점</label>
</div>
<div th:if="${user.esntlId != null}" class="form-floating mt-2">
<input type="text" class="form-control" th:classappend="${#fields.hasErrors('lastPasswordChangeDate')}? 'is-invalid'" th:field="*{lastPasswordChangeDate}"
name="lastPasswordChangeDate"
id="lastPasswordChangeDate" readonly/>
<label for="lastPasswordChangeDate" class="form-label must" th:text="#{lastPasswordChangeDate}">비밀번호 변경일자</label>
</div>
</form>
</div>
<div class="card-footer">
<button type="button" class="btn btn-primary btn_list"><i class="fa-solid fa-list"></i> 목록</button>
<button type="button" class="btn btn-primary btn_register"><i class="fa-sharp fa-solid fa-floppy-disk"></i> 저장</button>
</div>
</div>
</div>
</section>
</body>
<th:block layout:fragment="contentScript">
<script>
$(function () {
let fnRegister = document.querySelector(".btn_register");
fnRegister.addEventListener("click", function (event) {
let form = document.getElementById("staffUserForm");
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
} else {
form.submit();
}
form.classList.add('was-validated');
});
let fnList = document.querySelector(".btn_list");
fnList.addEventListener("click", function () {
location.href = "[[@{/mgmt/users/list_view.do}]]";
});
});
</script>
</th:block>
</html>
@@ -0,0 +1,158 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{/layout/default_layout}">
<body>
<section layout:fragment="contentFragment">
<div class="container">
<div class="card mt-2">
<div class="card-header">
<h1>사용자 목록</h1>
</div>
<div class="card-body">
<div class="d-flex justify-content-between">
<form role="form" name="searchForm" th:action="@{/mgmt/users/list_view.do}" method="get" th:object="${modelSearch}">
<input name="page" type="hidden" value="1"/>
<div class="search_field">
<div class="search_field_input">
<input class="form-control" name="username" placeholder="사용자 이름" type="text" size="35" th:title="#{title.search}+ '' + #{input.input}"
th:value="${modelSearch.username}"
maxlength="255"/>
</div>
<div class="search_field_button">
<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 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_add"><i class="fa-sharp fa-solid fa-plus"></i> [[#{button.create}]]</button><!-- 등록 -->
</div>
</div>
</form>
</div>
</div>
</div>
<div class="card mt-2">
<div class="card-body">
<form name="listForm">
<input name="id" type="hidden"/>
<input name="checkedIdForDel" type="hidden"/>
<input id="csrf" type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<table class="table table-hover pt-table">
<colgroup>
<col style="width: 5%;">
<col style="width: 5%;">
<col>
<col>
<col style="width: 7%;">
<col>
</colgroup>
<thead>
<tr>
<th class="no">번호</th>
<th><input type="checkbox" name="checkAll" class="check2" onclick="fncCheckAll()" th:title="#{input.selectAll.title}"></th><!-- 전체선택 -->
<th>ID</th>
<th>이름</th>
<th>상태</th>
<th>비고</th>
</tr>
</thead>
<tbody>
<tr th:each="row, status : ${page.content}">
<td class="no" th:text="${page.getTotalElements() - (page.getNumber() * page.getSize() + status.count ) + 1}"></td>
<td>
<input type="checkbox" name="checkField" class="check2" title="선택"/>
<input name="checkId" type="hidden" th:value="${row.esntlId}"/>
</td>
<td>
<span th:text="${row.userId}"></span>
</td>
<td>
<span th:text="${row.username}"></span>
</td>
<td>
<span th:text="${row.status.toString()}"></span>
</td>
<td>
<button type="button" class="btn btn-primary btn-sm" th:onclick="fnSelectItem([[${row.esntlId}]])" th:value="#{button.update}"><i class="fa-sharp fa-solid fa-edit"></i>
[[#{button.update}]]
</button>
</td>
</tr>
<!-- 글이 없는 경우 -->
<tr th:if="${page.content.size() == 0 }">
<td colspan="5" th:text="#{common.nodata.msg}"></td>
</tr>
</tbody>
</table>
</form>
</div>
<div class="card-footer">
<div th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}"></div>
</div>
</div>
</div>
</section>
</body>
<th:block layout:fragment="contentScript">
<script>
function fn_select_page(pageNo) {
document.searchForm.page.value = pageNo;
document.searchForm.submit();
}
function fnSelectItem(id) {
location.href = "[[@{/mgmt/users/update_view.do}]]" + "?userId=" + id;
}
$(function () {
let fnSearch = document.querySelector(".btn_search");
let fnDelete = document.querySelector(".btn_delete");
let fnAdd = document.querySelector(".btn_add");
fnSearch.addEventListener("click", function () {
document.searchForm.page.value = 1;
document.searchForm.submit();
});
fnDelete.addEventListener("click", function () {
var checkField = document.listForm.checkField;
var id = document.listForm.checkId;
var checkedIds = "";
var checkedCount = 0;
if (checkField) {
if (checkField.length > 1) {
for (var i = 0; i < checkField.length; i++) {
if (checkField[i].checked) {
checkedIds += ((checkedCount == 0 ? "" : ",") + id[i].value);
checkedCount++;
}
}
} else {
if (checkField.checked) {
checkedIds = id.value;
}
}
}
if (checkedIds.length > 0) {
if (confirm("[[#{common.delete.msg}]]")) {
document.listForm.checkedIdForDel.value = checkedIds;
document.listForm.method = 'POST';
document.listForm.action = '[[@{/mgmt/users/delete.do}]]';
document.listForm.submit();
}
}
});
fnAdd.addEventListener("click", function () {
location.href = '[[@{/mgmt/users/create_view.do}]]';
});
})
</script>
</th:block>
</html>