Add PIN lock screen and block search engines
DECK and HST frontends: - Add 4-digit PIN lock screen (default: 1234) - PIN stored in sessionStorage (persists during browser session) - Keypad with keyboard support - Add meta robots noindex/nofollow tags - Created robots.txt to disallow all crawlers Security note: This is client-side only, not cryptographically secure, but sufficient to prevent casual access and search engine indexing. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>deck</title>
|
||||
<meta name="description" content="DECK Tag Management System">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="googlebot" content="noindex, nofollow">
|
||||
|
||||
<!--
|
||||
DECK FRONTEND v4.6 - EventBus decoupling
|
||||
@@ -618,10 +620,88 @@ body {
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* 9. LOCK SCREEN
|
||||
* ----------------------------------------------------------------------------- */
|
||||
.lock-screen {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--bg-primary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
.lock-screen.hidden { display: none; }
|
||||
.lock-logo { font-size: 2rem; font-weight: 700; color: var(--accent); margin-bottom: 2rem; letter-spacing: 0.1em; }
|
||||
.lock-dots { display: flex; gap: 12px; margin-bottom: 2rem; }
|
||||
.lock-dot {
|
||||
width: 14px; height: 14px;
|
||||
border-radius: 50%;
|
||||
background: var(--border-color);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.lock-dot.filled { background: var(--accent); }
|
||||
.lock-dot.error { background: #ff4444; animation: shake 0.3s ease; }
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-4px); }
|
||||
75% { transform: translateX(4px); }
|
||||
}
|
||||
.lock-keypad {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 70px);
|
||||
gap: 12px;
|
||||
}
|
||||
.lock-key {
|
||||
width: 70px; height: 70px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--border-color);
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.lock-key:hover { border-color: var(--accent); background: var(--bg-hover); }
|
||||
.lock-key:active { transform: scale(0.95); }
|
||||
.lock-key.empty { visibility: hidden; }
|
||||
.lock-key.backspace { font-size: 1.2rem; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- LOCK SCREEN -->
|
||||
<div id="lock-screen" class="lock-screen">
|
||||
<div class="lock-logo">DECK</div>
|
||||
<div class="lock-dots">
|
||||
<div class="lock-dot"></div>
|
||||
<div class="lock-dot"></div>
|
||||
<div class="lock-dot"></div>
|
||||
<div class="lock-dot"></div>
|
||||
</div>
|
||||
<div class="lock-keypad">
|
||||
<button class="lock-key" data-key="1">1</button>
|
||||
<button class="lock-key" data-key="2">2</button>
|
||||
<button class="lock-key" data-key="3">3</button>
|
||||
<button class="lock-key" data-key="4">4</button>
|
||||
<button class="lock-key" data-key="5">5</button>
|
||||
<button class="lock-key" data-key="6">6</button>
|
||||
<button class="lock-key" data-key="7">7</button>
|
||||
<button class="lock-key" data-key="8">8</button>
|
||||
<button class="lock-key" data-key="9">9</button>
|
||||
<button class="lock-key empty"></button>
|
||||
<button class="lock-key" data-key="0">0</button>
|
||||
<button class="lock-key backspace" data-key="back">⌫</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════════════════════════════════════
|
||||
HTML STRUCTURE
|
||||
══════════════════════════════════════════════════════════════════════════ -->
|
||||
@@ -730,6 +810,91 @@ body {
|
||||
* EventBus pattern for module decoupling
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 0. LOCK SCREEN
|
||||
// =============================================================================
|
||||
const Lock = {
|
||||
PIN: "1234", // PIN code - can be changed
|
||||
entered: "",
|
||||
|
||||
init() {
|
||||
if (sessionStorage.getItem("deck_unlocked") === "true") {
|
||||
this.unlock();
|
||||
return true;
|
||||
}
|
||||
this.bind();
|
||||
return false;
|
||||
},
|
||||
|
||||
bind() {
|
||||
const keypad = document.querySelector(".lock-keypad");
|
||||
if (!keypad) return;
|
||||
|
||||
keypad.addEventListener("click", (e) => {
|
||||
const key = e.target.closest(".lock-key");
|
||||
if (!key) return;
|
||||
|
||||
const value = key.dataset.key;
|
||||
if (value === "back") {
|
||||
this.entered = this.entered.slice(0, -1);
|
||||
} else if (value && this.entered.length < 4) {
|
||||
this.entered += value;
|
||||
}
|
||||
|
||||
this.updateDots();
|
||||
|
||||
if (this.entered.length === 4) {
|
||||
setTimeout(() => this.check(), 150);
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard support
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (document.getElementById("lock-screen")?.classList.contains("hidden")) return;
|
||||
|
||||
if (e.key >= "0" && e.key <= "9" && this.entered.length < 4) {
|
||||
this.entered += e.key;
|
||||
this.updateDots();
|
||||
if (this.entered.length === 4) setTimeout(() => this.check(), 150);
|
||||
} else if (e.key === "Backspace") {
|
||||
this.entered = this.entered.slice(0, -1);
|
||||
this.updateDots();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
updateDots() {
|
||||
const dots = document.querySelectorAll(".lock-dot");
|
||||
dots.forEach((dot, i) => {
|
||||
dot.classList.toggle("filled", i < this.entered.length);
|
||||
dot.classList.remove("error");
|
||||
});
|
||||
},
|
||||
|
||||
check() {
|
||||
if (this.entered === this.PIN) {
|
||||
sessionStorage.setItem("deck_unlocked", "true");
|
||||
this.unlock();
|
||||
} else {
|
||||
this.showError();
|
||||
}
|
||||
},
|
||||
|
||||
showError() {
|
||||
const dots = document.querySelectorAll(".lock-dot");
|
||||
dots.forEach(dot => dot.classList.add("error"));
|
||||
setTimeout(() => {
|
||||
this.entered = "";
|
||||
this.updateDots();
|
||||
}, 500);
|
||||
},
|
||||
|
||||
unlock() {
|
||||
const screen = document.getElementById("lock-screen");
|
||||
if (screen) screen.classList.add("hidden");
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// 1. CONFIG
|
||||
// =============================================================================
|
||||
@@ -1678,7 +1843,22 @@ const App = {
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => App.init());
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const unlocked = Lock.init();
|
||||
if (unlocked) {
|
||||
App.init();
|
||||
} else {
|
||||
// Watch for unlock to init app
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
const screen = document.getElementById("lock-screen");
|
||||
if (screen?.classList.contains("hidden")) {
|
||||
observer.disconnect();
|
||||
App.init();
|
||||
}
|
||||
});
|
||||
observer.observe(document.getElementById("lock-screen"), { attributes: true });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user