<?php
if (!defined('_GNUBOARD_')) exit;
add_stylesheet('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">', 0);
include_once(G5_LIB_PATH.'/thumbnail.lib.php');
// 스타일시트 연결
add_stylesheet('<link rel="stylesheet" href="'.$board_skin_url.'/style.view.css">', 0);
$view_content = get_view_thumbnail($view['content']);
$is_run = (isset($view['x2_run']) && ($view['x2_run'] == '1' || $view['x2_run'] == '실행' || strtolower($view['x2_run']) == 'run'));
// 1. [수정] 데몬 파일 소스코드 조회 (기존 x2_daemon -> wr_subject로 변경)
$daemon_name = isset($view['wr_subject']) ? $view['wr_subject'] : '';
$daemon_code = '';
if ($daemon_name) {
$daemon_root = '/home/www/DATA/UPBIT/daemon';
$target_path = '';
// 루트에 바로 있는지 먼저 확인 (속도 최적화)
if (file_exists($daemon_root . '/' . $daemon_name)) {
$target_path = $daemon_root . '/' . $daemon_name;
} else {
// 없으면 하위 디렉토리 전체 스캔
if (is_dir($daemon_root) && is_readable($daemon_root)) {
try {
$di = new RecursiveDirectoryIterator($daemon_root, RecursiveDirectoryIterator::SKIP_DOTS);
$it = new RecursiveIteratorIterator($di);
foreach ($it as $file) {
if ($file->isFile() && $file->getFilename() == $daemon_name) {
$target_path = $file->getPathname();
break; // 찾으면 중단
}
}
} catch (Exception $e) { }
}
}
// 파일 읽기
if ($target_path && file_exists($target_path) && is_readable($target_path)) {
$daemon_code = file_get_contents($target_path);
}
}
// 상태 색상 설정
$state_color = $is_run ? '#00d4ff' : '#ff2d55';
?>
<style>
/* 전체 레이아웃 및 배경 */
#VIEW { position: relative; min-height: 100vh; background: #020617; padding: 40px 50px; font-family: 'Pretendard', sans-serif; color: #f1f5f9; overflow-x: hidden; }
#starCanvas { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; opacity: 0.25; }
.View-Wrapper { position: relative; z-index: 2; background: rgba(2, 6, 23, 0.8); border: 1px solid #1e293b; border-radius: 16px; box-shadow: 0 25px 50px rgba(0,0,0,0.6); overflow: hidden; backdrop-filter: blur(10px); }
/* 헤더 */
.View-Header { padding: 20px 30px; background: rgba(15, 23, 42, 0.6); border-bottom: 1px solid #1e293b; display: flex; justify-content: space-between; align-items: center; }
.View-Header h2 { margin: 0; font-size: 1.1rem; font-weight: 900; color: #fff; display: flex; align-items: center; gap: 12px; letter-spacing: -0.5px; }
.View-Header h2 i { color: #00d4ff; }
.View-Header .status-badge { padding: 5px 14px; border-radius: 50px; font-size: 0.7rem; font-weight: 800; background: rgba(0, 0, 0, 0.3); color: <?php echo $state_color; ?>; border: 1px solid <?php echo $state_color; ?>; text-transform: uppercase; }
/* [단독 디자인] Target Object 히어로 섹션 */
.Hero-Target-Section { padding: 50px 40px; background: linear-gradient(135deg, #0f172a 0%, #020617 100%); border-bottom: 1px solid #1e293b; display: flex; align-items: center; gap: 30px; position: relative; overflow: hidden; }
.Hero-Target-Section::before { content: ''; position: absolute; top: 0; left: 0; width: 4px; height: 100%; background: #00d4ff; box-shadow: 0 0 20px #00d4ff; }
.Hero-Icon { width: 80px; height: 80px; background: rgba(0, 212, 255, 0.1); border: 1px solid rgba(0, 212, 255, 0.3); border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 2.5rem; color: #00d4ff; text-shadow: 0 0 15px rgba(0, 212, 255, 0.5); }
.Hero-Content { flex: 1; }
.Hero-Content .label { display: block; color: #64748b; font-size: 0.75rem; font-weight: 800; text-transform: uppercase; letter-spacing: 2px; margin-bottom: 8px; }
.Hero-Content .subject-text { font-size: 2.2rem; color: #fff; font-weight: 900; margin-bottom: 8px; letter-spacing: -1px; font-family: 'JetBrains Mono', monospace; }
.Hero-Content .kor-text { font-size: 1.2rem; color: #94a3b8; font-weight: 600; display: flex; align-items: center; gap: 10px; margin-bottom: 15px; }
.Hero-Content .kor-text::before { content: ''; width: 20px; height: 2px; background: #1e293b; }
/* 메모(wr_1) 출력 스타일 */
.Hero-Memo { background: rgba(0, 212, 255, 0.08); border: 1px solid rgba(0, 212, 255, 0.2); padding: 12px 20px; border-radius: 8px; display: inline-flex; align-items: flex-start; gap: 12px; color: #00d4ff; font-size: 0.95rem; font-weight: 500; line-height: 1.5; max-width: 80%; }
.Hero-Memo i { font-size: 1rem; margin-top: 3px; filter: drop-shadow(0 0 5px #00d4ff); }
/* 대시보드 그리드 */
.Dashboard-Grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: #1e293b; border-bottom: 1px solid #1e293b; }
.info-item { background: #080c14; padding: 20px 30px; transition: 0.3s; }
.info-item:hover { background: #0f172a; }
.info-item .label { display: flex; align-items: center; color: #64748b; font-size: 0.65rem; font-weight: 800; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px; }
.info-item .label i { color: #00d4ff; margin-right: 8px; font-size: 0.8rem; }
.info-item .value { font-size: 0.95rem; color: #f1f5f9; font-weight: 600; }
.info-item .value.accent { color: #00d4ff; font-family: 'JetBrains Mono', monospace; }
/* 실시간 상태바 */
.dynamic-result-area { background: #020617; padding: 15px 30px; border-bottom: 1px solid #1e293b; display: flex; flex-wrap: wrap; gap: 15px 40px; border-left: 4px solid #00d4ff; }
.res-item { font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; display: flex; align-items: center; }
.res-item span { color: #475569; margin-right: 10px; font-weight: 800; }
.res-item b { color: #94a3b8; text-transform: uppercase; }
/* 본문 섹션 */
.View-Body { padding: 40px; background: #000; }
.section-title { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; color: #00d4ff; font-size: 0.85rem; font-weight: 900; text-transform: uppercase; }
.section-title::after { content: ''; flex: 1; height: 1px; background: linear-gradient(90deg, #1e293b, transparent); }
.main-content { line-height: 1.8; color: #cbd5e1; font-size: 1.05rem; margin-bottom: 50px; }
.info-card { background: rgba(30, 41, 59, 0.3); padding: 25px; border-radius: 12px; border: 1px solid #1e293b; margin-bottom: 30px; }
.info-card.special { border-left: 4px solid #ff2d55; background: rgba(255, 45, 85, 0.05); }
.info-card.additional { border-left: 4px solid #00d4ff; background: rgba(0, 212, 255, 0.05); }
/* 코드 블럭 (300px 스크롤 적용) */
.code-block { background: #050505; border: 1px solid #1e293b; padding: 25px; border-radius: 8px; font-family: 'JetBrains Mono', monospace; font-size: 0.85rem; color: #00d4ff; overflow-x: auto; overflow-y: auto; line-height: 1.6; max-height: 300px; margin-bottom: 30px; }
.code-block::-webkit-scrollbar { width: 8px; height: 8px; }
.code-block::-webkit-scrollbar-track { background: #020617; }
.code-block::-webkit-scrollbar-thumb { background: #1e293b; border-radius: 4px; }
.code-block::-webkit-scrollbar-thumb:hover { background: #00d4ff; }
/* 태그 링크 */
.Tag-Container { padding: 20px 40px; background: #080c14; border-top: 1px solid #1e293b; display: flex; flex-wrap: wrap; gap: 10px; }
.x2-tag-item { display: inline-block; padding: 4px 12px; background: #1e293b; color: #94a3b8; border-radius: 4px; font-size: 0.8rem; font-weight: 700; transition: 0.2s; text-decoration: none; }
.x2-tag-item:hover { background: #00d4ff; color: #020617; transform: translateY(-2px); }
/* 버튼 */
.View-Footer { padding: 30px; background: #080c14; border-top: 1px solid #1e293b; display: flex; justify-content: center; gap: 12px; }
.btn-ui { padding: 12px 28px; border-radius: 6px; font-size: 0.85rem; font-weight: 800; cursor: pointer; transition: 0.2s; border: 1px solid #334155; background: #0f172a; color: #f1f5f9; text-decoration: none; display: inline-flex; align-items: center; gap: 8px; }
.btn-ui:hover { border-color: #00d4ff; color: #00d4ff; background: #1e293b; }
.btn-ui.btn-edit { background: #00d4ff; color: #020617; border: none; }
.btn-ui.btn-edit:hover { background: #fff; color: #000; box-shadow: 0 0 20px rgba(0, 212, 255, 0.4); }
.Post-Nav { margin-top: 30px; display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.nav-item { padding: 20px; background: rgba(15, 23, 42, 0.5); border: 1px solid #1e293b; border-radius: 12px; text-decoration: none; transition: 0.3s; }
.nav-item:hover { border-color: #00d4ff; background: rgba(0, 212, 255, 0.05); }
.nav-item .nav-label { display: block; font-size: 0.7rem; color: #64748b; font-weight: 800; margin-bottom: 5px; text-transform: uppercase; }
.nav-item .nav-title { display: block; font-size: 0.95rem; color: #f1f5f9; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
</style>
<article id="VIEW">
<canvas id="starCanvas"></canvas>
<div class="View-Wrapper">
<header class="View-Header">
<h2><i class="fa-solid fa-terminal"></i> SYSTEM_CORE_VIEWER</h2>
<div class="status-badge">
<i class="fa-solid fa-circle" style="font-size: 8px; margin-right: 6px; vertical-align: middle;"></i>
<?php echo $is_run ? 'DAEMON' : 'STOPPED'; ?>
</div>
</header>
<section class="Hero-Target-Section">
<div class="Hero-Icon">
<i class="fa-solid fa-microchip"></i>
</div>
<div class="Hero-Content">
<span class="label">Target Object / Identifier</span>
<div class="subject-text"><?php echo get_text($view['wr_subject']); ?></div>
<div class="kor-text"><?php echo get_text($view['wr_subject_kor']); ?></div>
<?php if(isset($view['wr_1']) && trim($view['wr_1'])) { ?>
<div class="Hero-Memo">
<i class="fa-solid fa-note-sticky"></i>
<span><?php echo nl2br(get_text($view['wr_1'])); ?></span>
</div>
<?php } ?>
</div>
</section>
<section class="Dashboard-Grid">
<div class="info-item">
<span class="label"><i class="fa-solid fa-building-columns"></i> Exchange</span>
<div class="value"><?php echo $view['ca_name'] ? $view['ca_name'] : 'N/A'; ?></div>
</div>
<div class="info-item">
<span class="label"><i class="fa-solid fa-calendar-check"></i> TABLE</span>
<div class="value accent"><?php echo isset($view['sel_event']) && $view['sel_event'] ? get_text($view['sel_event']) : '-'; ?></div>
</div>
<div class="info-item">
<span class="label"><i class="fa-solid fa-gears"></i> Daemon Process</span>
<div class="value accent"><?php echo $view['wr_subject'] ? $view['wr_subject'] : 'STANDALONE'; ?></div>
</div>
<div class="info-item">
<span class="label"><i class="fa-solid fa-layer-group"></i> TYPE</span>
<div class="value"><?php echo $view['x2_ca2']; ?></div>
</div>
<div class="info-item">
<span class="label"><i class="fa-solid fa-layer-group"></i> FORM</span>
<div class="value"><?php echo $view['x2_ca3']; ?></div>
</div>
<div class="info-item">
<span class="label"><i class="fa-solid fa-shield-halved"></i> MANAGEMENT</span>
<div class="value accent" style="color:<?php echo $state_color; ?>;"><?php echo $is_run ? 'START' : 'STOP'; ?></div>
</div>
</section>
<div class="dynamic-result-area">
<div class="res-item"><span>[STATUS]</span> <b style="color:<?php echo $state_color; ?>"><?php echo $is_run ? 'ACTIVE' : 'INACTIVE'; ?></b></div>
<div class="res-item"><span>[ID]</span> <b>#<?php echo $view['wr_id']; ?></b></div>
<div class="res-item"><span>[DAEMON]</span> <b><?php echo $view['wr_subject']; ?></b></div>
<div class="res-item"><span>[TABLE]</span> <b><?php echo isset($view['sel_event']) ? $view['sel_event'] : ''; ?></b></div>
</div>
<section class="View-Body">
<?php if ($view_content) { ?>
<div class="section-title">Detailed Description</div>
<div class="main-content">
<?php echo $view_content; ?>
</div>
<?php } ?>
<?php if (isset($view['x2_content']) && trim($view['x2_content'])) { ?>
<div class="section-title">Additional Information</div>
<div class="info-card additional">
<div class="main-content" style="margin-bottom:0;">
<?php echo nl2br(stripslashes($view['x2_content'])); ?>
</div>
</div>
<?php } ?>
<?php if (isset($view['x2_content_2']) && trim($view['x2_content_2'])) { ?>
<div class="section-title" style="color:#ff2d55;">Special Notes & Warnings</div>
<div class="info-card special">
<div class="main-content" style="margin-bottom:0; color:#fca5a5;">
<?php echo nl2br(stripslashes($view['x2_content_2'])); ?>
</div>
</div>
<?php } ?>
<?php if ($daemon_code) { ?>
<div class="section-title" style="color:#00d4ff;">Daemon Source Code (<?php echo $daemon_name; ?>)</div>
<pre class="code-block"><?php echo htmlspecialchars($daemon_code); ?></pre>
<?php } ?>
</section>
<?php if (count($view['link']) > 0 || $view['file']['count'] > 0) { ?>
<section style="padding: 25px 40px; background: rgba(15, 23, 42, 0.4); border-top: 1px solid #1e293b;">
<div style="display:flex; flex-wrap:wrap; gap:12px;">
<?php
for ($i=1; $i<=count($view['link']); $i++) {
if ($view['link'][$i]) {
echo '<a href="'.$view['link_href'][$i].'" target="_blank" class="btn-ui" style="font-size:0.75rem;"><i class="fa-solid fa-link"></i> '.cut_str($view['link'][$i], 40).'</a>';
}
}
if ($view['file']['count']) {
for ($i=0; $i<count($view['file']); $i++) {
if (isset($view['file'][$i]['source']) && !isset($view['file'][$i]['view'])) {
echo '<a href="'.$view['file'][$i]['href'].'" class="btn-ui" style="font-size:0.75rem;"><i class="fa-solid fa-download"></i> '.$view['file'][$i]['source'].'</a>';
}
}
}
?>
</div>
</section>
<?php } ?>
<?php if ($view['x2_tag']) { ?>
<div class="Tag-Container">
<i class="fa-solid fa-tags" style="color:#475569; margin-right:10px; align-self:center;"></i>
<?php
$tags = explode(',', $view['x2_tag']);
foreach($tags as $tag_val) {
$tag_val = trim($tag_val);
if($tag_val) {
echo '<a href="'.G5_BBS_URL.'/board.php?bo_table='.$bo_table.'&stx='.urlencode($tag_val).'&sfl=wr_subject||wr_content" class="x2-tag-item">#'.$tag_val.'</a>';
}
}
?>
</div>
<?php } ?>
<footer class="View-Footer">
<a href="<?php echo $list_href; ?>" class="btn-ui"><i class="fa-solid fa-list"></i> LIST</a>
<?php if ($update_href) { ?>
<a href="<?php echo $update_href; ?>" class="btn-ui btn-edit"><i class="fa-solid fa-pen-to-square"></i> EDIT CONFIG</a>
<?php } ?>
<?php if ($delete_href) { ?>
<a href="<?php echo $delete_href; ?>" onclick="return confirm('정말 삭제하시겠습니까?');" class="btn-ui" style="color:#ff2d55; border-color:#ff2d5522;"><i class="fa-solid fa-trash-can"></i> DELETE</a>
<?php } ?>
</footer>
</div>
<nav class="Post-Nav">
<?php if ($prev_href) { ?>
<a href="<?php echo $prev_href; ?>" class="nav-item">
<span class="nav-label"><i class="fa-solid fa-arrow-left"></i> Previous Post</span>
<span class="nav-title"><?php echo $prev_wr_subject; ?></span>
</a>
<?php } else { ?>
<div class="nav-item" style="opacity:0.3; cursor:default;">
<span class="nav-label">Previous Post</span>
<span class="nav-title">No more posts</span>
</div>
<?php } ?>
<?php if ($next_href) { ?>
<a href="<?php echo $next_href; ?>" class="nav-item" style="text-align:right;">
<span class="nav-label">Next Post <i class="fa-solid fa-arrow-right"></i></span>
<span class="nav-title"><?php echo $next_wr_subject; ?></span>
</a>
<?php } else { ?>
<div class="nav-item" style="opacity:0.3; cursor:default; text-align:right;">
<span class="nav-label">Next Post</span>
<span class="nav-title">No more posts</span>
</div>
<?php } ?>
</nav>
</article>
<script>
$(function() {
// 배경 애니메이션
const canvas = document.getElementById('starCanvas');
const ctx = canvas.getContext('2d');
let stars = [];
function init() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
stars = [];
for(let i=0; i<150; i++) {
stars.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
size: Math.random() * 2,
spd: Math.random() * 0.4 + 0.1
});
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "rgba(255, 255, 255, 0.4)";
stars.forEach(s => {
ctx.beginPath();
ctx.arc(s.x, s.y, s.size, 0, Math.PI*2);
ctx.fill();
s.y += s.spd;
if(s.y > canvas.height) s.y = 0;
});
requestAnimationFrame(draw);
}
init();
draw();
window.onresize = init;
});
</script>