Compare commits
2 Commits
03269c9430
...
14eaf89960
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14eaf89960 | ||
|
|
5714ef4877 |
103
index.css
103
index.css
@@ -311,4 +311,107 @@ footer {
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Login Overlay Styles */
|
||||||
|
.login-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: transparent;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 32px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 400px;
|
||||||
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.3);
|
||||||
|
animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-header p {
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
#captchaCanvas {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn {
|
||||||
|
background: #f3f4f6;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn:hover {
|
||||||
|
background: #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
background: rgba(239, 68, 68, 0.1);
|
||||||
|
color: var(--danger);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.2);
|
||||||
|
animation: shake 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shake {
|
||||||
|
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
transform: translateX(-5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
transform: translateX(-5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Base CSS End */
|
/* Base CSS End */
|
||||||
45
index.html
45
index.html
@@ -12,9 +12,51 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<div id="loginOverlay" class="login-overlay">
|
||||||
|
<div class="login-card">
|
||||||
|
<header class="login-header">
|
||||||
|
<div class="icon-container">
|
||||||
|
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||||
|
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h1>Login Access</h1>
|
||||||
|
<p>Please enter your credentials to continue</p>
|
||||||
|
</header>
|
||||||
|
<form id="loginForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input type="text" id="username" placeholder="Type admin" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" id="password" placeholder="Type admin" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Security Verification</label>
|
||||||
|
<div class="captcha-container">
|
||||||
|
<canvas id="captchaCanvas" width="150" height="50"></canvas>
|
||||||
|
<button type="button" id="refreshCaptcha" class="icon-btn" title="Refresh Captcha">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||||
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"></path>
|
||||||
|
<path d="M21 3v5h-5"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<input type="text" id="captchaInput" placeholder="Enter characters above" required>
|
||||||
|
</div>
|
||||||
|
<div id="loginError" class="error-message" style="display: none;"></div>
|
||||||
|
<button type="submit" class="primary-btn full-width">Sign In</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="app-background"></div>
|
<div class="app-background"></div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container" id="appContainer" style="display: none;">
|
||||||
<header class="app-header">
|
<header class="app-header">
|
||||||
<div class="icon-container">
|
<div class="icon-container">
|
||||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||||
@@ -99,6 +141,7 @@
|
|||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="login.js"></script>
|
||||||
<script src="app.js"></script>
|
<script src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
127
login.js
Normal file
127
login.js
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
// Login and Captcha Logic for btlabel
|
||||||
|
|
||||||
|
const initLogin = () => {
|
||||||
|
console.log("Login module initializing...");
|
||||||
|
const loginForm = document.getElementById('loginForm');
|
||||||
|
const loginOverlay = document.getElementById('loginOverlay');
|
||||||
|
const appContainer = document.getElementById('appContainer');
|
||||||
|
const loginError = document.getElementById('loginError');
|
||||||
|
const captchaCanvas = document.getElementById('captchaCanvas');
|
||||||
|
const refreshCaptchaBtn = document.getElementById('refreshCaptcha');
|
||||||
|
const captchaInput = document.getElementById('captchaInput');
|
||||||
|
const usernameInput = document.getElementById('username');
|
||||||
|
const passwordInput = document.getElementById('password');
|
||||||
|
|
||||||
|
if (!captchaCanvas) {
|
||||||
|
console.error("Captcha canvas not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentCaptcha = '';
|
||||||
|
|
||||||
|
// Generate random captcha string
|
||||||
|
const generateCaptcha = () => {
|
||||||
|
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draw captcha on canvas
|
||||||
|
const drawCaptcha = (text) => {
|
||||||
|
const ctx = captchaCanvas.getContext('2d');
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, captchaCanvas.width, captchaCanvas.height);
|
||||||
|
|
||||||
|
// Background noise
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
ctx.strokeStyle = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.3)`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(Math.random() * captchaCanvas.width, Math.random() * captchaCanvas.height);
|
||||||
|
ctx.lineTo(Math.random() * captchaCanvas.width, Math.random() * captchaCanvas.height);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 30; i++) {
|
||||||
|
ctx.fillStyle = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.3)`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(Math.random() * captchaCanvas.width, Math.random() * captchaCanvas.height, 1, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw text
|
||||||
|
ctx.font = 'bold 28px Arial';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
|
||||||
|
const spacing = captchaCanvas.width / (text.length + 1);
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
ctx.fillStyle = `rgb(${Math.random() * 100}, ${Math.random() * 100}, ${Math.random() * 100})`;
|
||||||
|
const x = spacing * (i + 1);
|
||||||
|
const y = captchaCanvas.height / 2 + (Math.random() * 10 - 5);
|
||||||
|
const angle = (Math.random() * 0.4) - 0.2;
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(x, y);
|
||||||
|
ctx.rotate(angle);
|
||||||
|
ctx.fillText(text[i], -12, 12);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
console.log("Captcha drawn:", text);
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshCaptcha = () => {
|
||||||
|
currentCaptcha = generateCaptcha();
|
||||||
|
drawCaptcha(currentCaptcha);
|
||||||
|
if (captchaInput) captchaInput.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initial captcha
|
||||||
|
refreshCaptcha();
|
||||||
|
|
||||||
|
if (refreshCaptchaBtn) {
|
||||||
|
refreshCaptchaBtn.addEventListener('click', refreshCaptcha);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginForm) {
|
||||||
|
loginForm.addEventListener('submit', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const username = usernameInput.value;
|
||||||
|
const password = passwordInput.value;
|
||||||
|
const captcha = captchaInput.value.toUpperCase();
|
||||||
|
|
||||||
|
loginError.style.display = 'none';
|
||||||
|
|
||||||
|
if (captcha !== currentCaptcha) {
|
||||||
|
loginError.textContent = 'Invalid security code. Please try again.';
|
||||||
|
loginError.style.display = 'block';
|
||||||
|
refreshCaptcha();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (username === 'admin' && password === 'admin') {
|
||||||
|
loginOverlay.style.opacity = '0';
|
||||||
|
setTimeout(() => {
|
||||||
|
loginOverlay.style.display = 'none';
|
||||||
|
appContainer.style.display = 'block';
|
||||||
|
if (typeof updatePreview === 'function') {
|
||||||
|
updatePreview();
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
} else {
|
||||||
|
loginError.textContent = 'Invalid username or password.';
|
||||||
|
loginError.style.display = 'block';
|
||||||
|
refreshCaptcha();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initLogin);
|
||||||
|
} else {
|
||||||
|
initLogin();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user