c8c7b69ae0
- Add CSRF token per session, validated on all state-changing requests (POST/PUT/DELETE) - Upgrade password hashing from PBKDF2 to bcrypt; auto-upgrade old hashes on login - Add audit_log table tracking all security events (login, export, delete, etc.) - Add /reauth endpoint requiring master password before export - Client-side: re-auth modal before export, X-CSRF-Token header on mutations
200 lines
12 KiB
HTML
200 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>🔐 Vault</title>
|
||
<link rel="stylesheet" href="css/style.css">
|
||
</head>
|
||
<body>
|
||
<div class="toast-container" id="toastContainer"></div>
|
||
<div class="idle-warning" id="idleWarning">
|
||
<h3>⏰ Auto-lock</h3>
|
||
<p>Vault locks in <span id="idleCountdown">30</span>s</p>
|
||
<button class="btn btn-sm" onclick="resetIdle()">Stay Unlocked</button>
|
||
</div>
|
||
|
||
<div class="edit-modal" id="editModal">
|
||
<div class="edit-box">
|
||
<h3>✏️ Edit Entry</h3>
|
||
<label>Website / App</label><input type="text" id="editSite" placeholder="example.com">
|
||
<label style="margin-top:.6rem">Username / Email</label><input type="text" id="editUsername" placeholder="user@example.com">
|
||
<label style="margin-top:.6rem">Password</label>
|
||
<div style="position:relative">
|
||
<input type="password" id="editPassword" placeholder="Password" style="width:100%;padding-right:40px">
|
||
<button type="button" onclick="toggleEditPassword()" style="position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--text2);cursor:pointer;font-size:.9rem">👁️</button>
|
||
</div>
|
||
<label style="margin-top:.6rem">Folder</label>
|
||
<select id="editFolder" style="width:100%;margin-bottom:.4rem"></select>
|
||
<input type="hidden" id="editId">
|
||
<div style="display:flex;gap:.5rem;margin-top:1rem">
|
||
<button class="btn" onclick="saveEdit()">💾 Save</button>
|
||
<button class="btn btn-outline" onclick="closeEdit()">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Add Password Modal -->
|
||
<div class="modal-overlay" id="addModal">
|
||
<div class="modal-box">
|
||
<h3>➕ New Password</h3>
|
||
<label>Website / App</label><input type="text" id="addSite" placeholder="example.com">
|
||
<label style="margin-top:.6rem">Username / Email (optional)</label><input type="text" id="addUsername" placeholder="user@example.com">
|
||
<label style="margin-top:.6rem">Password</label>
|
||
<div style="position:relative">
|
||
<input type="password" id="addPassword" placeholder="Password" style="width:100%;padding-right:40px" oninput="checkAddStrength()">
|
||
<button type="button" onclick="openGen()" style="position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--text2);cursor:pointer;font-size:1.1rem;line-height:1" title="Generate">🎲</button>
|
||
</div>
|
||
<div class="strength-bar s0" id="addStrengthBar" style="margin-top:0.2rem;"></div>
|
||
<label style="margin-top:.6rem">Folder</label>
|
||
<select id="addFolder" style="width:100%;margin-bottom:.4rem"></select>
|
||
<div style="display:flex;gap:.5rem;margin-top:1rem">
|
||
<button class="btn" id="addEntryBtn" onclick="addEntry()">💾 Save</button>
|
||
<button class="btn btn-outline" onclick="closeAdd()">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Floating action button -->
|
||
<button class="fab" onclick="openAdd()" title="Add password">+</button>
|
||
<div class="vault">
|
||
<h1>🔐 Vault <span>XAMPP</span>
|
||
<button class="btn btn-outline btn-xs" onclick="toggleTheme()" style="margin-left:auto">🌓</button>
|
||
</h1>
|
||
|
||
<!-- Auth Section -->
|
||
<div id="authSection" class="auth-section">
|
||
<div class="auth-tabs">
|
||
<button class="auth-tab active" onclick="switchTab('login')">Login</button>
|
||
<button class="auth-tab" onclick="switchTab('register')">Register</button>
|
||
</div>
|
||
<div id="loginForm">
|
||
<div class="input-group">
|
||
<input type="text" id="loginUsername" placeholder="Username" autocomplete="off">
|
||
<input type="password" id="loginPassword" placeholder="Master Password" autocomplete="off">
|
||
<button class="btn" id="loginBtn" onclick="login()">🔓 Unlock</button>
|
||
</div>
|
||
</div>
|
||
<div id="registerForm" class="hidden">
|
||
<div class="input-group">
|
||
<input type="text" id="regUsername" placeholder="Username (min 3)" autocomplete="off">
|
||
<input type="password" id="regPassword" placeholder="Password (min 8)" autocomplete="off">
|
||
<button class="btn" id="registerBtn" onclick="register()">✨ Create</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Vault Section -->
|
||
<div id="vaultSection" class="hidden">
|
||
<!-- Top bar -->
|
||
<div style="display:flex;justify-content:space-between;margin-bottom:.5rem;align-items:center;flex-wrap:wrap;gap:.4rem">
|
||
<span class="status-badge" id="connectionStatus">🟢 Connected</span>
|
||
<span id="entryCount" style="color:var(--text2);font-size:.8rem"></span>
|
||
<div style="display:flex;gap:.4rem;align-items:center">
|
||
<span id="currentUser" style="color:var(--text2);font-size:.8rem"></span>
|
||
<div class="settings-dropdown">
|
||
<button class="btn btn-outline btn-sm" id="settingsBtn" onclick="toggleSettings()">⚙️</button>
|
||
<div class="settings-menu hidden" id="settingsMenu">
|
||
<div class="settings-item">
|
||
<span>Auto-lock</span>
|
||
<select id="autoLockTimer" onchange="setAutoLock()">
|
||
<option value="0">No lock</option>
|
||
<option value="1">1 min</option>
|
||
<option value="5" selected>5 min</option>
|
||
<option value="15">15 min</option>
|
||
<option value="30">30 min</option>
|
||
<option value="60">1 hour</option>
|
||
</select>
|
||
</div>
|
||
<div class="settings-item" onclick="toggleSound();event.stopPropagation();">
|
||
<span>Sound</span>
|
||
<div class="toggle-switch" id="soundToggleSwitch"></div>
|
||
</div>
|
||
<div class="settings-item" onclick="toggleTheme();event.stopPropagation();">
|
||
<span>Theme</span>
|
||
<div class="toggle-switch" id="themeToggleSwitch"></div>
|
||
</div>
|
||
<div class="settings-item" onclick="toggleViewBtn();event.stopPropagation();">
|
||
<span>👁️ View password</span>
|
||
<div class="toggle-switch active" id="showViewBtnToggle"></div>
|
||
</div>
|
||
<div class="settings-item" onclick="toggleShowEmail();event.stopPropagation();">
|
||
<span>📧 Show email</span>
|
||
<div class="toggle-switch active" id="showEmailToggle"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button class="btn btn-outline btn-sm" onclick="doLogout()">🔒 Lock</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Add Entry Form -->
|
||
<!-- <form onsubmit="return false" class="input-group" autocomplete="off"> -->
|
||
<!-- <input type="text" id="siteInput" placeholder="Website *" autocomplete="off"> -->
|
||
<!-- <input type="text" id="usernameInput" placeholder="Username (optional)" autocomplete="off"> -->
|
||
<!-- <div style="flex:1;min-width:130px;position:relative"> -->
|
||
<!-- <input type="password" id="passwordInput" placeholder="Password *" autocomplete="new-password" style="width:100%;padding-right:40px" oninput="checkStrength()"> -->
|
||
<!-- <button type="button" onclick="openGen()" style="position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--text2);cursor:pointer;font-size:1.1rem;line-height:1" title="Generate password">🎲</button> -->
|
||
<!-- </div> -->
|
||
<!-- <div class="strength-bar s0" id="strengthBar" style="flex-basis:100%;margin-top:-0.3rem;margin-bottom:0.3rem"></div> -->
|
||
<!-- <select id="addFolderSelect"></select> -->
|
||
<!-- <button type="button" class="btn" id="addBtn" onclick="addEntry()">➕ Add</button> -->
|
||
<!-- </form> -->
|
||
|
||
<!-- Toolbar with search + trash + views -->
|
||
<div class="toolbar">
|
||
<div class="search-box">
|
||
<input type="text" id="searchInput" placeholder="🔍 Search..." oninput="searchEntries()" autocomplete="off">
|
||
<button type="button" class="clear-search-btn" id="clearSearchBtn" onclick="clearSearch()" title="Clear search">✕</button>
|
||
</div>
|
||
<div style="display:flex;gap:.4rem;align-items:center;flex-wrap:wrap">
|
||
<button class="btn btn-outline btn-sm" id="trashBtn" onclick="toggleTrash()" title="Trash">🗑️</button>
|
||
<button class="btn btn-outline btn-sm" id="selectBtn" onclick="toggleSelectMode()" title="Batch select">☐ Select</button>
|
||
<div class="view-toggle" id="viewToggle">
|
||
<button class="view-btn active" data-view="grid">🟫 Grid</button>
|
||
<button class="view-btn" data-view="compact">📝 Compact</button>
|
||
<button class="view-btn" data-view="table">📊 Table</button>
|
||
<button class="view-btn" data-view="list">📋 List</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="folders-bar" id="foldersBar"></div>
|
||
<div id="trashActions" class="hidden" style="margin-bottom:.5rem;text-align:right;">
|
||
<button class="empty-trash-btn" onclick="emptyTrash()">🗑️ Empty Trash</button>
|
||
</div>
|
||
<div id="entriesContainer" class="grid-view">
|
||
<div style="text-align:center;color:var(--text2);padding:2rem;grid-column:1/-1">📭 No entries</div>
|
||
</div>
|
||
<div style="margin-top:.6rem;text-align:right">
|
||
<button class="btn btn-outline btn-sm" onclick="showExportModal()">📤 Export</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Generator Modal -->
|
||
<div id="genModal" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);display:none;justify-content:center;align-items:center;z-index:2000">
|
||
<div style="background:var(--bg2);border-radius:1.5rem;padding:1.5rem;min-width:320px;max-width:90%">
|
||
<h3 style="margin-bottom:.8rem;color:var(--text)">🎲 Generator</h3>
|
||
<div style="background:var(--input);padding:.8rem;border-radius:1rem;font-family:monospace;text-align:center;color:#4ade80;margin:.6rem 0;word-break:break-all" id="genPreview">Click Generate</div>
|
||
<div style="display:flex;align-items:center;gap:.6rem;margin:.8rem 0">
|
||
<span>Length:</span>
|
||
<input type="range" id="pwdLen" min="8" max="64" value="16" oninput="onLenChange()" style="flex:1">
|
||
<span id="lenVal" style="background:var(--input);padding:.2rem .6rem;border-radius:1rem;min-width:30px;text-align:center">16</span>
|
||
</div>
|
||
<div style="display:flex;flex-wrap:wrap;gap:.5rem;margin:.5rem 0">
|
||
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useUpper" checked onchange="genPwd()"> A-Z</label>
|
||
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useLower" checked onchange="genPwd()"> a-z</label>
|
||
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useNum" checked onchange="genPwd()"> 0-9</label>
|
||
<label style="font-size:.8rem;color:var(--text2)"><input type="checkbox" id="useSym" checked onchange="genPwd()"> !@#$</label>
|
||
</div>
|
||
<div style="display:flex;gap:.4rem;margin-top:.8rem">
|
||
<button class="btn" onclick="genPwd()" style="flex:1">🔄</button>
|
||
<button class="btn" onclick="useGen()" style="flex:1">✅ Use</button>
|
||
<button class="btn btn-outline" onclick="closeGen()">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="js/app.js"></script>
|
||
</body>
|
||
</html> |