|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isPasswordProtected() { |
|
|
|
|
|
const pwd = window.__ENV__ && window.__ENV__.PASSWORD; |
|
|
|
|
|
|
|
|
return typeof pwd === 'string' && pwd.length === 64 && !/^0+$/.test(pwd); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isPasswordRequired() { |
|
|
return !isPasswordProtected(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function ensurePasswordProtection() { |
|
|
if (isPasswordRequired()) { |
|
|
showPasswordModal(); |
|
|
throw new Error('Password protection is required'); |
|
|
} |
|
|
if (isPasswordProtected() && !isPasswordVerified()) { |
|
|
showPasswordModal(); |
|
|
throw new Error('Password verification required'); |
|
|
} |
|
|
return true; |
|
|
} |
|
|
|
|
|
window.isPasswordProtected = isPasswordProtected; |
|
|
window.isPasswordRequired = isPasswordRequired; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function verifyPassword(password) { |
|
|
try { |
|
|
const correctHash = window.__ENV__?.PASSWORD; |
|
|
if (!correctHash) return false; |
|
|
|
|
|
const inputHash = await sha256(password); |
|
|
const isValid = inputHash === correctHash; |
|
|
|
|
|
if (isValid) { |
|
|
localStorage.setItem(PASSWORD_CONFIG.localStorageKey, JSON.stringify({ |
|
|
verified: true, |
|
|
timestamp: Date.now(), |
|
|
passwordHash: correctHash |
|
|
})); |
|
|
} |
|
|
return isValid; |
|
|
} catch (error) { |
|
|
console.error('验证密码时出错:', error); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function isPasswordVerified() { |
|
|
try { |
|
|
if (!isPasswordProtected()) return true; |
|
|
|
|
|
const stored = localStorage.getItem(PASSWORD_CONFIG.localStorageKey); |
|
|
if (!stored) return false; |
|
|
|
|
|
const { timestamp, passwordHash } = JSON.parse(stored); |
|
|
const currentHash = window.__ENV__?.PASSWORD; |
|
|
|
|
|
return timestamp && passwordHash === currentHash && |
|
|
Date.now() - timestamp < PASSWORD_CONFIG.verificationTTL; |
|
|
} catch (error) { |
|
|
console.error('检查密码验证状态时出错:', error); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
window.isPasswordProtected = isPasswordProtected; |
|
|
window.isPasswordRequired = isPasswordRequired; |
|
|
window.isPasswordVerified = isPasswordVerified; |
|
|
window.verifyPassword = verifyPassword; |
|
|
window.ensurePasswordProtection = ensurePasswordProtection; |
|
|
|
|
|
|
|
|
async function sha256(message) { |
|
|
if (window.crypto && crypto.subtle && crypto.subtle.digest) { |
|
|
const msgBuffer = new TextEncoder().encode(message); |
|
|
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); |
|
|
const hashArray = Array.from(new Uint8Array(hashBuffer)); |
|
|
return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); |
|
|
} |
|
|
|
|
|
if (typeof window._jsSha256 === 'function') { |
|
|
return window._jsSha256(message); |
|
|
} |
|
|
throw new Error('No SHA-256 implementation available.'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function showPasswordModal() { |
|
|
const passwordModal = document.getElementById('passwordModal'); |
|
|
if (passwordModal) { |
|
|
|
|
|
document.getElementById('doubanArea').classList.add('hidden'); |
|
|
document.getElementById('passwordCancelBtn').classList.add('hidden'); |
|
|
|
|
|
|
|
|
if (isPasswordRequired()) { |
|
|
|
|
|
const title = passwordModal.querySelector('h2'); |
|
|
const description = passwordModal.querySelector('p'); |
|
|
if (title) title.textContent = '需要设置密码'; |
|
|
if (description) description.textContent = '请先在部署平台设置 PASSWORD 环境变量来保护您的实例'; |
|
|
|
|
|
|
|
|
const form = passwordModal.querySelector('form'); |
|
|
const errorMsg = document.getElementById('passwordError'); |
|
|
if (form) form.style.display = 'none'; |
|
|
if (errorMsg) { |
|
|
errorMsg.textContent = '为确保安全,必须设置 PASSWORD 环境变量才能使用本服务,请联系管理员进行配置'; |
|
|
errorMsg.classList.remove('hidden'); |
|
|
errorMsg.className = 'text-red-500 mt-2 font-medium'; |
|
|
} |
|
|
} else { |
|
|
|
|
|
const title = passwordModal.querySelector('h2'); |
|
|
const description = passwordModal.querySelector('p'); |
|
|
if (title) title.textContent = '访问验证'; |
|
|
if (description) description.textContent = '请输入密码继续访问'; |
|
|
|
|
|
const form = passwordModal.querySelector('form'); |
|
|
if (form) form.style.display = 'block'; |
|
|
} |
|
|
|
|
|
passwordModal.style.display = 'flex'; |
|
|
|
|
|
|
|
|
if (!isPasswordRequired()) { |
|
|
|
|
|
setTimeout(() => { |
|
|
const passwordInput = document.getElementById('passwordInput'); |
|
|
if (passwordInput) { |
|
|
passwordInput.focus(); |
|
|
} |
|
|
}, 100); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function hidePasswordModal() { |
|
|
const passwordModal = document.getElementById('passwordModal'); |
|
|
if (passwordModal) { |
|
|
|
|
|
hidePasswordError(); |
|
|
|
|
|
|
|
|
const passwordInput = document.getElementById('passwordInput'); |
|
|
if (passwordInput) passwordInput.value = ''; |
|
|
|
|
|
passwordModal.style.display = 'none'; |
|
|
|
|
|
|
|
|
if (localStorage.getItem('doubanEnabled') === 'true') { |
|
|
document.getElementById('doubanArea').classList.remove('hidden'); |
|
|
initDouban(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function showPasswordError() { |
|
|
const errorElement = document.getElementById('passwordError'); |
|
|
if (errorElement) { |
|
|
errorElement.classList.remove('hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function hidePasswordError() { |
|
|
const errorElement = document.getElementById('passwordError'); |
|
|
if (errorElement) { |
|
|
errorElement.classList.add('hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function handlePasswordSubmit() { |
|
|
const passwordInput = document.getElementById('passwordInput'); |
|
|
const password = passwordInput ? passwordInput.value.trim() : ''; |
|
|
if (await verifyPassword(password)) { |
|
|
hidePasswordModal(); |
|
|
|
|
|
|
|
|
document.dispatchEvent(new CustomEvent('passwordVerified')); |
|
|
} else { |
|
|
showPasswordError(); |
|
|
if (passwordInput) { |
|
|
passwordInput.value = ''; |
|
|
passwordInput.focus(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function initPasswordProtection() { |
|
|
|
|
|
if (isPasswordRequired()) { |
|
|
showPasswordModal(); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
if (isPasswordProtected() && !isPasswordVerified()) { |
|
|
showPasswordModal(); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function () { |
|
|
initPasswordProtection(); |
|
|
}); |