GNU/skin/board/daemon/write.skin.php
<?php
if (!defined('_GNUBOARD_')) exit;

// =================================================================
// 1. [구멍 뚫기] 오빠의 UPBIT DB (upbit_data) 연결
// =================================================================
include_once('/home/www/DB/db_upbit.php'); 

if (!isset($db_upbit)) {
    die("오빠! db_upbit.php 파일은 불렀는데 연결이 안 됐어! 파일 다시 확인해줘!");
}
$db_upbit = $pdo; // ★ UPBIT 전용 DB 핸들러

include_once("{$board_skin_path}/db_update.php");

// [필수] CSS 파일 로드
add_stylesheet('<link rel="stylesheet" href="'.$board_skin_url.'/style.css?v='.time().'">', 0);
add_stylesheet('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">', 0);
add_stylesheet('<link rel="stylesheet" href="'.$board_skin_url.'/style.write.css?v='.time().'">', 0);

// [변수 초기화]
$w = $_REQUEST['w'] ?? '';
$wr_id = $_REQUEST['wr_id'] ?? '';

// [값 할당] 규칙: wr_subject(데몬명), sel_event(테이블명)
$cur_daemon = $write['wr_subject'] ?? ''; 
$cur_table = $write['sel_event'] ?? '';  
$cur_ver = $write['x2_ver'] ?? '';

// 버전 자동 할당
if (!$cur_ver && $w == '') {
    $row_ver = sql_fetch(" select x2_ver from {$write_table} order by wr_id desc limit 1 ");
    $cur_ver = $row_ver['x2_ver'] ?? '';
}

// 외부 DB(upbit)에서 테이블 목록 가져오기 (sel_event용)
$db_tables = [];
if (isset($db_upbit) && $db_upbit) {
    try {
        $res_tables = $db_upbit->query("SHOW TABLES");
        if ($res_tables) {
            while ($row_table = $res_tables->fetch(PDO::FETCH_NUM)) {
                $db_tables[] = $row_table[0];
            }
        }
    } catch (Exception $e) { }
}

// [추가 기능] 이미 등록된 데몬명 확인 (중복 제외 로직)
$used_daemons = [];
// 현재 테이블에서 사용 중인 wr_subject 목록을 가져온다.
$sql_used = " select distinct wr_subject from {$write_table} ";
$res_used = sql_query($sql_used);
while ($row_u = sql_fetch_array($res_used)) {
    // 수정 모드($w='u')일 때, 내가 지금 수정하고 있는 이 글의 데몬명($cur_daemon)은 제외 목록에 넣으면 안 됨.
    if ($w == 'u' && $cur_daemon == $row_u['wr_subject']) {
        continue;
    }
    $used_daemons[] = $row_u['wr_subject'];
}

// 데몬 파일 목록 스캔 (wr_subject용)
$daemon_list = [];
$daemon_path = '/home/www/DATA/UPBIT/daemon';
if (is_dir($daemon_path) && is_readable($daemon_path)) {
    try {
        $di = new RecursiveDirectoryIterator($daemon_path, RecursiveDirectoryIterator::SKIP_DOTS);
        $it = new RecursiveIteratorIterator($di);
        foreach ($it as $file) {
            if ($file->isFile() && $file->getExtension() == 'php') {
                $fn = $file->getFilename();
                if (strpos($fn, 'daemon_') === 0) {
                    // ★ 핵심 수정: 이미 DB에 등록된 데몬명($used_daemons)에 포함되어 있다면 리스트에 넣지 않는다.
                    if (!in_array($fn, $used_daemons)) {
                        $daemon_list[] = $fn;
                    }
                }
            }
        }
        $daemon_list = array_unique($daemon_list);
        sort($daemon_list);
    } catch (Exception $e) { }
}

$row_status = sql_fetch(" SELECT @@event_scheduler as status ");
$scheduler_state = isset($row_status['status']) ? strtoupper($row_status['status']) : 'UNKNOWN';
$state_color = ($scheduler_state == 'ON') ? '#00d4ff' : '#ff2d55';
$has_data = ($w == 'u' && (trim($write['wr_content'] ?? '') || trim($write['x2_content'] ?? '')));
?>

<div id="WRITE_WRAP">
    <canvas id="space-canvas"></canvas>
    <article id="WRITE">
        <form name="fwrite" id="fwrite" action="<?php echo $action_url; ?>" onsubmit="return fwrite_submit(this);" method="post" enctype="multipart/form-data" autocomplete="off">
            <input type="hidden" name="w" value="<?php echo $w; ?>">
            <input type="hidden" name="bo_table" value="<?php echo $bo_table; ?>">
            <input type="hidden" name="wr_id" value="<?php echo $wr_id; ?>">
            <input type="hidden" name="sca" value="<?php echo $sca; ?>">

            <div class="write-header">
                <div class="write-header__title-group">
                    <h2 class="write-header__title">
                        <i class="fa-solid fa-microchip"></i> DAEMON CONTROL
                    </h2>
                    <span class="write-header__status-badge" style="color:<?php echo $state_color; ?>; border-color:<?php echo $state_color; ?>; box-shadow:0 0 10px <?php echo $state_color; ?>40;">
                        SCHEDULER : <?php echo $scheduler_state; ?>
                    </span>
                </div>
                <div class="write-header__btn-group">
                    <button type="button" class="btn-toggle-clean btn-submit--small" style="background:#0f172a; color:#fff;" onclick="history.back();">CANCEL</button>
                    <button type="submit" class="btn-submit btn-submit--small" id="btn_submit_top">SAVE</button>
                </div>
            </div>

            <table class="write-table">
                <tr>
                    <td class="td-label"><i class="fa-solid fa-sliders"></i> 설정 (Config)</td>
                    <td class="td-content">
                        <div class="form-row">
                            <div class="form-group">
                                <span class="form-label-mini" style="color:#00d4ff;">관리여부</span>
                                <label class="toggle-btn">
                                    <input type="checkbox" name="x2_run" value="1" <?php echo (isset($write['x2_run']) && $write['x2_run'] || $w=='') ? 'checked' : ''; ?>>
                                    <span class="slider"></span>
                                </label>
                            </div>
                            
                            <div class="form-divider"></div>

                            <?php if ($is_category) { ?>
                                <span class="form-label-mini">거래소.</span>
                                <select name="ca_name" required class="select-base">
                                    <option value="">거래소 선택</option>
                                    <?php echo $category_option; ?>
                                </select>
                            <?php } ?>
                            
                            <span class="form-label-mini">/&nbsp;&nbsp;&nbsp;&nbsp;종류.</span>
                            <select name="x2_ca2" class="select-base">
                                <option value="">종류 선택</option>
                                <?php
                                $opts = explode('|', $board['bo_1']);
                                foreach($opts as $o) {
                                    $o = trim($o); if(!$o) continue;
                                    echo '<option value="'.$o.'" '.($write['x2_ca2'] == $o ? 'selected' : '').'>'.$o.'</option>';
                                }
                                ?>
                            </select>
                            
                            <span class="form-label-mini">/&nbsp;&nbsp;&nbsp;&nbsp;형태.</span>
                            <select name="x2_ca3" class="select-base">
                                <option value="">형태 선택</option>
                                <?php
                                $opts = explode('|', $board['bo_2']);
                                foreach($opts as $o) {
                                    $o = trim($o); if(!$o) continue;
                                    echo '<option value="'.$o.'" '.($write['x2_ca3'] == $o ? 'selected' : '').'>'.$o.'</option>';
                                }
                                ?>
                            </select>
                            
                            <div class="form-group ml-auto">
                                <span class="form-label-mini">/&nbsp;&nbsp;&nbsp;&nbsp;VER.</span>
                                <input type="text" name="x2_ver" value="<?php echo $cur_ver; ?>" class="input-ver">
                            </div>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="td-label"><i class="fa-solid fa-robot"></i> 대상 (Target)</td>
                    <td class="td-content">
                        <div class="form-row">
                            <div class="form-group">
                                <span class="form-label-mini">데몬명.</span>
                                <span class="target-label target-label--blue">DAEMON</span>
                                <select name="wr_subject" id="wr_subject" class="select-target" required>
                                    <option value="">:: 데몬 선택 ::</option>
                                    <?php 
                                    foreach($daemon_list as $d) {
                                        $selected = ($cur_daemon == $d) ? 'selected' : '';
                                        echo '<option value="'.$d.'" '.$selected.'>'.$d.'</option>';
                                    }
                                    ?>
                                </select>
                            </div>

                            <div class="form-divider"></div>

                            <div class="form-group">
                                <span class="form-label-mini">테이블명.</span>
                                <span class="target-label target-label--orange">TABLE</span>
                                <select name="sel_event" id="sel_event" class="select-target">
                                    <option value="">:: 테이블 선택 ::</option>
                                    <?php 
                                    foreach($db_tables as $t) {
                                        $selected = ($cur_table == $t) ? 'selected' : '';
                                        echo '<option value="'.$t.'" '.$selected.'>'.$t.'</option>'; 
                                    }
                                    ?>
                                </select>
                            </div>
                            
                            <div class="form-divider"></div>

                            <div class="form-group">
                                <span class="form-label-mini">식별명.</span>
                                <input type="text" name="wr_subject_kor" value="<?php echo $write['wr_subject_kor'] ?? ''; ?>" class="input-kor" placeholder="한글 식별명">
                            </div>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td class="td-label"><i class="fa-regular fa-note-sticky"></i> 메모 (Memo)</td>
                    <td class="td-content">
                         <input type="text" name="wr_1" value="<?php echo $write['wr_1'] ?? ''; ?>" class="input-full" placeholder="간단한 메모 입력">
                    </td>
                </tr>
                <tr>
                    <td class="td-label"><i class="fa-solid fa-tags"></i> 태그 (Tag)</td>
                    <td class="td-content">
                        <input type="text" name="x2_tag" value="<?php echo $write['x2_tag'] ?? ''; ?>" class="input-full" placeholder="#TAG1 #TAG2">
                    </td>
                </tr>
            </table>

            <div class="dynamic-result-area">
                <div class="res-item"><span>[STATUS]</span> <b id="res_state">-</b></div>
                <div class="res-item"><span>[DAEMON]</span> <b id="res_daemon">-</b></div>
                <div class="res-item"><span>[TABLE]</span> <b id="res_table">-</b></div>
                <div class="res-item"><span>[EXCHANGE]</span> <b id="res_exchange">-</b></div>
                <div class="ml-auto res-item"><span>[UPDATED]</span> <b id="res_time" style="color:#fff;">-</b></div>
            </div>

            <button type="button" class="btn-toggle-clean" onclick="toggleExtra()">
                상세 파라미터 / 콘텐츠 관리 <i class="fa-solid fa-chevron-down" id="toggle_icon"></i>
            </button>
            
            <div id="extra_area" style="display:<?php echo $has_data ? 'block' : 'none'; ?>; border-top:1px solid var(--border-color);">
                <table class="write-table">
                    <tr>
                        <td class="td-label">상세 설명</td>
                        <td class="td-content--editor">
                            <?php echo $editor_html; ?>
                        </td>
                    </tr>
                    <tr>
                        <td class="td-label">추가 설정 (JSON)</td>
                        <td class="td-content">
                            <textarea name="x2_content" class="textarea-json"><?php echo get_text($write['x2_content'] ?? '', 0); ?></textarea>
                        </td>
                    </tr>
                    <tr>
                        <td class="td-label">로그 기록 (Log)</td>
                        <td class="td-content">
                            <textarea name="x2_content_2" class="textarea-json"><?php echo get_text($write['x2_content_2'] ?? '', 0); ?></textarea>
                        </td>
                    </tr>
                </table>
            </div>

            <div class="write-footer">
                <button type="submit" class="btn-submit" id="btn_submit_bottom">SAVE CONFIG</button>
            </div>
        </form>
    </article>
</div>

<script>
$(function() {
    function updateUI() {
        const isRun = $('input[name="x2_run"]').is(':checked');
        const daemonName = $('#wr_subject').val(); 
        const tableName = $('#sel_event').val(); 
        const exchange = $('select[name="ca_name"]').val();

        $('#res_state').text(isRun ? 'RUNNING' : 'STOPPED').css('color', isRun ? '#00d4ff' : '#ff2d55');
        $('#res_daemon').text(daemonName || 'NONE').css('color', daemonName ? '#00d4ff' : '#64748b');
        $('#res_table').text(tableName || 'NONE').css('color', tableName ? '#f59e0b' : '#64748b');
        $('#res_exchange').text(exchange || '-');
        $('#res_time').text(new Date().toLocaleTimeString());
    }

    $('#wr_subject, #sel_event, input[name="x2_run"], select[name="ca_name"]').on('change', updateUI);
    updateUI();

    const canvas = document.getElementById('space-canvas');
    if(canvas) {
        const ctx = canvas.getContext('2d');
        let stars = [];
        let animationFrameId;

        function init() {
            canvas.width = window.innerWidth; 
            canvas.height = window.innerHeight;
            stars = Array.from({length:100}, () => ({
                x: Math.random() * canvas.width, 
                y: Math.random() * canvas.height, 
                s: Math.random() * 1.5 + 0.5, 
                sp: Math.random() * 0.3 + 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.s, 0, Math.PI*2); 
                ctx.fill(); 
                s.y += s.sp; 
                if(s.y > canvas.height) s.y = 0; 
            });
            animationFrameId = requestAnimationFrame(draw);
        }
        init();
        draw();
        window.addEventListener('resize', () => {
            if(animationFrameId) cancelAnimationFrame(animationFrameId);
            init();
            draw();
        });
    }
});

function toggleExtra() { 
    const area = $('#extra_area');
    const icon = $('#toggle_icon');
    area.slideToggle(function(){
        if(area.is(':visible')) icon.removeClass('fa-chevron-down').addClass('fa-chevron-up');
        else icon.removeClass('fa-chevron-up').addClass('fa-chevron-down');
    });
}

function fwrite_submit(f) {
    <?php echo isset($editor_js) ? $editor_js : ''; ?> 
    
    if(!f.wr_subject.value) { 
        alert("데몬명(wr_subject)을 선택해야 합니다."); 
        return false;
    }
    
    const btnTop = document.getElementById("btn_submit_top");
    const btnBot = document.getElementById("btn_submit_bottom");
    if(btnTop) btnTop.disabled = true;
    if(btnBot) btnBot.disabled = true;

    return true;
}
</script>