Initial private import
This commit is contained in:
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
const HA_BASE = 'https://ha.chaegeon.com';
|
||||||
|
const HA_LONG_LIVED_TOKEN = 'REDACTED_HA_TOKEN';
|
||||||
|
const POKE_ENTITY_ID = 'counter.kog_jjireugi';
|
||||||
|
|
||||||
|
function ha_get_entity_state($entity_id) {
|
||||||
|
$url = rtrim(HA_BASE, '/') . '/api/states/' . urlencode($entity_id);
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt_array($ch, [
|
||||||
|
CURLOPT_HTTPHEADER => [
|
||||||
|
'Authorization: Bearer ' . HA_LONG_LIVED_TOKEN,
|
||||||
|
'Content-Type: application/json'
|
||||||
|
],
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_TIMEOUT => 5,
|
||||||
|
]);
|
||||||
|
$res = curl_exec($ch);
|
||||||
|
$errno = curl_errno($ch);
|
||||||
|
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($errno !== 0 || $status < 200 || $status >= 300) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = json_decode($res, true);
|
||||||
|
// HA는 상태값을 문자열로 줍니다. 숫자면 (string)"12" 형태.
|
||||||
|
if (!is_array($json) || !isset($json['state'])) return null;
|
||||||
|
|
||||||
|
// 숫자로 캐스팅 시도 (실패해도 문자열 반환 가능)
|
||||||
|
$stateRaw = $json['state'];
|
||||||
|
if (is_numeric($stateRaw)) {
|
||||||
|
return (int)$stateRaw;
|
||||||
|
}
|
||||||
|
return $stateRaw; // 숫자가 아니면 원본 문자열
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
if ($password === '0010') {
|
||||||
|
echo json_encode(['result' => 'ok']);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['result' => 'fail']);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
if (isset($_GET['failMessages'])) {
|
||||||
|
echo json_encode([
|
||||||
|
'failMessages' => [
|
||||||
|
'비밀번호가 틀립니다.',
|
||||||
|
]
|
||||||
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['pokeCount'])) {
|
||||||
|
// HA 엔티티 상태 읽기
|
||||||
|
$count = ha_get_entity_state(POKE_ENTITY_ID);
|
||||||
|
|
||||||
|
if ($count === null) {
|
||||||
|
http_response_code(502);
|
||||||
|
echo json_encode([
|
||||||
|
'ok' => false,
|
||||||
|
'error' => 'failed_to_fetch_from_ha'
|
||||||
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'ok' => true,
|
||||||
|
'count' => $count
|
||||||
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode(['error' => 'not_found']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['error' => 'method_not_allowed']);
|
||||||
|
?>
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
.find-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
background: #f2f2f7;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
user-select: none;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-container {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: fit-content;
|
||||||
|
height: auto;
|
||||||
|
background: #f2f2f7;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#findMyShortcut input[type="password"] {
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
width: 200px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 500;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
appearance: none;
|
||||||
|
outline: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#findMyShortcut input[type="password"]:focus {
|
||||||
|
border-color: #999;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#findMyShortcut button {
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background-color: #007aff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
margin-left: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#findMyShortcut button:active {
|
||||||
|
background-color: #0051c7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 90px;
|
||||||
|
margin: 0 12px;
|
||||||
|
border-radius: 16px;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconContainer img {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
|
||||||
|
background: #fff;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconContainer:active img {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconLabel {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #000;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1; /* 1줄만 표시하고 자름 (2줄 자르려면 2로 변경) */
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
#failMsg {
|
||||||
|
font-size: 13px;
|
||||||
|
color: red;
|
||||||
|
min-height: 18px;
|
||||||
|
margin-top: 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,27 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" id="find-my">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="a" x1="64.06" x2="55.94" y1="-2.02" y2="122.02" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#dddddb"></stop>
|
||||||
|
<stop offset="1" stop-color="#ccccca"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="b" x1="60.28" x2="60.28" y1="60" y2="-2.94" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset=".18" stop-color="#057eee"></stop>
|
||||||
|
<stop offset=".2" stop-color="#057eef" stop-opacity=".96"></stop>
|
||||||
|
<stop offset=".46" stop-color="#037cf6" stop-opacity=".56"></stop>
|
||||||
|
<stop offset=".67" stop-color="#017bfb" stop-opacity=".26"></stop>
|
||||||
|
<stop offset=".83" stop-color="#007afe" stop-opacity=".07"></stop>
|
||||||
|
<stop offset=".9" stop-color="#007AFF" stop-opacity="0"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<rect width="120" height="120" fill="url(#a)" rx="26"></rect>
|
||||||
|
<circle cx="60.28" cy="60" r="52" fill="#38d165"></circle>
|
||||||
|
<circle cx="60.28" cy="60" r="32" fill="#46d75e"></circle>
|
||||||
|
<circle cx="60.28" cy="60" r="32" fill="#46d75e"></circle>
|
||||||
|
<circle cx="60.28" cy="60" r="14" fill="#efeff4"></circle>
|
||||||
|
<path fill="#58dc73" d="M60.28 93a33 33 0 1 1 33-33 33 33 0 0 1-33 33Zm0-64a31 31 0 1 0 31 31 31 31 0 0 0-31-31Z"></path>
|
||||||
|
<path fill="url(#b)" d="m60.28 60-26-45a52 52 0 0 1 52 0Z"></path>
|
||||||
|
<circle cx="60.28" cy="60" r="14" fill="#efeff4"></circle>
|
||||||
|
<circle cx="60.28" cy="60" r="9" fill="#007AFF"></circle>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,12 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<!-- 라운드 사각형 테두리 (아이콘 베이스) -->
|
||||||
|
<rect x="2" y="2" width="60" height="60" rx="16" fill="none" stroke="#8E8E93" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- 전원 버튼 (iOS 스타일) -->
|
||||||
|
<path d="M32 14v12" stroke="#34C759" stroke-width="4" stroke-linecap="round"/>
|
||||||
|
<path d="M22 20a16 16 0 1 0 20 0" stroke="#34C759" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
|
||||||
|
<!-- 네트워크 파동 -->
|
||||||
|
<path d="M18 42c4-4 8-6 14-6s10 2 14 6" stroke="#007AFF" stroke-width="2.5" fill="none" stroke-linecap="round"/>
|
||||||
|
<path d="M22 48c3-3 6-4 10-4s7 1 10 4" stroke="#007AFF" stroke-width="2" fill="none" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 778 B |
+317
@@ -0,0 +1,317 @@
|
|||||||
|
const shortcut = document.getElementById('findMyShortcut');
|
||||||
|
let failIndex = 0;
|
||||||
|
|
||||||
|
const iconItems = [
|
||||||
|
{
|
||||||
|
id: 'findmydevice',
|
||||||
|
img: '/custom/launcher/img/find-my.svg',
|
||||||
|
label: '나의 찾기',
|
||||||
|
onclick: () => {
|
||||||
|
requireAccess(() => location.href = '/findmydevice');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'wakeonlan',
|
||||||
|
img: '/custom/launcher/img/wakeonlan-svgrepo-com.svg',
|
||||||
|
label: 'PC 켜기',
|
||||||
|
onclick: () => {
|
||||||
|
fetch('/custom/wakeonlan/api.php?status=1')
|
||||||
|
.then(res => res.ok ? res.json() : Promise.reject())
|
||||||
|
.then(data => {
|
||||||
|
|
||||||
|
if (!data || data.result !== 'ok') {
|
||||||
|
showAlert('error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.state === 'on') {
|
||||||
|
showAlert('wakeonlan_already');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/custom/wakeonlan/api.php', { method: 'POST' })
|
||||||
|
.then(res => res.ok ? res.json() : Promise.reject())
|
||||||
|
.then(() => {
|
||||||
|
showAlert('wakeonlan_request_sent');
|
||||||
|
|
||||||
|
let attempts = 0;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
attempts++;
|
||||||
|
fetch('/custom/wakeonlan/api.php?status=1')
|
||||||
|
.then(res => res.ok ? res.json() : Promise.reject())
|
||||||
|
.then(status => {
|
||||||
|
if (status.state === 'on') {
|
||||||
|
clearInterval(interval);
|
||||||
|
showAlert('wakeonlan_success');
|
||||||
|
}
|
||||||
|
else if (attempts >= 12) {
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => clearInterval(interval));
|
||||||
|
}, 5000);
|
||||||
|
})
|
||||||
|
.catch(() => showAlert('error'));
|
||||||
|
})
|
||||||
|
.catch(() => showAlert('error'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'poke',
|
||||||
|
img: '/custom/launcher/img/R1280x0.jpg',
|
||||||
|
label: '콕 찌르기',
|
||||||
|
onclick: () => {
|
||||||
|
showAlert('poke_completed');
|
||||||
|
const alertBox = document.querySelector('#poke_completed');
|
||||||
|
const content = alertBox.querySelector('.alert-content');
|
||||||
|
|
||||||
|
content.style.minHeight = 'auto';
|
||||||
|
content.style.padding = '15px 20px 10px 20px';
|
||||||
|
content.style.justifyContent = 'flex-start';
|
||||||
|
|
||||||
|
const renderMain = (countText = "조회 중...") => {
|
||||||
|
content.innerHTML = `
|
||||||
|
<div id="poke_container" style="width:100%; display:flex; flex-direction:column; gap:6px;">
|
||||||
|
<p id="poke_title" style="text-align:center; font-weight:bold; margin-bottom:4px; color:#007AFF; font-size:1.05em;">${countText}</p>
|
||||||
|
|
||||||
|
<input type="text" id="poke_sender" placeholder="보내는 이 (미입력 시 익명)"
|
||||||
|
style="padding:8px 12px; border:1px solid #eee; border-radius:10px; outline:none; font-size:14px; background:#f9f9f9;">
|
||||||
|
|
||||||
|
<input type="text" id="poke_message" placeholder="할 말 (선택 사항)"
|
||||||
|
style="padding:8px 12px; border:1px solid #eee; border-radius:10px; outline:none; font-size:14px; background:#f9f9f9;">
|
||||||
|
|
||||||
|
<div class="button-custom-container" style="margin-top:6px; gap:6px;">
|
||||||
|
<button class="button-custom" id="btn_poke_cancel" style="background-color:#eee; color:#666; flex:1; min-width:auto;">닫기</button>
|
||||||
|
<button class="button-custom" id="btn_poke_confirm" style="flex:2; min-width:auto;">콕 찌르기</button>
|
||||||
|
</div>
|
||||||
|
<p id="poke_status" style="text-align:center; font-size:10px; color:#bbb; margin-top:2px; height:1px;"></p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.getElementById('btn_poke_cancel').onclick = () => { closeAlert('poke_completed'); };
|
||||||
|
document.getElementById('btn_poke_confirm').onclick = handlePoke;
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch('/custom/launcher/api.php?pokeCount=0')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
const n = Number(data.count) || 0;
|
||||||
|
document.getElementById('poke_title').innerHTML = `현재까지 <span style="color:#ff3b30;">${n}회</span> 찔렸습니다!`;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
document.getElementById('poke_title').innerText = "현재 횟수를 불러올 수 없음";
|
||||||
|
});
|
||||||
|
|
||||||
|
const handlePoke = () => {
|
||||||
|
const sender = document.getElementById('poke_sender').value.trim() || "익명";
|
||||||
|
const message = document.getElementById('poke_message').value.trim() || "";
|
||||||
|
const titleField = document.getElementById('poke_title');
|
||||||
|
const statusField = document.getElementById('poke_status');
|
||||||
|
const confirmBtn = document.getElementById('btn_poke_confirm');
|
||||||
|
|
||||||
|
confirmBtn.disabled = true;
|
||||||
|
statusField.innerText = "전송 중...";
|
||||||
|
|
||||||
|
const params = new URLSearchParams({ sender, message });
|
||||||
|
fetch(`https://ha.chaegeon.com/api/webhook/jczSPJyIX4iG4JTvy9HztFq91MsrIfsUErFh4DLstDcsm7RKL?${params.toString()}`, {
|
||||||
|
method: 'POST',
|
||||||
|
mode: 'no-cors'
|
||||||
|
});
|
||||||
|
|
||||||
|
fetch(`/custom/launcher/api.php?pokeCount=1&sender=${encodeURIComponent(sender)}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
const n = Number(data.count);
|
||||||
|
titleField.innerHTML = `현재까지 <span style="color:#ff3b30;">${n}회</span> 찔렸습니다!`;
|
||||||
|
statusField.innerText = "성공!";
|
||||||
|
setTimeout(() => {
|
||||||
|
confirmBtn.disabled = false;
|
||||||
|
statusField.innerText = "";
|
||||||
|
}, 300);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
statusField.innerText = "오류 발생";
|
||||||
|
confirmBtn.disabled = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
renderMain();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'car',
|
||||||
|
img: '/custom/launcher/img/car.png',
|
||||||
|
label: '니모',
|
||||||
|
onclick: () => {
|
||||||
|
requireAccess(() => location.href = 'https://seo.chaegeon.com/car/monitor.php');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function renderIcons() {
|
||||||
|
shortcut.className = 'find-container';
|
||||||
|
shortcut.innerHTML = '';
|
||||||
|
|
||||||
|
iconItems.forEach(item => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'iconContainer';
|
||||||
|
div.id = item.id;
|
||||||
|
if (item.onclick) div.addEventListener('click', item.onclick);
|
||||||
|
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.src = item.img;
|
||||||
|
img.alt = item.label;
|
||||||
|
|
||||||
|
const label = document.createElement('div');
|
||||||
|
label.className = 'iconLabel';
|
||||||
|
label.textContent = item.label;
|
||||||
|
|
||||||
|
div.appendChild(img);
|
||||||
|
div.appendChild(label);
|
||||||
|
shortcut.appendChild(div);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireAccess(nextAction) {
|
||||||
|
const access = getCookie('access');
|
||||||
|
if (access === '9KxH6236') {
|
||||||
|
nextAction && nextAction();
|
||||||
|
} else {
|
||||||
|
showPasswordForm(nextAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPasswordForm(onSuccess) {
|
||||||
|
shortcut.className = 'password-container';
|
||||||
|
shortcut.innerHTML = '';
|
||||||
|
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.id = 'settingsHeader';
|
||||||
|
header.style.display = 'flex';
|
||||||
|
header.style.alignItems = 'center';
|
||||||
|
header.style.justifyContent = 'center';
|
||||||
|
header.style.position = 'relative';
|
||||||
|
header.style.marginBottom = '15px';
|
||||||
|
header.style.padding = '12px';
|
||||||
|
header.style.fontSize = '16px';
|
||||||
|
header.style.color = '#000';
|
||||||
|
header.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
|
||||||
|
|
||||||
|
const backLink = document.createElement('a');
|
||||||
|
backLink.id = 'blogBackLink';
|
||||||
|
backLink.innerHTML = '<span class="backArrow">‹</span> 목록';
|
||||||
|
backLink.style.position = 'absolute';
|
||||||
|
backLink.style.left = '0px';
|
||||||
|
backLink.style.fontSize = '16px';
|
||||||
|
backLink.style.top = '3px';
|
||||||
|
backLink.style.color = '#007AFF';
|
||||||
|
backLink.style.textDecoration = 'none';
|
||||||
|
backLink.style.fontWeight = 'normal';
|
||||||
|
backLink.style.cursor = 'pointer';
|
||||||
|
backLink.addEventListener('click', () => renderIcons());
|
||||||
|
|
||||||
|
backLink.querySelector('.backArrow').style.cssText = `
|
||||||
|
font-size: 26px;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
margin-right: 1px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const title = document.createElement('span');
|
||||||
|
title.className = 'headerTitle';
|
||||||
|
title.textContent = '비밀번호';
|
||||||
|
title.style.fontWeight = 'bold';
|
||||||
|
|
||||||
|
header.appendChild(backLink);
|
||||||
|
header.appendChild(title);
|
||||||
|
shortcut.appendChild(header);
|
||||||
|
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.type = 'password';
|
||||||
|
input.placeholder = '비밀번호';
|
||||||
|
input.id = 'blog_secret';
|
||||||
|
input.inputMode = 'numeric';
|
||||||
|
input.pattern = '[0-9]*';
|
||||||
|
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.id = 'blog_secret_check';
|
||||||
|
button.textContent = '확인';
|
||||||
|
|
||||||
|
const msg = document.createElement('div');
|
||||||
|
msg.id = 'failMsg';
|
||||||
|
|
||||||
|
const inputRow = document.createElement('div');
|
||||||
|
inputRow.style.display = 'flex';
|
||||||
|
inputRow.style.alignItems = 'center';
|
||||||
|
inputRow.appendChild(input);
|
||||||
|
inputRow.appendChild(button);
|
||||||
|
|
||||||
|
const column = document.createElement('div');
|
||||||
|
column.style.display = 'flex';
|
||||||
|
column.style.flexDirection = 'column';
|
||||||
|
column.style.alignItems = 'center';
|
||||||
|
column.appendChild(inputRow);
|
||||||
|
|
||||||
|
shortcut.appendChild(column);
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
const password = input.value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const messageRes = await fetch('/custom/launcher/api.php?failMessages=1');
|
||||||
|
const messagesJson = await messageRes.json();
|
||||||
|
const failMessages = messagesJson.failMessages || [];
|
||||||
|
|
||||||
|
const res = await fetch('/custom/launcher/api.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: new URLSearchParams({ password })
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (data.result === 'ok') {
|
||||||
|
document.cookie = 'access=9KxH6236; path=/; max-age=21600; SameSite=Strict' + (location.protocol === 'https:' ? '; Secure' : '');
|
||||||
|
if (typeof onSuccess === 'function') {
|
||||||
|
onSuccess();
|
||||||
|
} else {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
column.appendChild(msg);
|
||||||
|
msg.textContent = failMessages[failIndex] || '비밀번호가 틀렸습니다.';
|
||||||
|
failIndex = (failIndex + 1) % Math.max(1, failMessages.length);
|
||||||
|
input.value = '';
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
button.addEventListener('click', submit);
|
||||||
|
input.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') submit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCookie(name) {
|
||||||
|
const value = `; ${document.cookie}`;
|
||||||
|
const parts = value.split(`; ${name}=`);
|
||||||
|
if (parts.length === 2) return parts.pop().split(';').shift();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
renderIcons();
|
||||||
|
|
||||||
|
const findShortcut = document.getElementById('findMyShortcut');
|
||||||
|
if (findShortcut) {
|
||||||
|
findShortcut.style.userSelect = 'none';
|
||||||
|
findShortcut.style.webkitUserSelect = 'none';
|
||||||
|
findShortcut.style.msUserSelect = 'none';
|
||||||
|
findShortcut.style.MozUserSelect = 'none';
|
||||||
|
findShortcut.style.webkitTouchCallout = 'none';
|
||||||
|
findShortcut.addEventListener('dragstart', e => e.preventDefault());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (location.pathname.split('/').filter(Boolean).length === 1) document.title = "블로그 - 채건닷컴";
|
||||||
Reference in New Issue
Block a user