You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
7.4 KiB
183 lines
7.4 KiB
<script setup>
|
|
/**
|
|
* 에이전트 관리 화면 컴포넌트 (AgentManageView.vue)
|
|
* - 서버에 등록된 에이전트 노드 목록(/app-api/agent/getAgentInfoDtoList)을 조회하고 키워드 필터링을 제공합니다.
|
|
* - 각 에이전트의 '설정' 버튼 클릭 시 원격 환경설정 팝업(AgentConfigView)을 엽니다.
|
|
*/
|
|
import '@/assets/main.css'
|
|
import { useApiClient } from '@/components/apiClient'
|
|
import { onMounted, ref, reactive, computed } from 'vue'
|
|
import AgentConfigView from '@/components/AgentConfigView.vue'
|
|
|
|
const apiClient = useApiClient()
|
|
|
|
/**
|
|
* 에이전트 목록 조회 API 호출
|
|
*/
|
|
async function getAgentInfoDtoList() {
|
|
const response = await apiClient.post('/app-api/agent/getAgentInfoDtoList')
|
|
return response.data
|
|
}
|
|
|
|
// 에이전트 전체 목록 및 검색어 상태
|
|
let dfxAgentInfoDtoList = ref([])
|
|
const searchKeyword = ref('')
|
|
|
|
// 에이전트 설정 모달(AgentConfigView)에 전달할 데이터 객체 및 모달 참조
|
|
const agentConfigDto = reactive({})
|
|
const agentConfigViewModal = ref(null)
|
|
|
|
/**
|
|
* 검색어(Agent ID, 호스트명, 설명) 기준 실시간 필터링된 에이전트 목록
|
|
*/
|
|
const filteredAgentList = computed(() => {
|
|
const kw = searchKeyword.value.trim().toLowerCase()
|
|
if (!kw) return dfxAgentInfoDtoList.value
|
|
return dfxAgentInfoDtoList.value.filter(
|
|
(a) =>
|
|
(a.agentId && a.agentId.toLowerCase().includes(kw)) ||
|
|
(a.hostName && a.hostName.toLowerCase().includes(kw)) ||
|
|
(a.description && a.description.toLowerCase().includes(kw)),
|
|
)
|
|
})
|
|
|
|
/**
|
|
* 선택한 에이전트의 JSON 설정 문자열(settingsData)을 객체로 변환하여 설정 모달 오픈
|
|
* @param {Object} dfxAgentInfoDto 선택한 에이전트 정보
|
|
*/
|
|
function openAgentConfigView(dfxAgentInfoDto) {
|
|
const dto = JSON.parse(dfxAgentInfoDto.settingsData)
|
|
Object.assign(agentConfigDto, dto)
|
|
agentConfigViewModal.value.open()
|
|
}
|
|
|
|
/**
|
|
* 에이전트 목록 새로고침
|
|
*/
|
|
async function reloadList() {
|
|
dfxAgentInfoDtoList.value = await getAgentInfoDtoList()
|
|
}
|
|
|
|
// 컴포넌트 마운트 시 에이전트 목록 최초 로드
|
|
onMounted(async () => {
|
|
dfxAgentInfoDtoList.value = await getAgentInfoDtoList()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<main class="dfx-main">
|
|
<!-- 화면 상단 헤더 및 새로고침 버튼 -->
|
|
<div class="dfx-page-header">
|
|
<div>
|
|
<h3 class="dfx-page-title">에이전트 관리</h3>
|
|
<div class="dfx-page-subtitle">등록된 DFX 에이전트 노드 상태 조회 및 원격 환경설정(DropBox / Postman) 제어</div>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<button type="button" class="btn btn-sm dfx-btn-ghost d-flex align-items-center gap-1" @click="reloadList">
|
|
<i class="bi bi-arrow-clockwise"></i>
|
|
<span>새로고침</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 상단 상태 요약 배지 및 검색 필터 바 -->
|
|
<div class="card mb-3">
|
|
<div class="card-body py-2 px-3 d-flex flex-wrap align-items-center justify-content-between gap-2">
|
|
<div class="d-flex align-items-center gap-2">
|
|
<span class="dfx-badge dfx-badge-neutral">전체 {{ dfxAgentInfoDtoList.length }}</span>
|
|
<span class="dfx-badge dfx-badge-success">
|
|
<span class="dfx-badge-dot"></span>
|
|
정상 {{ dfxAgentInfoDtoList.filter((a) => a.statusCode === 'STATUS_OK').length }}
|
|
</span>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-2" style="min-width: 280px">
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text"><i class="bi bi-search"></i></span>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
placeholder="Agent ID, 호스트명, 설명 검색..."
|
|
v-model="searchKeyword"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 에이전트 목록 테이블 카드 -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<span class="d-flex align-items-center gap-2">
|
|
<i class="bi bi-hdd-network text-primary"></i>
|
|
<span>에이전트 노드 목록 (Agent Nodes)</span>
|
|
</span>
|
|
<span class="dfx-mono text-secondary" style="font-size: 11.5px">SHOWING {{ filteredAgentList.length }} NODES</span>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-bordered table-hover align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col" class="text-center">Agent ID</th>
|
|
<th scope="col" class="text-center">Host</th>
|
|
<th scope="col" class="text-center">port</th>
|
|
<th scope="col" class="text-center">Description</th>
|
|
<th scope="col" class="text-center">Postman Count</th>
|
|
<th scope="col" class="text-center">Dropbox Count</th>
|
|
<th scope="col" class="text-center">Last Check</th>
|
|
<th scope="col" class="text-center">Status</th>
|
|
<th scope="col" class="text-center">Setting</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="table-group-divider">
|
|
<tr
|
|
v-if="filteredAgentList.length > 0"
|
|
v-for="dfxAgentInfoDto in filteredAgentList"
|
|
:key="`${dfxAgentInfoDto.agentId}-${dfxAgentInfoDto.hostName}`"
|
|
>
|
|
<td scope="row" class="dfx-mono fw-semibold text-light">
|
|
<i class="bi bi-hdd-rack text-primary me-2"></i>{{ dfxAgentInfoDto.agentId }}
|
|
</td>
|
|
<td class="dfx-mono text-secondary">{{ dfxAgentInfoDto.hostName }}</td>
|
|
<td class="text-center dfx-mono">{{ dfxAgentInfoDto.listenPort }}</td>
|
|
<td class="text-secondary">{{ dfxAgentInfoDto.description || '-' }}</td>
|
|
<td class="text-end dfx-mono fw-semibold">{{ dfxAgentInfoDto.postmanCount }}</td>
|
|
<td class="text-end dfx-mono fw-semibold">{{ dfxAgentInfoDto.dropboxCount }}</td>
|
|
<td class="text-center dfx-mono text-secondary" style="font-size: 12px">
|
|
{{ dfxAgentInfoDto.lastStatusTimeString || '-' }}
|
|
</td>
|
|
<td class="text-center">
|
|
<span
|
|
class="dfx-badge"
|
|
:class="dfxAgentInfoDto.statusCode == 'STATUS_OK' ? 'dfx-badge-success' : 'dfx-badge-neutral'"
|
|
>
|
|
<span class="dfx-badge-dot"></span>
|
|
{{ dfxAgentInfoDto.statusCode == 'STATUS_OK' ? 'STATUS_OK' : dfxAgentInfoDto.statusCode || 'OFFLINE' }}
|
|
</span>
|
|
</td>
|
|
<td class="text-center">
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary btn-sm"
|
|
@click.prevent="openAgentConfigView(dfxAgentInfoDto)"
|
|
>
|
|
<i class="bi bi-sliders me-1"></i>설정
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<tr v-else>
|
|
<td scope="row" class="text-center py-4 text-secondary" colspan="9">no datas.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- 에이전트 설정 모달 팝업 컴포넌트 -->
|
|
<agent-config-view modal-id="agentConfigModalView" :dto="agentConfigDto" ref="agentConfigViewModal"></agent-config-view>
|
|
</template>
|
|
|
|
<style></style>
|