인증서 생성기 화면 및 관련 작업
This commit is contained in:
@@ -1,24 +1,107 @@
|
||||
<div class="card border-danger">
|
||||
<div class="card-header bg-danger text-white">System Engineering - Discovery Mode</div>
|
||||
<div class="card-body">
|
||||
<h5>1. 하니웰 자산 모델 탐사 (Crawler)</h5>
|
||||
<p class="text-muted">서버의 모든 노드를 훑어 CSV 파일을 생성합니다. (시간이 오래 걸릴 수 있음)</p>
|
||||
<button id="btnRunCrawler" class="btn btn-danger" onclick="runCrawler()">🚀 탐사 및 CSV 생성 시작</button>
|
||||
|
||||
<hr>
|
||||
@{
|
||||
ViewData["Title"] = "System Admin";
|
||||
}
|
||||
|
||||
<h5>2. DB 동기화 (CSV to Database)</h5>
|
||||
<p class="text-muted">생성된 Honeywell_FullMap.csv 파일을 읽어 DB에 일괄 저장합니다.</p>
|
||||
<button id="btnImportCsv" class="btn btn-warning" onclick="importCsvToDb()">📥 CSV 데이터를 DB로 가져오기</button>
|
||||
<div class="container mt-4">
|
||||
<div class="card border-danger shadow">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h4 class="mb-0">🛠️ System Engineering - Discovery Mode</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<section class="mb-5">
|
||||
<h5>1. 하니웰 Asset 모델 탐사 (Crawler)</h5>
|
||||
<p class="text-muted">서버의 모든 노드를 훑어 <code>Honeywell_FullMap.csv</code> 파일을 생성합니다. <br>
|
||||
<small class="text-danger">* 주의: 노드 수가 많을 경우 하니웰 서버와 네트워크에 부하가 발생할 수 있습니다.</small></p>
|
||||
|
||||
<button id="btnRunCrawler" class="btn btn-danger btn-lg" onclick="runCrawler()">
|
||||
<span id="spinnerCrawler" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
||||
🚀 탐사 및 CSV 생성 시작
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<hr>
|
||||
|
||||
<section class="mt-4">
|
||||
<h5>2. DB 동기화 (CSV to Database)</h5>
|
||||
<p class="text-muted">생성된 CSV 파일을 읽어 PostgreSQL <code>raw_node_map</code> 테이블에 일괄 저장합니다.</p>
|
||||
<button id="btnImportCsv" class="btn btn-warning btn-lg" onclick="importCsvToDb()">
|
||||
<span id="spinnerImport" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
|
||||
📥 CSV 데이터를 DB로 가져오기
|
||||
</button>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card-footer bg-light">
|
||||
<div id="statusMessage" class="fw-bold"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
function runCrawler() {
|
||||
if(!confirm("전체 노드 탐사를 시작하시겠습니까? 하니웰 서버에 부하가 갈 수 있습니다.")) return;
|
||||
// API 호출: /Engineering/RunCrawler
|
||||
async function runCrawler() {
|
||||
const btn = document.getElementById('btnRunCrawler');
|
||||
const spinner = document.getElementById('spinnerCrawler');
|
||||
const status = document.getElementById('statusMessage');
|
||||
|
||||
if (!confirm("전체 노드 탐사를 시작하시겠습니까? (ns=1;s=$assetmodel 기준)")) return;
|
||||
|
||||
try {
|
||||
// 버튼 비활성화 및 로딩 시작
|
||||
btn.disabled = true;
|
||||
spinner.classList.remove('d-none');
|
||||
status.innerText = "⏳ 하니웰 서버 탐사 중... 잠시만 기다려 주세요.";
|
||||
status.className = "text-primary fw-bold";
|
||||
|
||||
const response = await fetch('/Engineering/RunCrawler', { method: 'POST' });
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert("✅ 성공: " + result.message);
|
||||
status.innerText = "✅ 탐사 완료: " + result.message;
|
||||
status.className = "text-success fw-bold";
|
||||
} else {
|
||||
throw new Error(result.message || "탐사 실패");
|
||||
}
|
||||
} catch (error) {
|
||||
alert("❌ 에러 발생: " + error.message);
|
||||
status.innerText = "❌ 오류: " + error.message;
|
||||
status.className = "text-danger fw-bold";
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
spinner.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
function importCsvToDb() {
|
||||
// API 호출: /Engineering/ImportCsv
|
||||
|
||||
async function importCsvToDb() {
|
||||
const btn = document.getElementById('btnImportCsv');
|
||||
const spinner = document.getElementById('spinnerImport');
|
||||
const status = document.getElementById('statusMessage');
|
||||
|
||||
if (!confirm("CSV 데이터를 DB에 덮어씌우시겠습니까? (기존 데이터는 삭제됩니다.)")) return;
|
||||
|
||||
try {
|
||||
btn.disabled = true;
|
||||
spinner.classList.remove('d-none');
|
||||
status.innerText = "⏳ DB 동기화 작업 중...";
|
||||
|
||||
const response = await fetch('/Engineering/ImportCsv', { method: 'POST' });
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert("✅ 성공: " + result.message);
|
||||
status.innerText = "✅ DB 동기화 완료!";
|
||||
status.className = "text-success fw-bold";
|
||||
} else {
|
||||
throw new Error(result.message || "동기화 실패");
|
||||
}
|
||||
} catch (error) {
|
||||
alert("❌ 에러 발생: " + error.message);
|
||||
status.innerText = "❌ 오류: " + error.message;
|
||||
status.className = "text-danger fw-bold";
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
spinner.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
}
|
||||
128
OpcPksPlatform/OpcPks.Web/Views/Engineering/CertManager.cshtml
Normal file
128
OpcPksPlatform/OpcPks.Web/Views/Engineering/CertManager.cshtml
Normal file
@@ -0,0 +1,128 @@
|
||||
@model OpcPks.Core.Models.CertRequestModel
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4>🛡️ 하니웰 Experion 전용 인증서 생성기</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form asp-action="GenerateCertificate" method="post">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">시스템 구성</label>
|
||||
<select asp-for="IsRedundant" class="form-select" id="systemType">
|
||||
<option value="false">Standalone (단일 서버)</option>
|
||||
<option value="true">Redundant (이중화 서버)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">어플리케이션 이름</label>
|
||||
<input asp-for="ApplicationName" class="form-control" placeholder="OpcPksClient" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<h5 class="text-secondary"><span class="badge bg-info">Primary</span> 서버 정보</h5>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Host Name</label>
|
||||
<input asp-for="PrimaryHostName" class="form-control" placeholder="예: HONPKS" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-warning">IP Address (Yellow)</label>
|
||||
<input asp-for="PrimaryIpA" class="form-control" placeholder="192.168.0.20" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-success">IP Address (Green)</label>
|
||||
<input asp-for="PrimaryIpB" class="form-control" placeholder="192.168.1.20" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="text-secondary mt-4"><span class="badge bg-secondary" id="secondaryBadge">Secondary</span> 서버 정보</h5>
|
||||
<div class="row mb-3" id="secondaryFields">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Host Name</label>
|
||||
<input asp-for="SecondaryHostName" class="form-control sec-input" placeholder="예: HONPKS" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-warning">IP Address (Yellow)</label>
|
||||
<input asp-for="SecondaryIpA" class="form-control sec-input" placeholder="192.168.0.21" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-success">IP Address (Green)</label>
|
||||
<input asp-for="SecondaryIpB" class="form-control sec-input" placeholder="192.168.1.21" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-light border-warning mt-5">
|
||||
<div class="card-body">
|
||||
@if (ViewBag.IsCertExists == true)
|
||||
{
|
||||
<div class="alert alert-warning d-flex align-items-center">
|
||||
<span class="fs-4 me-3">⚠️</span>
|
||||
<div>
|
||||
<strong>기존 인증서가 이미 존재합니다!</strong><br />
|
||||
새로 생성 시 기존 서버와의 통신 신뢰관계(Trust)가 깨질 수 있습니다.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="chkUnlock" onchange="toggleCertBtn()">
|
||||
<label class="form-check-label text-danger fw-bold" for="chkUnlock">
|
||||
[위험 인지] 기존 인증서를 무시하고 새로 생성하는 것에 동의합니다.
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="btnGenerate" class="btn btn-danger btn-lg w-100" disabled>
|
||||
🔒 인증서가 이미 존재하여 잠겨있습니다
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="submit" id="btnGenerate" class="btn btn-primary btn-lg w-100">
|
||||
🚀 인증서 생성 및 자동 적용 시작
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// 1. 이중화 선택 로직
|
||||
$('#systemType').change(function () {
|
||||
const isRedundant = $(this).val() === 'true';
|
||||
$('.sec-input').prop('disabled', !isRedundant);
|
||||
|
||||
if(isRedundant) {
|
||||
$('#secondaryBadge').removeClass('bg-secondary').addClass('bg-info');
|
||||
} else {
|
||||
$('#secondaryBadge').removeClass('bg-info').addClass('bg-secondary');
|
||||
}
|
||||
});
|
||||
|
||||
$('#systemType').trigger('change');
|
||||
});
|
||||
|
||||
// 2. 버튼 잠금 해제 로직
|
||||
function toggleCertBtn() {
|
||||
const isChecked = document.getElementById('chkUnlock').checked;
|
||||
const btn = document.getElementById('btnGenerate');
|
||||
|
||||
if(isChecked) {
|
||||
btn.disabled = false;
|
||||
btn.innerText = "🔥 인증서 새로 생성 (강제 실행)";
|
||||
btn.classList.replace('btn-danger', 'btn-warning');
|
||||
} else {
|
||||
btn.disabled = true;
|
||||
btn.innerText = "🔒 인증서가 이미 존재하여 잠겨있습니다";
|
||||
btn.classList.replace('btn-warning', 'btn-danger');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">OpcPks.Web</a>
|
||||
<a class="navbar-brand fw-bold" asp-area="" asp-controller="Home" asp-action="Index">🚀 OpcPks Platform</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@@ -22,7 +22,24 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<span class="nav-link text-muted">|</span>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-primary" asp-area="" asp-controller="Engineering" asp-action="TagExplorer">🔍 Tag Explorer</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-success" asp-area="" asp-controller="Engineering" asp-action="Admin">⚙️ DB Admin</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-danger" asp-area="" asp-controller="Engineering" asp-action="CertManager">🛡️ Cert Manager</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -30,7 +47,23 @@
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
@if (TempData["Success"] != null)
|
||||
{
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<strong>성공!</strong> @TempData["Success"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
}
|
||||
@if (TempData["Error"] != null)
|
||||
{
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<strong>오류!</strong> @TempData["Error"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
@@ -46,4 +79,4 @@
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
Reference in New Issue
Block a user