GNU/_PAGE/maria/maria.php
<?php
/* ======================================================
   마리아DB 통합 관제 센터 (Main Dashboard)
   - DB: upbit_data
   - 목적: 전체 현황 요약 및 전용 관제 페이지 연결
====================================================== */

error_reporting(E_ALL);
ini_set('display_errors', 1);
date_default_timezone_set('Asia/Seoul');

require_once '/home/www/DB/db_upbit.php';
$pdo = $db_upbit;
$DB_NAME = 'upbit_data';

/* 1. 서버 전역 상태 정보 조회 */
$status_res = $pdo->query("SHOW GLOBAL STATUS WHERE Variable_name IN ('Uptime', 'Threads_connected', 'Max_used_connections', 'Questions')");
$db_status = [];
while($row = $status_res->fetch(PDO::FETCH_ASSOC)) {
    $db_status[$row['Variable_name']] = $row['Value'];
}

/* 2. 전역 설정 정보 */
$var_res = $pdo->query("SHOW VARIABLES LIKE 'event_scheduler'");
$scheduler_val = $var_res->fetch(PDO::FETCH_ASSOC)['Value'];
$ver_res = $pdo->query("SELECT VERSION() as ver");
$db_version = $ver_res->fetch(PDO::FETCH_ASSOC)['ver'];

/* 3. 테이블 요약 (용량 TOP 5) */
$table_sql = "
    SELECT TABLE_NAME, TABLE_ROWS, 
           ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) AS total_mb,
           ROUND(DATA_FREE / 1024 / 1024, 2) AS free_mb
    FROM information_schema.TABLES
    WHERE TABLE_SCHEMA = :db
";
$stmt = $pdo->prepare($table_sql);
$stmt->execute(['db' => $DB_NAME]);
$all_tables = $stmt->fetchAll(PDO::FETCH_ASSOC);

$summary = ['count' => count($all_tables), 'size' => 0, 'free' => 0];
foreach($all_tables as $t) {
    $summary['size'] += $t['total_mb'];
    $summary['free'] += $t['free_mb'];
}

usort($all_tables, function($a, $b) { return $b['total_mb'] <=> $a['total_mb']; });
$top_tables = array_slice($all_tables, 0, 5);

/* 4. 활성 이벤트 수 조회 */
$event_sql = "SELECT COUNT(*) FROM information_schema.EVENTS WHERE EVENT_SCHEMA = :db AND STATUS = 'ENABLED'";
$e_stmt = $pdo->prepare($event_sql);
$e_stmt->execute(['db' => $DB_NAME]);
$active_events = $e_stmt->fetchColumn();

// 헤더 부분 포함
require_once '/home/www/GNU/_PAGE/head.php';
?>

<title>마리아DB 관제 대시보드</title>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">

<style>
    :root {
        --bg-main: #0b0f1a;
        --card-bg: #161b2a;
        --border: #2d3343;
        --accent: #38bdf8;
        --green: #4ade80;
        --rose: #f43f5e;
        --dim: #94a3b8;
        padding: 0px; 
    }

    body { background: var(--bg-main); color: #e2e8f0; font-family: 'Inter', 'Malgun Gothic', sans-serif; padding: 0 0 40px 0; margin: 0; font-size: 16px; }

    @keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
    .ani { animation: fadeIn 0.6s ease backwards; }

    .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 30px; }
    .header h1 { font-size: 32px; color: var(--accent); margin: 0; font-weight: 900; letter-spacing: -1px; padding-left:20px; }
    .live-clock { font-family: 'JetBrains Mono', monospace; font-size: 18px; color: var(--dim); }

    .grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-bottom: 30px; }
    .grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 25px; align-items: stretch; }

    .section-wrapper { display: flex; flex-direction: column; height: 100%; }

    .card { 
        background: var(--card-bg); border: 1px solid var(--border); border-radius: 9px; 
        padding: 30px; transition: 0.3s; position: relative; overflow: hidden;
        flex-grow: 1;
    }
    .card:hover { border-color: var(--accent); }
    .card-label { font-size: 13px; color: var(--dim); display: block; margin-bottom: 12px; text-transform: uppercase; font-weight: 600; }
    .card-value { font-size: 34px; font-weight: 800; }

    .pulse { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 10px; }
    .pulse-green { background: var(--green); box-shadow: 0 0 10px var(--green); animation: blink 2s infinite; }
    @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }

    table { width: 100%; border-collapse: collapse; margin-top: 15px; }
    th { text-align: left; font-size: 12px; color: var(--dim); padding: 12px 10px; border-bottom: 1px solid var(--border); }
    td { padding: 15px 10px; border-bottom: 1px solid var(--border); font-size: 14px; }

    .btn-container-outer { margin-top: 15px; width: 100%; }
    .btn-move { 
        display: block; width: 100%; text-align: center; box-sizing: border-box;
        background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); 
        color: var(--accent); text-decoration: none; 
        padding: 15px 0; border-radius: 7px; font-size: 15px; font-weight: bold;
        transition: 0.3s; border: 1px solid var(--border);
        box-shadow: 0 4px 10px rgba(0,0,0,0.3);
    }
    .btn-move:hover { 
        border-color: var(--accent); 
        background: var(--accent); 
        color: var(--bg-main);
        box-shadow: 0 0 20px rgba(56, 189, 248, 0.4);
        transform: translateY(-3px);
    }

    .font-mono { font-family: 'JetBrains Mono', monospace; }

    .header, .grid-4, .grid-2 {
        margin:30px 50px 0 50px;
    }

    /* 전체 스크롤바 스타일 */
    ::-webkit-scrollbar { width: 10px; }
    ::-webkit-scrollbar-track { background: #0d1117; }
    ::-webkit-scrollbar-thumb { background: #30363d; border-radius: 4px; border: 2px solid #0d1117; }
    ::-webkit-scrollbar-thumb:hover { background: #8b949e; } 
</style>

<body>

<div class="header ani" style="animation-delay: 0.1s;">
    <h1><i class="fa-solid fa-users-gear"></i> MARIA DB MAIN <span>DASHBOARD</span></h1>
    <div class="live-clock" id="clock">00:00:00</div>
</div>

<div class="grid-4">
    <!-- 항목 한글화 적용 -->
    <div class="card ani" style="animation-delay: 0.2s;">
        <span class="card-label">DB 엔진 버전</span>
        <div class="card-value" style="font-size: 20px; color: var(--accent);"><?= $db_version ?></div>
        <div style="margin-top: 10px; font-size: 12px; color: var(--dim);">데이터베이스 시스템 정상 가동 중</div>
    </div>
    <div class="card ani" style="animation-delay: 0.3s;">
        <span class="card-label">현재 연결 수 (Session)</span>
        <div class="card-value counter" data-target="<?= $db_status['Threads_connected'] ?>">0</div>
        <div style="margin-top: 5px; font-size: 12px; color: var(--dim);">최대 기록 사용량: <?= $db_status['Max_used_connections'] ?></div>
    </div>
    <div class="card ani" style="animation-delay: 0.4s;">
        <span class="card-label">총 데이터 용량</span>
        <div class="card-value"><?= number_format($summary['size'] / 1024, 2) ?> <small style="font-size: 16px;">GB</small></div>
        <div style="margin-top: 10px; font-size: 12px; color: var(--dim);">전체 <?= $summary['count'] ?>개 테이블 통합</div>
    </div>
    <div class="card ani" style="animation-delay: 0.5s;">
        <span class="card-label">이벤트 스케줄러 상태</span>
        <div class="card-value" style="color: <?= ($scheduler_val=='ON')? 'var(--green)':'var(--rose)' ?>;">
            <span class="pulse <?= ($scheduler_val=='ON')? 'pulse-green':'' ?>"></span><?= ($scheduler_val=='ON')? '가동 중':'중단됨' ?>
        </div>
        <div style="margin-top: 5px; font-size: 12px; color: var(--dim);">활성화된 이벤트: <?= $active_events ?>건</div>
    </div>
</div>

<div class="grid-2">
    <!-- 왼쪽 섹션: 대용량 테이블 -->
    <div class="section-wrapper ani" style="animation-delay: 0.6s;">
        <div class="card">
            <span class="card-label" style="color: var(--accent);">상위 5개 대용량 테이블</span>
            <table>
                <thead>
                    <tr>
                        <th>테이블명</th>
                        <th style="text-align: right;">행 수(Rows)</th>
                        <th style="text-align: right;">용량(Size)</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach($top_tables as $t): ?>
                    <tr>
                        <td style="font-weight: bold; color: #fff;"><?= $t['TABLE_NAME'] ?></td>
                        <td style="text-align: right;" class="font-mono"><?= number_format($t['TABLE_ROWS']) ?></td>
                        <td style="text-align: right;" class="font-mono" style="color: var(--accent);"><?= number_format($t['total_mb'], 1) ?> MB</td>
                    </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
        <div class="btn-container-outer">
            <a href="upbit_db.php" class="btn-move">DB 구조 및 상세 용량 분석 &rarr;</a>
        </div>
    </div>

    <!-- 오른쪽 섹션: 시스템 건강 지표 -->
    <div class="section-wrapper ani" style="animation-delay: 0.7s;">
        <div class="card">
            <span class="card-label" style="color: var(--green);">데이터베이스 상태 지표</span>
            <div style="margin-top: 10px;">
                <div style="margin-bottom: 25px;">
                    <span style="font-size: 13px; color: var(--dim);">시스템 가동 시간 (Uptime)</span>
                    <div style="font-size: 22px; font-weight: bold; margin-top: 5px;" class="font-mono">
                        <?= number_format($db_status['Uptime'] / 3600, 1) ?> 시간
                    </div>
                </div>
                <div style="margin-bottom: 25px;">
                    <span style="font-size: 13px; color: var(--dim);">누적 쿼리 처리량 (Questions)</span>
                    <div style="font-size: 22px; font-weight: bold; margin-top: 5px;" class="font-mono">
                        <?= number_format($db_status['Questions']) ?> 회
                    </div>
                </div>
                <div style="margin-bottom: 5px;">
                    <span style="font-size: 13px; color: var(--dim);">최적화 필요 공간 (DATA_FREE)</span>
                    <div style="font-size: 22px; font-weight: bold; color: var(--rose); margin-top: 5px;" class="font-mono">
                        <?= number_format($summary['free'], 2) ?> MB
                    </div>
                </div>
            </div>
        </div>
        <div class="btn-container-outer">
            <a href="upbit_event.php" class="btn-move">이벤트 스케줄러 라이브 모니터링 &rarr;</a>
        </div>
    </div>
</div>

<script>
/* 실시간 시계 */
function updateClock() {
    const now = new Date();
    document.getElementById('clock').innerText = now.toLocaleTimeString('ko-KR', { hour12: false });
}
setInterval(updateClock, 1000);
updateClock();

/* 숫자 카운트업 */
function countUp() {
    document.querySelectorAll('.counter').forEach(el => {
        const target = parseInt(el.getAttribute('data-target'));
        let count = 0;
        const speed = target / 30;
        const timer = setInterval(() => {
            count += speed;
            if (count >= target) {
                el.innerText = target;
                clearInterval(timer);
            } else {
                el.innerText = Math.floor(count);
            }
        }, 30);
    });
}
document.addEventListener('DOMContentLoaded', countUp);
</script>

</body>

<?php require_once '/home/www/GNU/_PAGE/tail.php'; ?>