403Webshell
Server IP : 158.247.231.215  /  Your IP : 216.73.216.159
Web Server : Apache/2.4.41 (Ubuntu)
System : Linux CTMS 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
User : www-data ( 33)
PHP Version : 8.0.30
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /mnt/blockstorage/ctms/wp-content/themes/zakra/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /mnt/blockstorage/ctms/wp-content/themes/zakra/page-apk-detail.php
<?php
/**
 * Template Name: APK Detail
 * Template Post Type: page
 */

if ( ! is_user_logged_in() ) {
	wp_redirect( wp_login_url( get_permalink() ) );
	exit;
}

global $wpdb;

$content_id = isset( $_GET['id'] ) ? max( 0, intval( $_GET['id'] ) ) : 0;

if ( ! $content_id ) {
	wp_redirect( home_url( '/apk-down/' ) );
	exit;
}

// Content
$content = $wpdb->get_row(
	$wpdb->prepare( "SELECT * FROM vr_contents WHERE id = %d AND status = 'active'", $content_id )
);

if ( ! $content ) {
	wp_redirect( home_url( '/apk-down/' ) );
	exit;
}

// Versions
$versions = $wpdb->get_results(
	$wpdb->prepare(
		"SELECT * FROM vr_content_versions WHERE content_id = %d ORDER BY created_at DESC",
		$content_id
	)
);

// Images
$images = $wpdb->get_results(
	$wpdb->prepare(
		"SELECT * FROM vr_content_images WHERE content_id = %d ORDER BY display_order ASC",
		$content_id
	)
);

$thumbnail_url = '';
$gallery_urls  = array();
foreach ( $images as $img ) {
	$url = 'https://training.contentsda.kr/vr-content-uploads/' . $img->image_path;
	if ( 'thumbnail' === $img->image_type && ! $thumbnail_url ) {
		$thumbnail_url = $url;
	} else {
		$gallery_urls[] = $url;
	}
}
if ( ! $thumbnail_url && $gallery_urls ) {
	$thumbnail_url = array_shift( $gallery_urls );
}

// Current user's download requests for this content
$current_user_id = get_current_user_id();
$requests        = $wpdb->get_results(
	$wpdb->prepare(
		"SELECT r.*, v.version_number FROM vr_download_requests r
		 INNER JOIN vr_content_versions v ON r.version_id = v.id
		 WHERE r.user_id = %d AND r.content_id = %d
		 ORDER BY r.created_at DESC",
		$current_user_id,
		$content_id
	)
);

// Build requests map: [version_id][device_type] = latest request
$req_map = array();
foreach ( $requests as $req ) {
	$key = $req->version_id . '_' . $req->device_type;
	if ( ! isset( $req_map[ $key ] ) ) {
		$req_map[ $key ] = $req;
	}
}

// Generate JWT for JavaScript API calls
$secret_key = defined( 'JWT_AUTH_SECRET_KEY' ) ? JWT_AUTH_SECRET_KEY : '';
$now        = time();
$jwt_header  = rtrim( strtr( base64_encode( wp_json_encode( array( 'typ' => 'JWT', 'alg' => 'HS256' ) ) ), '+/', '-_' ), '=' );
$jwt_payload = rtrim(
	strtr(
		base64_encode(
			wp_json_encode(
				array(
					'iss'  => get_bloginfo( 'url' ),
					'iat'  => $now,
					'nbf'  => $now,
					'exp'  => $now + ( 7 * DAY_IN_SECONDS ),
					'data' => array( 'user' => array( 'id' => $current_user_id ) ),
				)
			)
		),
		'+/',
		'-_'
	),
	'='
);
$jwt_sig   = rtrim( strtr( base64_encode( hash_hmac( 'sha256', "$jwt_header.$jwt_payload", $secret_key, true ) ), '+/', '-_' ), '=' );
$jwt_token = "$jwt_header.$jwt_payload.$jwt_sig";

// Platform stats
$total_users    = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->users}" );
$total_contents = (int) $wpdb->get_var( "SELECT COUNT(*) FROM vr_contents WHERE status = 'active'" );

// Latest version for default selection
$latest_version = $versions ? $versions[0] : null;

$list_page_url = home_url( '/apk-down/' );

// Remove default .zak-container/.zak-row so hero can span full viewport width.
remove_action( 'zakra_action_before_content', 'zakra_content_start', 20 );
remove_action( 'zakra_action_after_content', 'zakra_content_end', 10 );

get_header();
?>

<style>
.apkd-hero {
	background: #7B3FBE;
	padding: 36px 20px;
	text-align: center;
}
.apkd-hero h1 { font-size: 26px; font-weight: 700; margin: 0; color: #fff; }

.apkd-breadcrumb {
	max-width: 1200px;
	margin: 16px auto 0;
	padding: 0 20px;
	font-size: 13px;
	color: #888;
}
.apkd-breadcrumb a { color: #7B3FBE; text-decoration: none; }
.apkd-breadcrumb a:hover { text-decoration: underline; }

.apkd-wrap {
	max-width: 1200px;
	margin: 30px auto 50px;
	padding: 0 20px;
	display: grid;
	grid-template-columns: 1fr 360px;
	gap: 30px;
	align-items: start;
}
@media (max-width: 900px) {
	.apkd-wrap { grid-template-columns: 1fr; }
}

/* ── Left column ── */
.apkd-left {}
.apkd-tag {
	display: inline-block;
	background: #d4edda;
	color: #1b5e20;
	font-size: 12px;
	font-weight: 700;
	padding: 4px 12px;
	border-radius: 20px;
	margin-bottom: 14px;
}
.apkd-content-title {
	font-size: 24px;
	font-weight: 800;
	margin: 0 0 16px;
	color: #1a1a1a;
}
.apkd-img-wrap {
	position: relative;
	border-radius: 16px;
	overflow: hidden;
	margin-bottom: 20px;
	background: #f0e8ff;
	min-height: 240px;
	display: flex;
	align-items: center;
	justify-content: center;
}
.apkd-img-wrap img {
	width: 100%;
	display: block;
	border-radius: 16px;
}
.apkd-img-placeholder { font-size: 80px; }
.apkd-stats {
	display: flex;
	gap: 12px;
	margin-bottom: 20px;
}
.apkd-stat {
	background: rgba(123,63,190,.08);
	border-radius: 10px;
	padding: 12px 20px;
	text-align: center;
	min-width: 100px;
}
.apkd-stat-num { font-size: 22px; font-weight: 800; color: #7B3FBE; }
.apkd-stat-label { font-size: 12px; color: #666; margin-top: 2px; }
.apkd-desc {
	font-size: 15px;
	color: #444;
	line-height: 1.7;
	white-space: pre-line;
}

/* ── Right column: APK card ── */
.apkd-card {
	background: #fff;
	border-radius: 14px;
	box-shadow: 0 4px 20px rgba(0,0,0,.1);
	overflow: hidden;
	position: sticky;
	top: 80px;
}
.apkd-card-header {
	background: #7B3FBE;
	color: #fff;
	padding: 16px 20px;
	display: flex;
	justify-content: space-between;
	align-items: center;
}
.apkd-card-header h3 { margin: 0; font-size: 16px; font-weight: 700; }
.apkd-version-badge {
	background: rgba(255,255,255,.25);
	border-radius: 20px;
	padding: 3px 12px;
	font-size: 13px;
	font-weight: 600;
}
.apkd-card-body { padding: 20px; }

.apkd-select-label {
	font-size: 13px;
	font-weight: 600;
	color: #555;
	margin-bottom: 8px;
}
.apkd-version-select {
	width: 100%;
	padding: 10px 14px;
	border: 2px solid #e0e0e0;
	border-radius: 8px;
	font-size: 14px;
	color: #1a1a1a;
	outline: none;
	cursor: pointer;
	margin-bottom: 16px;
	transition: border-color .2s;
}
.apkd-version-select:focus { border-color: #7B3FBE; }

.apkd-device-group { display: flex; gap: 0; margin-bottom: 16px; border-radius: 8px; overflow: hidden; border: 2px solid #7B3FBE; }
.apkd-device-btn {
	flex: 1;
	padding: 10px;
	border: none;
	background: #fff;
	color: #7B3FBE;
	font-weight: 700;
	font-size: 14px;
	cursor: pointer;
	transition: background .2s, color .2s;
}
.apkd-device-btn.active { background: #7B3FBE; color: #fff; }
.apkd-device-btn:first-child { border-right: 1px solid #7B3FBE; }

.apkd-dl-btn {
	width: 100%;
	padding: 13px;
	border: none;
	border-radius: 8px;
	font-size: 15px;
	font-weight: 700;
	cursor: pointer;
	transition: background .2s, opacity .2s;
	margin-bottom: 16px;
}
.apkd-dl-btn.action-request  { background: #00897B; color: #fff; }
.apkd-dl-btn.action-download { background: #2E7D32; color: #fff; }
.apkd-dl-btn.action-pending  { background: #EF6C00; color: #fff; cursor: not-allowed; }
.apkd-dl-btn.action-done     { background: #546E7A; color: #fff; }
.apkd-dl-btn:disabled { opacity: .7; cursor: not-allowed; }

.apkd-status-box {
	padding: 10px 14px;
	border-radius: 8px;
	font-size: 13px;
	margin-bottom: 16px;
	display: none;
}
.apkd-status-box.show { display: block; }
.apkd-status-pending  { background: #FFF3E0; color: #E65100; border: 1px solid #FFCC80; }
.apkd-status-approved { background: #E8F5E9; color: #1B5E20; border: 1px solid #A5D6A7; }
.apkd-status-done     { background: #ECEFF1; color: #37474F; border: 1px solid #B0BEC5; }
.apkd-status-rejected { background: #FFEBEE; color: #B71C1C; border: 1px solid #FFCDD2; }

.apkd-divider { border: none; border-top: 1px solid #eee; margin: 16px 0; }

.apkd-section-title { font-size: 13px; font-weight: 700; color: #555; margin-bottom: 10px; }

.apkd-avail-list { list-style: none; margin: 0 0 16px; padding: 0; }
.apkd-avail-list li {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 7px 0;
	border-bottom: 1px solid #f0f0f0;
	font-size: 13px;
}
.apkd-avail-list li:last-child { border-bottom: none; }
.apkd-avail-dot { width: 8px; height: 8px; border-radius: 50%; background: #7B3FBE; margin-right: 8px; flex-shrink: 0; }

/* Version history accordion */
.apkd-history {}
.apkd-history-item { border: 1px solid #eee; border-radius: 8px; margin-bottom: 8px; overflow: hidden; }
.apkd-history-toggle {
	width: 100%;
	text-align: left;
	padding: 12px 14px;
	background: #fafafa;
	border: none;
	cursor: pointer;
	display: flex;
	justify-content: space-between;
	align-items: center;
	font-size: 13px;
	font-weight: 700;
	color: #333;
}
.apkd-history-toggle:hover { background: #f0e8ff; }
.apkd-history-toggle .arrow { transition: transform .2s; }
.apkd-history-toggle.open .arrow { transform: rotate(180deg); }
.apkd-history-body { display: none; padding: 10px 14px; background: #fff; }
.apkd-history-body.open { display: block; }
.apkd-dl-row {
	display: flex;
	justify-content: space-between;
	align-items: center;
	padding: 7px 0;
	border-bottom: 1px solid #f5f5f5;
	font-size: 13px;
}
.apkd-dl-row:last-child { border-bottom: none; }
.apkd-dl-row-label { display: flex; align-items: center; gap: 8px; color: #444; }
.apkd-dl-row-label .device-icon { font-size: 16px; }
.apkd-history-dl-btn {
	padding: 5px 12px;
	border-radius: 6px;
	border: none;
	font-size: 12px;
	font-weight: 700;
	cursor: pointer;
	transition: background .2s;
}
.apkd-history-dl-btn.req  { background: #7B3FBE; color: #fff; }
.apkd-history-dl-btn.dl   { background: #2E7D32; color: #fff; }
.apkd-history-dl-btn.wait { background: #EF6C00; color: #fff; cursor: not-allowed; }
.apkd-history-dl-btn.done { background: #90A4AE; color: #fff; cursor: not-allowed; }
.apkd-history-dl-btn:disabled { opacity: .7; }
.apkd-no-apk { font-size: 12px; color: #aaa; font-style: italic; }

/* Contents info section */
.apkd-info-section {
	max-width: 1200px;
	margin: 0 auto 50px;
	padding: 0 20px;
}
.apkd-info-box {
	background: #fff;
	border-radius: 14px;
	box-shadow: 0 2px 10px rgba(0,0,0,.07);
	padding: 30px;
}
.apkd-info-box h2 { font-size: 20px; font-weight: 700; margin: 0 0 16px; color: #1a1a1a; }
.apkd-info-body { font-size: 15px; color: #444; line-height: 1.8; white-space: pre-line; }
</style>

<div class="apkd-hero">
	<h1>Learn with Nuguna VR World</h1>
</div>

<div class="apkd-breadcrumb">
	<a href="<?php echo esc_url( $list_page_url ); ?>">APK Down</a> &rsaquo; <?php echo esc_html( $content->title ); ?>
</div>

<div class="apkd-wrap">

	<!-- ── Left: Content Info ── -->
	<div class="apkd-left">
		<span class="apkd-tag">누구나 VR 교육 콘텐츠</span>
		<h1 class="apkd-content-title"><?php echo esc_html( $content->title ); ?></h1>

		<div class="apkd-img-wrap">
			<?php if ( $thumbnail_url ) : ?>
				<img src="<?php echo esc_url( $thumbnail_url ); ?>" alt="<?php echo esc_attr( $content->title ); ?>" />
			<?php else : ?>
				<span class="apkd-img-placeholder">📱</span>
			<?php endif; ?>
		</div>

		<div class="apkd-stats">
			<div class="apkd-stat">
				<div class="apkd-stat-num"><?php echo number_format( $total_users ); ?>+</div>
				<div class="apkd-stat-label">사용자</div>
			</div>
			<div class="apkd-stat">
				<div class="apkd-stat-num"><?php echo $total_contents; ?>+</div>
				<div class="apkd-stat-label">콘텐츠</div>
			</div>
			<?php if ( $versions ) : ?>
			<div class="apkd-stat">
				<div class="apkd-stat-num"><?php echo count( $versions ); ?></div>
				<div class="apkd-stat-label">버전</div>
			</div>
			<?php endif; ?>
		</div>

		<div class="apkd-desc"><?php echo esc_html( $content->description ); ?></div>
	</div>

	<!-- ── Right: APK Download Card ── -->
	<div class="apkd-right">
		<div class="apkd-card">
			<div class="apkd-card-header">
				<h3>APK Download</h3>
				<span class="apkd-version-badge" id="selected-version-badge">
					<?php echo $latest_version ? 'v' . esc_html( $latest_version->version_number ) : '-'; ?>
				</span>
			</div>

			<div class="apkd-card-body">

				<?php if ( $versions ) : ?>
					<div class="apkd-select-label">Select Version</div>
					<select class="apkd-version-select" id="version-select" onchange="onVersionChange()">
						<?php foreach ( $versions as $ver ) : ?>
							<option value="<?php echo intval( $ver->id ); ?>"
								data-version="<?php echo esc_attr( $ver->version_number ); ?>"
								data-has-pico="<?php echo $ver->pico_apk_path ? '1' : '0'; ?>"
								data-has-quest="<?php echo $ver->quest_apk_path ? '1' : '0'; ?>">
								v<?php echo esc_html( $ver->version_number ); ?>
								<?php if ( $ver->release_notes ) : ?>
									– <?php echo esc_html( mb_substr( $ver->release_notes, 0, 30 ) ); ?>
								<?php endif; ?>
							</option>
						<?php endforeach; ?>
					</select>
				<?php else : ?>
					<p style="color:#999;font-size:14px;margin-bottom:16px;">등록된 버전이 없습니다.</p>
				<?php endif; ?>

				<div class="apkd-device-group">
					<button class="apkd-device-btn active" id="btn-pico" onclick="selectDevice('pico')">Pico</button>
					<button class="apkd-device-btn" id="btn-quest" onclick="selectDevice('quest')">Quest</button>
				</div>

				<div class="apkd-status-box" id="status-box"></div>

				<button
					class="apkd-dl-btn action-request"
					id="main-dl-btn"
					onclick="handleDownload()"
					<?php echo ! $versions ? 'disabled' : ''; ?>
				>
					<?php echo $versions ? 'Download Free APK' : '버전 없음'; ?>
				</button>

				<hr class="apkd-divider" />

				<!-- Available Versions list -->
				<?php if ( $versions ) : ?>
					<div class="apkd-section-title">Available Versions</div>
					<ul class="apkd-avail-list">
						<?php foreach ( $versions as $ver ) : ?>
							<li>
								<span style="display:flex;align-items:center;">
									<span class="apkd-avail-dot"></span>
									v<?php echo esc_html( $ver->version_number ); ?>
								</span>
								<span style="color:#aaa;font-size:11px;"><?php echo esc_html( substr( $ver->created_at, 0, 10 ) ); ?></span>
							</li>
						<?php endforeach; ?>
					</ul>

					<hr class="apkd-divider" />

					<!-- Version History -->
					<div class="apkd-section-title">Version History</div>
					<div class="apkd-history">
						<?php foreach ( $versions as $ver ) : ?>
							<?php
							$pico_req  = isset( $req_map[ $ver->id . '_pico' ] ) ? $req_map[ $ver->id . '_pico' ] : null;
							$quest_req = isset( $req_map[ $ver->id . '_quest' ] ) ? $req_map[ $ver->id . '_quest' ] : null;
							?>
							<div class="apkd-history-item">
								<button
									class="apkd-history-toggle"
									onclick="toggleHistory(this)"
									aria-expanded="false"
								>
									<span>v<?php echo esc_html( $ver->version_number ); ?></span>
									<span class="arrow">▼</span>
								</button>
								<div class="apkd-history-body">
									<!-- Pico row -->
									<div class="apkd-dl-row">
										<span class="apkd-dl-row-label">
											<span class="device-icon">🥽</span> Pico
										</span>
										<?php if ( $ver->pico_apk_path ) : ?>
											<?php
											$btn_class = 'req';
											$btn_label = '요청하기';
											$btn_extra = '';
											$btn_id    = 'hist-' . $ver->id . '-pico';
											if ( $pico_req ) {
												if ( 'approved' === $pico_req->status && ! $pico_req->is_downloaded ) {
													$btn_class = 'dl';
													$btn_label = '다운로드';
												} elseif ( 'pending' === $pico_req->status ) {
													$btn_class = 'wait';
													$btn_label = '대기중';
													$btn_extra = 'disabled';
												} elseif ( 'approved' === $pico_req->status && $pico_req->is_downloaded ) {
													$btn_class = 'done';
													$btn_label = '완료';
													$btn_extra = 'disabled';
												}
											}
											?>
											<button
												id="<?php echo esc_attr( $btn_id ); ?>"
												class="apkd-history-dl-btn <?php echo esc_attr( $btn_class ); ?>"
												data-version-id="<?php echo intval( $ver->id ); ?>"
												data-device="pico"
												data-request-id="<?php echo $pico_req ? intval( $pico_req->id ) : '0'; ?>"
												onclick="handleHistoryDownload(this)"
												<?php echo $btn_extra; ?>
											><?php echo esc_html( $btn_label ); ?></button>
										<?php else : ?>
											<span class="apkd-no-apk">미제공</span>
										<?php endif; ?>
									</div>
									<!-- Quest row -->
									<div class="apkd-dl-row">
										<span class="apkd-dl-row-label">
											<span class="device-icon">🎮</span> Quest
										</span>
										<?php if ( $ver->quest_apk_path ) : ?>
											<?php
											$btn_class = 'req';
											$btn_label = '요청하기';
											$btn_extra = '';
											$btn_id    = 'hist-' . $ver->id . '-quest';
											if ( $quest_req ) {
												if ( 'approved' === $quest_req->status && ! $quest_req->is_downloaded ) {
													$btn_class = 'dl';
													$btn_label = '다운로드';
												} elseif ( 'pending' === $quest_req->status ) {
													$btn_class = 'wait';
													$btn_label = '대기중';
													$btn_extra = 'disabled';
												} elseif ( 'approved' === $quest_req->status && $quest_req->is_downloaded ) {
													$btn_class = 'done';
													$btn_label = '완료';
													$btn_extra = 'disabled';
												}
											}
											?>
											<button
												id="<?php echo esc_attr( $btn_id ); ?>"
												class="apkd-history-dl-btn <?php echo esc_attr( $btn_class ); ?>"
												data-version-id="<?php echo intval( $ver->id ); ?>"
												data-device="quest"
												data-request-id="<?php echo $quest_req ? intval( $quest_req->id ) : '0'; ?>"
												onclick="handleHistoryDownload(this)"
												<?php echo $btn_extra; ?>
											><?php echo esc_html( $btn_label ); ?></button>
										<?php else : ?>
											<span class="apkd-no-apk">미제공</span>
										<?php endif; ?>
									</div>
								</div>
							</div>
						<?php endforeach; ?>
					</div>
				<?php endif; ?>

			</div><!-- /card-body -->
		</div><!-- /card -->
	</div><!-- /right -->

</div><!-- /wrap -->

<!-- Contents Info -->
<div class="apkd-info-section">
	<div class="apkd-info-box">
		<h2>Contents Info</h2>
		<div class="apkd-info-body"><?php echo esc_html( $content->description ); ?></div>
	</div>
</div>

<script>
const API_BASE     = '<?php echo esc_js( home_url( '/api/custom-api' ) ); ?>';
const JWT_TOKEN    = '<?php echo esc_js( $jwt_token ); ?>';
const USER_ID      = <?php echo intval( $current_user_id ); ?>;
const CONTENT_ID   = <?php echo intval( $content_id ); ?>;

let selectedDevice = 'pico';

// Map of [versionId + '_' + device] => request data from PHP
const requestMap = <?php echo wp_json_encode( $req_map ); ?>;

function getSelectedVersion() {
	const sel = document.getElementById('version-select');
	if (!sel) return null;
	const opt = sel.options[sel.selectedIndex];
	return {
		id:       parseInt(sel.value),
		number:   opt.dataset.version,
		hasPico:  opt.dataset.hasPico === '1',
		hasQuest: opt.dataset.hasQuest === '1',
	};
}

function onVersionChange() {
	const ver = getSelectedVersion();
	if (!ver) return;
	document.getElementById('selected-version-badge').textContent = 'v' + ver.number;
	updateMainBtn();
}

function selectDevice(device) {
	selectedDevice = device;
	document.getElementById('btn-pico').classList.toggle('active', device === 'pico');
	document.getElementById('btn-quest').classList.toggle('active', device === 'quest');
	updateMainBtn();
}

function updateMainBtn() {
	const ver = getSelectedVersion();
	if (!ver) return;

	const btn       = document.getElementById('main-dl-btn');
	const statusBox = document.getElementById('status-box');
	const key       = ver.id + '_' + selectedDevice;
	const req       = requestMap[key];

	const hasApk = (selectedDevice === 'pico' ? ver.hasPico : ver.hasQuest);
	statusBox.className = 'apkd-status-box';
	statusBox.textContent = '';

	if (!hasApk) {
		btn.textContent = selectedDevice.charAt(0).toUpperCase() + selectedDevice.slice(1) + ' APK 미제공';
		btn.className = 'apkd-dl-btn action-done';
		btn.disabled = true;
		return;
	}

	btn.disabled = false;

	if (!req) {
		btn.textContent = 'Download Free APK';
		btn.className = 'apkd-dl-btn action-request';
	} else if (req.status === 'pending') {
		btn.textContent = '승인 대기중';
		btn.className = 'apkd-dl-btn action-pending';
		btn.disabled = true;
		showStatus('pending', '다운로드 요청이 접수되었습니다. 관리자 승인 후 다운로드 가능합니다.');
	} else if (req.status === 'approved' && !req.is_downloaded) {
		btn.textContent = '다운로드 ↓';
		btn.className = 'apkd-dl-btn action-download';
		showStatus('approved', '다운로드가 승인되었습니다!');
	} else if (req.status === 'approved' && req.is_downloaded) {
		btn.textContent = '다운로드 완료';
		btn.className = 'apkd-dl-btn action-done';
		showStatus('done', '이미 다운로드하셨습니다. 새 요청을 하시려면 버튼을 클릭하세요.');
		btn.disabled = false;
	} else if (req.status === 'rejected') {
		btn.textContent = '다시 요청하기';
		btn.className = 'apkd-dl-btn action-request';
		showStatus('rejected', '요청이 반려되었습니다. 다시 요청할 수 있습니다.');
	}
}

function showStatus(type, msg) {
	const box = document.getElementById('status-box');
	box.textContent = msg;
	box.className = 'apkd-status-box show apkd-status-' + type;
}

async function handleDownload() {
	const ver = getSelectedVersion();
	if (!ver) return;

	const key = ver.id + '_' + selectedDevice;
	const req = requestMap[key];

	if (req && req.status === 'approved' && !req.is_downloaded) {
		await executeDownload(req.id, ver.number);
	} else {
		await requestDownload(ver.id, selectedDevice);
	}
}

async function requestDownload(versionId, device) {
	const btn = document.getElementById('main-dl-btn');
	btn.disabled = true;
	btn.textContent = '요청 중...';

	try {
		const fd = new FormData();
		fd.append('user_id', USER_ID);
		fd.append('content_id', CONTENT_ID);
		fd.append('version_id', versionId);
		fd.append('device_type', device);

		const resp = await fetch(API_BASE + '/vr-download-request.php', {
			method: 'POST',
			headers: { 'Authorization': 'Bearer ' + JWT_TOKEN },
			body: fd,
		});
		const data = await resp.json();

		if (data.status === 'success') {
			const key = versionId + '_' + device;
			requestMap[key] = {
				id:           data.data.request_id,
				status:       'pending',
				is_downloaded: false,
				version_id:   versionId,
				device_type:  device,
			};
			showStatus('pending', '다운로드 요청이 접수되었습니다. 관리자 승인 후 다운로드 가능합니다.');
			btn.textContent = '승인 대기중';
			btn.className   = 'apkd-dl-btn action-pending';
			btn.disabled    = true;
		} else {
			alert(data.message || '요청에 실패했습니다.');
			btn.disabled = false;
			updateMainBtn();
		}
	} catch (e) {
		alert('오류가 발생했습니다: ' + e.message);
		btn.disabled = false;
		updateMainBtn();
	}
}

async function executeDownload(requestId, versionNumber) {
	const btn = document.getElementById('main-dl-btn');
	btn.disabled = true;
	btn.textContent = '다운로드 중...';

	try {
		const url = API_BASE + '/vr-download-execute.php?request_id=' + requestId + '&user_id=' + USER_ID;
		const resp = await fetch(url, {
			headers: { 'Authorization': 'Bearer ' + JWT_TOKEN },
		});

		if (!resp.ok) {
			const err = await resp.json().catch(() => ({ message: '다운로드 실패' }));
			throw new Error(err.message);
		}

		const blob = await resp.blob();
		const blobUrl = URL.createObjectURL(blob);
		const a = document.createElement('a');
		a.href = blobUrl;
		a.download = 'NugunaVR_' + versionNumber + '_' + selectedDevice + '.apk';
		document.body.appendChild(a);
		a.click();
		document.body.removeChild(a);
		URL.revokeObjectURL(blobUrl);

		// Update state
		const key = document.getElementById('version-select').value + '_' + selectedDevice;
		if (requestMap[key]) requestMap[key].is_downloaded = true;
		showStatus('done', '다운로드가 완료되었습니다!');
		btn.textContent = '다운로드 완료';
		btn.className   = 'apkd-dl-btn action-done';
		btn.disabled    = false;
	} catch (e) {
		alert('다운로드 오류: ' + e.message);
		btn.disabled = false;
		updateMainBtn();
	}
}

async function handleHistoryDownload(btn) {
	const versionId  = parseInt(btn.dataset.versionId);
	const device     = btn.dataset.device;
	const requestId  = parseInt(btn.dataset.requestId);
	const key        = versionId + '_' + device;
	const req        = requestMap[key];

	if (req && req.status === 'approved' && !req.is_downloaded) {
		btn.disabled = true;
		btn.textContent = '다운로드 중...';
		try {
			const url = API_BASE + '/vr-download-execute.php?request_id=' + req.id + '&user_id=' + USER_ID;
			const resp = await fetch(url, { headers: { 'Authorization': 'Bearer ' + JWT_TOKEN } });
			if (!resp.ok) {
				const err = await resp.json().catch(() => ({ message: '실패' }));
				throw new Error(err.message);
			}
			const blob   = await resp.blob();
			const blobUrl = URL.createObjectURL(blob);
			const a = document.createElement('a');
			a.href = blobUrl;
			a.download = 'NugunaVR_' + device + '.apk';
			document.body.appendChild(a);
			a.click();
			document.body.removeChild(a);
			URL.revokeObjectURL(blobUrl);
			requestMap[key].is_downloaded = true;
			btn.className = 'apkd-history-dl-btn done';
			btn.textContent = '완료';
		} catch (e) {
			alert('오류: ' + e.message);
			btn.disabled = false;
		}
	} else {
		// Request download
		btn.disabled = true;
		btn.textContent = '요청 중...';
		try {
			const fd = new FormData();
			fd.append('user_id', USER_ID);
			fd.append('content_id', CONTENT_ID);
			fd.append('version_id', versionId);
			fd.append('device_type', device);
			const resp = await fetch(API_BASE + '/vr-download-request.php', {
				method: 'POST',
				headers: { 'Authorization': 'Bearer ' + JWT_TOKEN },
				body: fd,
			});
			const data = await resp.json();
			if (data.status === 'success') {
				requestMap[key] = {
					id: data.data.request_id,
					status: 'pending',
					is_downloaded: false,
				};
				btn.id = 'hist-' + versionId + '-' + device;
				btn.dataset.requestId = data.data.request_id;
				btn.className = 'apkd-history-dl-btn wait';
				btn.textContent = '대기중';
			} else {
				alert(data.message || '요청 실패');
				btn.disabled = false;
				btn.textContent = '요청하기';
			}
		} catch (e) {
			alert('오류: ' + e.message);
			btn.disabled = false;
			btn.textContent = '요청하기';
		}
	}
}

function toggleHistory(toggleBtn) {
	const body = toggleBtn.nextElementSibling;
	const isOpen = body.classList.contains('open');
	body.classList.toggle('open', !isOpen);
	toggleBtn.classList.toggle('open', !isOpen);
	toggleBtn.setAttribute('aria-expanded', !isOpen);
}

// Init
document.addEventListener('DOMContentLoaded', () => {
	updateMainBtn();
	// Open first history item by default
	const firstToggle = document.querySelector('.apkd-history-toggle');
	if (firstToggle) toggleHistory(firstToggle);
});
</script>

<?php
// Close #zak-content opened by zakra_main_start in header.php
echo '</div><!-- /#zak-content -->';
get_footer();

Youez - 2016 - github.com/yon3zu
LinuXploit