Download manage_database.html from RecapAnon/AskMiku: direct link, hf CLI and curl.
- Browser
- Download file 15.9 kB
-
https://huggingface.co/spaces/RecapAnon/AskMiku/resolve/main/manage_database.html
- Command line
-
hf download hf://spaces/RecapAnon/AskMiku/manage_database.html
-
curl -L -o manage_database.html https://huggingface.co/spaces/RecapAnon/AskMiku/resolve/main/manage_database.html
15.9 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Database Management - Ask Miku</title> | |
| <link rel="icon" type="image/svg+xml" href="favicon.svg"> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 20px; | |
| } | |
| .container { | |
| background: white; | |
| border-radius: 20px; | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); | |
| padding: 40px; | |
| max-width: 600px; | |
| width: 100%; | |
| } | |
| h1 { | |
| color: #667eea; | |
| margin-bottom: 10px; | |
| font-size: 32px; | |
| text-align: center; | |
| } | |
| .subtitle { | |
| color: #666; | |
| text-align: center; | |
| margin-bottom: 40px; | |
| font-size: 14px; | |
| } | |
| .section { | |
| margin-bottom: 30px; | |
| padding: 20px; | |
| background: #f8f9fa; | |
| border-radius: 10px; | |
| } | |
| .section h2 { | |
| color: #333; | |
| font-size: 18px; | |
| margin-bottom: 10px; | |
| } | |
| .section p { | |
| color: #666; | |
| font-size: 14px; | |
| line-height: 1.6; | |
| margin-bottom: 15px; | |
| } | |
| .button-group { | |
| display: flex; | |
| gap: 10px; | |
| flex-wrap: wrap; | |
| } | |
| button { | |
| flex: 1; | |
| min-width: 150px; | |
| padding: 12px 24px; | |
| font-size: 16px; | |
| font-weight: 600; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| } | |
| .btn-primary:hover:not(:disabled) { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); | |
| } | |
| .btn-secondary { | |
| background: #6c757d; | |
| color: white; | |
| } | |
| .btn-secondary:hover:not(:disabled) { | |
| background: #5a6268; | |
| transform: translateY(-2px); | |
| } | |
| .btn-danger { | |
| background: #dc3545; | |
| color: white; | |
| } | |
| .btn-danger:hover:not(:disabled) { | |
| background: #c82333; | |
| transform: translateY(-2px); | |
| } | |
| button:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| } | |
| .file-input-wrapper { | |
| position: relative; | |
| overflow: hidden; | |
| display: inline-block; | |
| flex: 1; | |
| min-width: 150px; | |
| } | |
| .file-input-wrapper input[type=file] { | |
| position: absolute; | |
| left: -9999px; | |
| } | |
| .status-message { | |
| margin-top: 15px; | |
| padding: 12px; | |
| border-radius: 8px; | |
| font-size: 14px; | |
| display: none; | |
| } | |
| .status-message.show { | |
| display: block; | |
| } | |
| .status-success { | |
| background: #d4edda; | |
| color: #155724; | |
| border: 1px solid #c3e6cb; | |
| } | |
| .status-error { | |
| background: #f8d7da; | |
| color: #721c24; | |
| border: 1px solid #f5c6cb; | |
| } | |
| .status-info { | |
| background: #d1ecf1; | |
| color: #0c5460; | |
| border: 1px solid #bee5eb; | |
| } | |
| .loading { | |
| display: inline-block; | |
| width: 16px; | |
| height: 16px; | |
| border: 2px solid #f3f3f3; | |
| border-top: 2px solid #667eea; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .back-link { | |
| text-align: center; | |
| margin-top: 20px; | |
| } | |
| .back-link a { | |
| color: #667eea; | |
| text-decoration: none; | |
| font-size: 14px; | |
| } | |
| .back-link a:hover { | |
| text-decoration: underline; | |
| } | |
| .divider { | |
| height: 1px; | |
| background: #dee2e6; | |
| margin: 30px 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>ποΈ Database Management</h1> | |
| <p class="subtitle">Manage your Ask Miku knowledge base</p> | |
| <!-- Import Section --> | |
| <div class="section"> | |
| <h2>π₯ Import Database</h2> | |
| <p>Load database from a previously exported JSON file. This will populate the vector database with pre-calculated embeddings for instant startup.</p> | |
| <div class="button-group"> | |
| <div class="file-input-wrapper"> | |
| <button class="btn-primary" onclick="document.getElementById('importFile').click()"> | |
| <span>π Choose File</span> | |
| </button> | |
| <input type="file" id="importFile" accept=".json" onchange="handleImport(this)"> | |
| </div> | |
| <button class="btn-secondary" id="loadDummyBtn" onclick="loadDummyData()"> | |
| <span>π Load Dummy Data</span> | |
| </button> | |
| </div> | |
| <div id="importStatus" class="status-message"></div> | |
| </div> | |
| <div class="divider"></div> | |
| <!-- Export Section --> | |
| <div class="section"> | |
| <h2>π€ Export Database</h2> | |
| <p>Export the current database state to a JSON file. This includes all vector embeddings and metadata. Use this file for backup or to speed up future initializations.</p> | |
| <div class="button-group"> | |
| <button class="btn-primary" id="exportBtn" onclick="handleExport()"> | |
| <span>πΎ Export to File</span> | |
| </button> | |
| </div> | |
| <div id="exportStatus" class="status-message"></div> | |
| </div> | |
| <div class="divider"></div> | |
| <!-- Clear Section --> | |
| <div class="section"> | |
| <h2>ποΈ Clear Database</h2> | |
| <p>Remove all data from the vector database. This action cannot be undone. You'll need to import or load dummy data again after clearing.</p> | |
| <div class="button-group"> | |
| <button class="btn-danger" id="clearBtn" onclick="handleClear()"> | |
| <span>β οΈ Clear All Data</span> | |
| </button> | |
| </div> | |
| <div id="clearStatus" class="status-message"></div> | |
| </div> | |
| <div class="back-link"> | |
| <a href="index.html">β Back to Chat</a> | |
| </div> | |
| </div> | |
| <script src="https://cdn.jsdelivr.net/npm/indexeddb-export-import@2.1.5/index.min.js"></script> | |
| <script type="module"> | |
| import { EntityDB } from "./entity-db.js"; | |
| let vectorDB = null; | |
| async function getNativeDB() { | |
| return new Promise((resolve, reject) => { | |
| const request = indexedDB.open("EntityDB", 1); | |
| request.onsuccess = () => resolve(request.result); | |
| request.onerror = () => reject(request.error); | |
| }); | |
| } | |
| async function initializeVectorDB() { | |
| try { | |
| vectorDB = new EntityDB({ | |
| vectorPath: "askmiku-memory", | |
| model: "onnx-community/harrier-oss-v1-0.6b-ONNX" | |
| }); | |
| console.log('EntityDB initialized successfully'); | |
| return true; | |
| } catch (error) { | |
| console.error('Error initializing EntityDB:', error); | |
| showStatusMessage('importStatus', 'Error initializing database: ' + error.message, 'error'); | |
| return false; | |
| } | |
| } | |
| function showStatusMessage(elementId, message, type) { | |
| const statusEl = document.getElementById(elementId); | |
| statusEl.textContent = message; | |
| statusEl.className = `status-message show status-${type}`; | |
| if (type === 'success' || type === 'info') { | |
| setTimeout(() => { | |
| statusEl.classList.remove('show'); | |
| }, 5000); | |
| } | |
| } | |
| window.handleImport = async function(input) { | |
| if (!input.files || !input.files[0]) return; | |
| const file = input.files[0]; | |
| const importBtn = input.closest('.file-input-wrapper').querySelector('button'); | |
| const originalText = importBtn.innerHTML; | |
| try { | |
| importBtn.disabled = true; | |
| importBtn.innerHTML = '<span class="loading"></span> <span>Importing...</span>'; | |
| showStatusMessage('importStatus', 'Reading file...', 'info'); | |
| if (!vectorDB) { | |
| await initializeVectorDB(); | |
| } | |
| const jsonString = await file.text(); | |
| try { | |
| JSON.parse(jsonString); | |
| } catch (e) { | |
| throw new Error('Invalid JSON file: ' + e.message); | |
| } | |
| showStatusMessage('importStatus', 'Importing data to database...', 'info'); | |
| // Import to database | |
| const nativeDB = await getNativeDB(); | |
| await new Promise((resolve, reject) => { | |
| importFromJsonString(nativeDB, jsonString, (err) => { | |
| if (err) reject(err); | |
| else resolve(); | |
| }); | |
| }); | |
| localStorage.setItem('dbLoadedFromJson', 'true'); | |
| showStatusMessage('importStatus', `β Successfully imported database from ${file.name}`, 'success'); | |
| } catch (error) { | |
| console.error('Import error:', error); | |
| showStatusMessage('importStatus', `β Import failed: ${error.message}`, 'error'); | |
| } finally { | |
| importBtn.disabled = false; | |
| importBtn.innerHTML = originalText; | |
| input.value = ''; | |
| } | |
| }; | |
| window.loadDummyData = async function() { | |
| const btn = document.getElementById('loadDummyBtn'); | |
| const originalText = btn.innerHTML; | |
| try { | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="loading"></span> <span>Loading...</span>'; | |
| showStatusMessage('importStatus', 'Loading dummy data from file...', 'info'); | |
| if (!vectorDB) { | |
| await initializeVectorDB(); | |
| } | |
| const response = await fetch('data/dummy.json'); | |
| if (!response.ok) { | |
| throw new Error(`Failed to load dummy.json: ${response.statusText}`); | |
| } | |
| const data = await response.json(); | |
| if (!data.knowledgeBase || !Array.isArray(data.knowledgeBase)) { | |
| throw new Error('Invalid dummy.json format: missing knowledgeBase array'); | |
| } | |
| showStatusMessage('importStatus', `Inserting ${data.knowledgeBase.length} entries...`, 'info'); | |
| for (const entry of data.knowledgeBase) { | |
| await vectorDB.insert({ | |
| text: entry.text, | |
| category: entry.category, | |
| topic: entry.topic | |
| }); | |
| } | |
| localStorage.setItem('dbLoadedFromJson', 'true'); | |
| showStatusMessage('importStatus', `β Successfully loaded ${data.knowledgeBase.length} dummy entries`, 'success'); | |
| } catch (error) { | |
| console.error('Load dummy data error:', error); | |
| showStatusMessage('importStatus', `β Failed to load dummy data: ${error.message}`, 'error'); | |
| } finally { | |
| btn.disabled = false; | |
| btn.innerHTML = originalText; | |
| } | |
| }; | |
| window.handleExport = async function() { | |
| const btn = document.getElementById('exportBtn'); | |
| const originalText = btn.innerHTML; | |
| try { | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="loading"></span> <span>Exporting...</span>'; | |
| showStatusMessage('exportStatus', 'Exporting database...', 'info'); | |
| if (!vectorDB) { | |
| await initializeVectorDB(); | |
| } | |
| // Export database | |
| const nativeDB = await getNativeDB(); | |
| const jsonString = await new Promise((resolve, reject) => { | |
| exportToJsonString(nativeDB, (err, data) => { | |
| if (err) reject(err); | |
| else resolve(data); | |
| }); | |
| }); | |
| // Create download | |
| const blob = new Blob([jsonString], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `askmiku-export-${new Date().toISOString().split('T')[0]}.json`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| showStatusMessage('exportStatus', 'β Database exported successfully', 'success'); | |
| } catch (error) { | |
| console.error('Export error:', error); | |
| showStatusMessage('exportStatus', `β Export failed: ${error.message}`, 'error'); | |
| } finally { | |
| btn.disabled = false; | |
| btn.innerHTML = originalText; | |
| } | |
| }; | |
| window.handleClear = async function() { | |
| if (!confirm('Are you sure you want to clear all database data? This cannot be undone.')) { | |
| return; | |
| } | |
| const btn = document.getElementById('clearBtn'); | |
| const originalText = btn.innerHTML; | |
| try { | |
| btn.disabled = true; | |
| btn.innerHTML = '<span class="loading"></span> <span>Clearing...</span>'; | |
| showStatusMessage('clearStatus', 'Clearing database...', 'info'); | |
| if (!vectorDB) { | |
| await initializeVectorDB(); | |
| } | |
| const nativeDB = await getNativeDB(); | |
| await new Promise((resolve, reject) => { | |
| clearDatabase(nativeDB, (err) => { | |
| if (err) reject(err); | |
| else resolve(); | |
| }); | |
| }); | |
| localStorage.removeItem('dbLoadedFromJson'); | |
| showStatusMessage('clearStatus', 'β Database cleared successfully', 'success'); | |
| } catch (error) { | |
| console.error('Clear error:', error); | |
| showStatusMessage('clearStatus', `β Clear failed: ${error.message}`, 'error'); | |
| } finally { | |
| btn.disabled = false; | |
| btn.innerHTML = originalText; | |
| } | |
| }; | |
| initializeVectorDB(); | |
| </script> | |
| </body> | |
| </html> |