Compare commits
No commits in common. "6573a01d7567b51eb62ef3de4e0a4406d1bac2a5" and "2cf31905b0eaf1cc0bbde3f6e03baf6f42c3c8af" have entirely different histories.
6573a01d75
...
2cf31905b0
@ -1,507 +0,0 @@
|
|||||||
# Implementation Plan: Migrating from LocalStorage to IndexedDB
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This document outlines the plan for migrating the WebAssembly crypto example application from using `localStorage` to `IndexedDB` for persisting encrypted key spaces. The primary motivations for this migration are:
|
|
||||||
|
|
||||||
1. Transaction capabilities for better data integrity
|
|
||||||
2. Improved performance for larger data operations
|
|
||||||
3. More structured approach to data storage
|
|
||||||
|
|
||||||
## Current Implementation
|
|
||||||
|
|
||||||
The current implementation uses localStorage with the following key functions:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// LocalStorage functions for key spaces
|
|
||||||
const STORAGE_PREFIX = 'crypto_space_';
|
|
||||||
|
|
||||||
// Save encrypted space to localStorage
|
|
||||||
function saveSpaceToStorage(spaceName, encryptedData) {
|
|
||||||
localStorage.setItem(`${STORAGE_PREFIX}${spaceName}`, encryptedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get encrypted space from localStorage
|
|
||||||
function getSpaceFromStorage(spaceName) {
|
|
||||||
return localStorage.getItem(`${STORAGE_PREFIX}${spaceName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// List all spaces in localStorage
|
|
||||||
function listSpacesFromStorage() {
|
|
||||||
const spaces = [];
|
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
|
||||||
const key = localStorage.key(i);
|
|
||||||
if (key.startsWith(STORAGE_PREFIX)) {
|
|
||||||
spaces.push(key.substring(STORAGE_PREFIX.length));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return spaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove space from localStorage
|
|
||||||
function removeSpaceFromStorage(spaceName) {
|
|
||||||
localStorage.removeItem(`${STORAGE_PREFIX}${spaceName}`);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
### 1. Database Structure
|
|
||||||
|
|
||||||
- Create a database named 'CryptoSpaceDB'
|
|
||||||
- Create an object store named 'keySpaces' with 'name' as the key path
|
|
||||||
- Add indexes for efficient querying: 'name' (unique) and 'lastAccessed'
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
erDiagram
|
|
||||||
KeySpaces {
|
|
||||||
string name PK
|
|
||||||
string encryptedData
|
|
||||||
date created
|
|
||||||
date lastAccessed
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Database Initialization
|
|
||||||
|
|
||||||
Create a module for initializing and managing the IndexedDB database:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Database constants
|
|
||||||
const DB_NAME = 'CryptoSpaceDB';
|
|
||||||
const DB_VERSION = 1;
|
|
||||||
const STORE_NAME = 'keySpaces';
|
|
||||||
|
|
||||||
// Initialize the database
|
|
||||||
function initDatabase() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
reject('Error opening database: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const db = event.target.result;
|
|
||||||
resolve(db);
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onupgradeneeded = (event) => {
|
|
||||||
const db = event.target.result;
|
|
||||||
// Create object store for key spaces if it doesn't exist
|
|
||||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
||||||
const store = db.createObjectStore(STORE_NAME, { keyPath: 'name' });
|
|
||||||
store.createIndex('name', 'name', { unique: true });
|
|
||||||
store.createIndex('lastAccessed', 'lastAccessed', { unique: false });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get database connection
|
|
||||||
function getDB() {
|
|
||||||
return initDatabase();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Replace Storage Functions
|
|
||||||
|
|
||||||
Replace the localStorage functions with IndexedDB equivalents:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Save encrypted space to IndexedDB
|
|
||||||
async function saveSpaceToStorage(spaceName, encryptedData) {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
|
|
||||||
const space = {
|
|
||||||
name: spaceName,
|
|
||||||
encryptedData: encryptedData,
|
|
||||||
created: new Date(),
|
|
||||||
lastAccessed: new Date()
|
|
||||||
};
|
|
||||||
|
|
||||||
const request = store.put(space);
|
|
||||||
|
|
||||||
request.onsuccess = () => {
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
reject('Error saving space: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get encrypted space from IndexedDB
|
|
||||||
async function getSpaceFromStorage(spaceName) {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
const request = store.get(spaceName);
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const space = event.target.result;
|
|
||||||
if (space) {
|
|
||||||
// Update last accessed timestamp
|
|
||||||
updateLastAccessed(spaceName).catch(console.error);
|
|
||||||
resolve(space.encryptedData);
|
|
||||||
} else {
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
reject('Error retrieving space: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update last accessed timestamp
|
|
||||||
async function updateLastAccessed(spaceName) {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
const request = store.get(spaceName);
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const space = event.target.result;
|
|
||||||
if (space) {
|
|
||||||
space.lastAccessed = new Date();
|
|
||||||
store.put(space);
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// List all spaces in IndexedDB
|
|
||||||
async function listSpacesFromStorage() {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
const request = store.openCursor();
|
|
||||||
|
|
||||||
const spaces = [];
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const cursor = event.target.result;
|
|
||||||
if (cursor) {
|
|
||||||
spaces.push(cursor.value.name);
|
|
||||||
cursor.continue();
|
|
||||||
} else {
|
|
||||||
resolve(spaces);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
reject('Error listing spaces: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove space from IndexedDB
|
|
||||||
async function removeSpaceFromStorage(spaceName) {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
const request = store.delete(spaceName);
|
|
||||||
|
|
||||||
request.onsuccess = () => {
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
reject('Error removing space: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Update Application Flow
|
|
||||||
|
|
||||||
Update the login, logout, and other functions to handle the asynchronous nature of IndexedDB:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Login to a space
|
|
||||||
async function performLogin() {
|
|
||||||
const spaceName = document.getElementById('space-name').value.trim();
|
|
||||||
const password = document.getElementById('space-password').value;
|
|
||||||
|
|
||||||
if (!spaceName || !password) {
|
|
||||||
document.getElementById('space-result').textContent = 'Please enter both space name and password';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get encrypted space from IndexedDB
|
|
||||||
const encryptedSpace = await getSpaceFromStorage(spaceName);
|
|
||||||
if (!encryptedSpace) {
|
|
||||||
document.getElementById('space-result').textContent = `Space "${spaceName}" not found`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt the space
|
|
||||||
const result = decrypt_key_space(encryptedSpace, password);
|
|
||||||
if (result === 0) {
|
|
||||||
isLoggedIn = true;
|
|
||||||
currentSpace = spaceName;
|
|
||||||
updateLoginUI();
|
|
||||||
updateKeypairsList();
|
|
||||||
document.getElementById('space-result').textContent = `Successfully logged in to space "${spaceName}"`;
|
|
||||||
|
|
||||||
// Setup auto-logout
|
|
||||||
updateActivity();
|
|
||||||
setupAutoLogout();
|
|
||||||
|
|
||||||
// Add activity listeners
|
|
||||||
document.addEventListener('click', updateActivity);
|
|
||||||
document.addEventListener('keypress', updateActivity);
|
|
||||||
} else {
|
|
||||||
document.getElementById('space-result').textContent = `Error logging in: ${result}`;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new space
|
|
||||||
async function performCreateSpace() {
|
|
||||||
const spaceName = document.getElementById('space-name').value.trim();
|
|
||||||
const password = document.getElementById('space-password').value;
|
|
||||||
|
|
||||||
if (!spaceName || !password) {
|
|
||||||
document.getElementById('space-result').textContent = 'Please enter both space name and password';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check if space already exists
|
|
||||||
const existingSpace = await getSpaceFromStorage(spaceName);
|
|
||||||
if (existingSpace) {
|
|
||||||
document.getElementById('space-result').textContent = `Space "${spaceName}" already exists`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new space
|
|
||||||
const result = create_key_space(spaceName);
|
|
||||||
if (result === 0) {
|
|
||||||
// Encrypt and save the space
|
|
||||||
const encryptedSpace = encrypt_key_space(password);
|
|
||||||
await saveSpaceToStorage(spaceName, encryptedSpace);
|
|
||||||
|
|
||||||
isLoggedIn = true;
|
|
||||||
currentSpace = spaceName;
|
|
||||||
updateLoginUI();
|
|
||||||
updateKeypairsList();
|
|
||||||
document.getElementById('space-result').textContent = `Successfully created space "${spaceName}"`;
|
|
||||||
|
|
||||||
// Setup auto-logout
|
|
||||||
updateActivity();
|
|
||||||
setupAutoLogout();
|
|
||||||
|
|
||||||
// Add activity listeners
|
|
||||||
document.addEventListener('click', updateActivity);
|
|
||||||
document.addEventListener('keypress', updateActivity);
|
|
||||||
} else {
|
|
||||||
document.getElementById('space-result').textContent = `Error creating space: ${result}`;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete a space from storage
|
|
||||||
async function deleteSpace(spaceName) {
|
|
||||||
if (!spaceName) return false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check if space exists
|
|
||||||
const existingSpace = await getSpaceFromStorage(spaceName);
|
|
||||||
if (!existingSpace) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from IndexedDB
|
|
||||||
await removeSpaceFromStorage(spaceName);
|
|
||||||
|
|
||||||
// If this was the current space, logout
|
|
||||||
if (isLoggedIn && currentSpace === spaceName) {
|
|
||||||
performLogout();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error deleting space:', e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the spaces dropdown list
|
|
||||||
async function updateSpacesList() {
|
|
||||||
const spacesList = document.getElementById('space-list');
|
|
||||||
|
|
||||||
// Clear existing options
|
|
||||||
while (spacesList.options.length > 1) {
|
|
||||||
spacesList.remove(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get spaces list
|
|
||||||
const spaces = await listSpacesFromStorage();
|
|
||||||
|
|
||||||
// Add options for each space
|
|
||||||
spaces.forEach(spaceName => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = spaceName;
|
|
||||||
option.textContent = spaceName;
|
|
||||||
spacesList.appendChild(option);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error updating spaces list:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the current space to storage
|
|
||||||
async function saveCurrentSpace() {
|
|
||||||
if (!isLoggedIn || !currentSpace) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Store the password in a session variable when logging in
|
|
||||||
// and use it here to avoid issues when the password field is cleared
|
|
||||||
const password = document.getElementById('space-password').value;
|
|
||||||
if (!password) {
|
|
||||||
console.error('Password not available for saving space');
|
|
||||||
alert('Please re-enter your password to save changes');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const encryptedSpace = encrypt_key_space(password);
|
|
||||||
await saveSpaceToStorage(currentSpace, encryptedSpace);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error saving space:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Update Event Handlers
|
|
||||||
|
|
||||||
Update the event handlers in the `run()` function to handle asynchronous operations:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
document.getElementById('delete-space-button').addEventListener('click', async () => {
|
|
||||||
if (confirm(`Are you sure you want to delete the space "${currentSpace}"? This action cannot be undone.`)) {
|
|
||||||
try {
|
|
||||||
if (await deleteSpace(currentSpace)) {
|
|
||||||
document.getElementById('space-result').textContent = `Space "${currentSpace}" deleted successfully`;
|
|
||||||
} else {
|
|
||||||
document.getElementById('space-result').textContent = `Error deleting space "${currentSpace}"`;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('delete-selected-space-button').addEventListener('click', async () => {
|
|
||||||
const selectedSpace = document.getElementById('space-list').value;
|
|
||||||
if (!selectedSpace) {
|
|
||||||
document.getElementById('space-result').textContent = 'Please select a space to delete';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (confirm(`Are you sure you want to delete the space "${selectedSpace}"? This action cannot be undone.`)) {
|
|
||||||
try {
|
|
||||||
if (await deleteSpace(selectedSpace)) {
|
|
||||||
document.getElementById('space-result').textContent = `Space "${selectedSpace}" deleted successfully`;
|
|
||||||
await updateSpacesList();
|
|
||||||
} else {
|
|
||||||
document.getElementById('space-result').textContent = `Error deleting space "${selectedSpace}"`;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
1. **Unit Tests**:
|
|
||||||
- Test individual IndexedDB functions
|
|
||||||
- Verify CRUD operations work correctly
|
|
||||||
|
|
||||||
2. **Integration Tests**:
|
|
||||||
- Test full application flow with IndexedDB
|
|
||||||
- Verify UI updates correctly
|
|
||||||
|
|
||||||
3. **Error Handling Tests**:
|
|
||||||
- Test database connection errors
|
|
||||||
- Test transaction rollbacks
|
|
||||||
|
|
||||||
4. **Performance Tests**:
|
|
||||||
- Compare performance with localStorage
|
|
||||||
- Verify improved performance for larger data sets
|
|
||||||
|
|
||||||
## Potential Challenges and Solutions
|
|
||||||
|
|
||||||
1. **Browser Compatibility**:
|
|
||||||
- IndexedDB is supported in all modern browsers, but older browsers might have compatibility issues
|
|
||||||
- Consider using a feature detection approach before initializing IndexedDB
|
|
||||||
- Provide a fallback mechanism for browsers that don't support IndexedDB
|
|
||||||
|
|
||||||
2. **Transaction Management**:
|
|
||||||
- Properly manage transactions to maintain data integrity
|
|
||||||
- Ensure all operations within a transaction are completed or rolled back
|
|
||||||
- Use appropriate transaction modes ('readonly' or 'readwrite')
|
|
||||||
|
|
||||||
3. **Error Handling**:
|
|
||||||
- Implement comprehensive error handling for all IndexedDB operations
|
|
||||||
- Provide user-friendly error messages
|
|
||||||
- Log detailed error information for debugging
|
|
||||||
|
|
||||||
4. **Asynchronous Operations**:
|
|
||||||
- Handle Promise rejections with try/catch blocks
|
|
||||||
- Provide loading indicators for operations that might take time
|
|
||||||
- Consider using async/await for cleaner code and better error handling
|
|
||||||
|
|
||||||
## Implementation Steps
|
|
||||||
|
|
||||||
1. Create the database initialization module
|
|
||||||
2. Implement the IndexedDB storage functions
|
|
||||||
3. Update the UI functions to handle asynchronous operations
|
|
||||||
4. Add comprehensive error handling
|
|
||||||
5. Test all functionality
|
|
||||||
6. Deploy the updated application
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
Migrating from localStorage to IndexedDB will provide better performance, transaction capabilities, and a more structured approach to data storage. The asynchronous nature of IndexedDB requires updates to the application flow, but the benefits outweigh the implementation effort.
|
|
@ -33,107 +33,17 @@ function hexToBuffer(hex) {
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IndexedDB setup for Ethereum wallets
|
// LocalStorage functions for Ethereum wallets
|
||||||
const DB_NAME = 'EthWalletDB';
|
const ETH_WALLET_PREFIX = 'eth_wallet_';
|
||||||
const DB_VERSION = 1;
|
|
||||||
const STORE_NAME = 'ethWallets';
|
|
||||||
|
|
||||||
// Initialize the database
|
// Save Ethereum wallet to localStorage
|
||||||
function initDatabase() {
|
function saveEthWalletToStorage(address, privateKey) {
|
||||||
return new Promise((resolve, reject) => {
|
localStorage.setItem(`${ETH_WALLET_PREFIX}${address}`, privateKey);
|
||||||
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
console.error('Error opening Ethereum wallet database:', event.target.error);
|
|
||||||
reject('Error opening database: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const db = event.target.result;
|
|
||||||
resolve(db);
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onupgradeneeded = (event) => {
|
|
||||||
const db = event.target.result;
|
|
||||||
// Create object store for Ethereum wallets if it doesn't exist
|
|
||||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
||||||
const store = db.createObjectStore(STORE_NAME, { keyPath: 'address' });
|
|
||||||
store.createIndex('address', 'address', { unique: true });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get database connection
|
// Get Ethereum wallet from localStorage
|
||||||
function getDB() {
|
function getEthWalletFromStorage(address) {
|
||||||
return initDatabase();
|
return localStorage.getItem(`${ETH_WALLET_PREFIX}${address}`);
|
||||||
}
|
|
||||||
|
|
||||||
// Save Ethereum wallet to IndexedDB
|
|
||||||
async function saveEthWalletToStorage(address, privateKey) {
|
|
||||||
try {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
|
|
||||||
const wallet = {
|
|
||||||
address: address,
|
|
||||||
privateKey: privateKey,
|
|
||||||
created: new Date()
|
|
||||||
};
|
|
||||||
|
|
||||||
const request = store.put(wallet);
|
|
||||||
|
|
||||||
request.onsuccess = () => {
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
console.error('Error saving Ethereum wallet:', event.target.error);
|
|
||||||
reject('Error saving wallet: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Database error in saveEthWalletToStorage:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Ethereum wallet from IndexedDB
|
|
||||||
async function getEthWalletFromStorage(address) {
|
|
||||||
try {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
const request = store.get(address);
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const wallet = event.target.result;
|
|
||||||
if (wallet) {
|
|
||||||
resolve(wallet.privateKey);
|
|
||||||
} else {
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
console.error('Error retrieving Ethereum wallet:', event.target.error);
|
|
||||||
reject('Error retrieving wallet: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Database error in getEthWalletFromStorage:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session state
|
// Session state
|
||||||
@ -237,20 +147,13 @@ async function performCreateEthereumWallet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Show loading state
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = 'Creating wallet...';
|
|
||||||
|
|
||||||
// Create Ethereum wallet
|
// Create Ethereum wallet
|
||||||
console.log('Creating Ethereum wallet from keypair:', selectedKeypair);
|
|
||||||
const result = create_ethereum_wallet();
|
const result = create_ethereum_wallet();
|
||||||
console.log('Create Ethereum wallet result:', result);
|
|
||||||
|
|
||||||
if (result === 0) {
|
if (result === 0) {
|
||||||
hasEthereumWallet = true;
|
hasEthereumWallet = true;
|
||||||
|
|
||||||
// Get and display Ethereum address
|
// Get and display Ethereum address
|
||||||
const address = get_ethereum_address();
|
const address = get_ethereum_address();
|
||||||
console.log('Generated Ethereum address:', address);
|
|
||||||
document.getElementById('ethereum-address-value').textContent = address;
|
document.getElementById('ethereum-address-value').textContent = address;
|
||||||
|
|
||||||
// Get and display private key
|
// Get and display private key
|
||||||
@ -260,22 +163,14 @@ async function performCreateEthereumWallet() {
|
|||||||
// Show the wallet info
|
// Show the wallet info
|
||||||
document.getElementById('ethereum-wallet-info').classList.remove('hidden');
|
document.getElementById('ethereum-wallet-info').classList.remove('hidden');
|
||||||
|
|
||||||
try {
|
// Save the wallet to localStorage
|
||||||
// Save the wallet to IndexedDB
|
saveEthWalletToStorage(address, privateKey);
|
||||||
console.log('Saving wallet to IndexedDB:', address);
|
|
||||||
await saveEthWalletToStorage(address, privateKey);
|
document.getElementById('ethereum-wallet-result').textContent = 'Successfully created Ethereum wallet';
|
||||||
console.log('Wallet saved successfully');
|
|
||||||
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = 'Successfully created Ethereum wallet';
|
|
||||||
} catch (saveError) {
|
|
||||||
console.error('Error saving wallet to IndexedDB:', saveError);
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = 'Wallet created but failed to save to storage';
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('ethereum-wallet-result').textContent = `Error creating Ethereum wallet: ${result}`;
|
document.getElementById('ethereum-wallet-result').textContent = `Error creating Ethereum wallet: ${result}`;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error in performCreateEthereumWallet:', e);
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
|
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,20 +190,13 @@ async function performCreateEthereumWalletFromName() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Show loading state
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = 'Creating wallet...';
|
|
||||||
|
|
||||||
// Create Ethereum wallet from name
|
// Create Ethereum wallet from name
|
||||||
console.log('Creating Ethereum wallet from name:', name);
|
|
||||||
const result = create_ethereum_wallet_from_name(name);
|
const result = create_ethereum_wallet_from_name(name);
|
||||||
console.log('Create Ethereum wallet from name result:', result);
|
|
||||||
|
|
||||||
if (result === 0) {
|
if (result === 0) {
|
||||||
hasEthereumWallet = true;
|
hasEthereumWallet = true;
|
||||||
|
|
||||||
// Get and display Ethereum address
|
// Get and display Ethereum address
|
||||||
const address = get_ethereum_address();
|
const address = get_ethereum_address();
|
||||||
console.log('Generated Ethereum address:', address);
|
|
||||||
document.getElementById('ethereum-address-value').textContent = address;
|
document.getElementById('ethereum-address-value').textContent = address;
|
||||||
|
|
||||||
// Get and display private key
|
// Get and display private key
|
||||||
@ -318,22 +206,14 @@ async function performCreateEthereumWalletFromName() {
|
|||||||
// Show the wallet info
|
// Show the wallet info
|
||||||
document.getElementById('ethereum-wallet-info').classList.remove('hidden');
|
document.getElementById('ethereum-wallet-info').classList.remove('hidden');
|
||||||
|
|
||||||
try {
|
// Save the wallet to localStorage
|
||||||
// Save the wallet to IndexedDB
|
saveEthWalletToStorage(address, privateKey);
|
||||||
console.log('Saving wallet to IndexedDB:', address);
|
|
||||||
await saveEthWalletToStorage(address, privateKey);
|
document.getElementById('ethereum-wallet-result').textContent = `Successfully created Ethereum wallet from name "${name}"`;
|
||||||
console.log('Wallet saved successfully');
|
|
||||||
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = `Successfully created Ethereum wallet from name "${name}"`;
|
|
||||||
} catch (saveError) {
|
|
||||||
console.error('Error saving wallet to IndexedDB:', saveError);
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = 'Wallet created but failed to save to storage';
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('ethereum-wallet-result').textContent = `Error creating Ethereum wallet: ${result}`;
|
document.getElementById('ethereum-wallet-result').textContent = `Error creating Ethereum wallet: ${result}`;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error in performCreateEthereumWalletFromName:', e);
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
|
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -348,20 +228,13 @@ async function performCreateEthereumWalletFromPrivateKey() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Show loading state
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = 'Creating wallet...';
|
|
||||||
|
|
||||||
// Create Ethereum wallet from private key
|
// Create Ethereum wallet from private key
|
||||||
console.log('Creating Ethereum wallet from private key');
|
|
||||||
const result = create_ethereum_wallet_from_private_key(privateKey);
|
const result = create_ethereum_wallet_from_private_key(privateKey);
|
||||||
console.log('Create Ethereum wallet from private key result:', result);
|
|
||||||
|
|
||||||
if (result === 0) {
|
if (result === 0) {
|
||||||
hasEthereumWallet = true;
|
hasEthereumWallet = true;
|
||||||
|
|
||||||
// Get and display Ethereum address
|
// Get and display Ethereum address
|
||||||
const address = get_ethereum_address();
|
const address = get_ethereum_address();
|
||||||
console.log('Generated Ethereum address:', address);
|
|
||||||
document.getElementById('ethereum-address-value').textContent = address;
|
document.getElementById('ethereum-address-value').textContent = address;
|
||||||
|
|
||||||
// Get and display private key
|
// Get and display private key
|
||||||
@ -371,22 +244,14 @@ async function performCreateEthereumWalletFromPrivateKey() {
|
|||||||
// Show the wallet info
|
// Show the wallet info
|
||||||
document.getElementById('ethereum-wallet-info').classList.remove('hidden');
|
document.getElementById('ethereum-wallet-info').classList.remove('hidden');
|
||||||
|
|
||||||
try {
|
// Save the wallet to localStorage
|
||||||
// Save the wallet to IndexedDB
|
saveEthWalletToStorage(address, displayPrivateKey);
|
||||||
console.log('Saving wallet to IndexedDB:', address);
|
|
||||||
await saveEthWalletToStorage(address, displayPrivateKey);
|
document.getElementById('ethereum-wallet-result').textContent = 'Successfully imported Ethereum wallet from private key';
|
||||||
console.log('Wallet saved successfully');
|
|
||||||
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = 'Successfully imported Ethereum wallet from private key';
|
|
||||||
} catch (saveError) {
|
|
||||||
console.error('Error saving wallet to IndexedDB:', saveError);
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = 'Wallet imported but failed to save to storage';
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('ethereum-wallet-result').textContent = `Error importing Ethereum wallet: ${result}`;
|
document.getElementById('ethereum-wallet-result').textContent = `Error importing Ethereum wallet: ${result}`;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error in performCreateEthereumWalletFromPrivateKey:', e);
|
|
||||||
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
|
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
434
www/js/index.js
434
www/js/index.js
@ -70,186 +70,34 @@ function clearAutoLogout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IndexedDB setup and functions
|
// LocalStorage functions for key spaces
|
||||||
const DB_NAME = 'CryptoSpaceDB';
|
const STORAGE_PREFIX = 'crypto_space_';
|
||||||
const DB_VERSION = 1;
|
|
||||||
const STORE_NAME = 'keySpaces';
|
|
||||||
|
|
||||||
// Initialize the database
|
// Save encrypted space to localStorage
|
||||||
function initDatabase() {
|
function saveSpaceToStorage(spaceName, encryptedData) {
|
||||||
return new Promise((resolve, reject) => {
|
localStorage.setItem(`${STORAGE_PREFIX}${spaceName}`, encryptedData);
|
||||||
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
console.error('Error opening database:', event.target.error);
|
|
||||||
reject('Error opening database: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const db = event.target.result;
|
|
||||||
resolve(db);
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onupgradeneeded = (event) => {
|
|
||||||
const db = event.target.result;
|
|
||||||
// Create object store for key spaces if it doesn't exist
|
|
||||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
||||||
const store = db.createObjectStore(STORE_NAME, { keyPath: 'name' });
|
|
||||||
store.createIndex('name', 'name', { unique: true });
|
|
||||||
store.createIndex('lastAccessed', 'lastAccessed', { unique: false });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get database connection
|
// Get encrypted space from localStorage
|
||||||
function getDB() {
|
function getSpaceFromStorage(spaceName) {
|
||||||
return initDatabase();
|
return localStorage.getItem(`${STORAGE_PREFIX}${spaceName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save encrypted space to IndexedDB
|
// List all spaces in localStorage
|
||||||
async function saveSpaceToStorage(spaceName, encryptedData) {
|
function listSpacesFromStorage() {
|
||||||
const db = await getDB();
|
const spaces = [];
|
||||||
return new Promise((resolve, reject) => {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
const key = localStorage.key(i);
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
if (key.startsWith(STORAGE_PREFIX)) {
|
||||||
|
spaces.push(key.substring(STORAGE_PREFIX.length));
|
||||||
const space = {
|
}
|
||||||
name: spaceName,
|
|
||||||
encryptedData: encryptedData,
|
|
||||||
created: new Date(),
|
|
||||||
lastAccessed: new Date()
|
|
||||||
};
|
|
||||||
|
|
||||||
const request = store.put(space);
|
|
||||||
|
|
||||||
request.onsuccess = () => {
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
console.error('Error saving space:', event.target.error);
|
|
||||||
reject('Error saving space: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get encrypted space from IndexedDB
|
|
||||||
async function getSpaceFromStorage(spaceName) {
|
|
||||||
try {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
const request = store.get(spaceName);
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const space = event.target.result;
|
|
||||||
if (space) {
|
|
||||||
// Update last accessed timestamp
|
|
||||||
updateLastAccessed(spaceName).catch(console.error);
|
|
||||||
resolve(space.encryptedData);
|
|
||||||
} else {
|
|
||||||
resolve(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
console.error('Error retrieving space:', event.target.error);
|
|
||||||
reject('Error retrieving space: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Database error in getSpaceFromStorage:', error);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return spaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update last accessed timestamp
|
// Remove space from localStorage
|
||||||
async function updateLastAccessed(spaceName) {
|
function removeSpaceFromStorage(spaceName) {
|
||||||
const db = await getDB();
|
localStorage.removeItem(`${STORAGE_PREFIX}${spaceName}`);
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
const request = store.get(spaceName);
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const space = event.target.result;
|
|
||||||
if (space) {
|
|
||||||
space.lastAccessed = new Date();
|
|
||||||
store.put(space);
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// List all spaces in IndexedDB
|
|
||||||
async function listSpacesFromStorage() {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readonly');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
const request = store.openCursor();
|
|
||||||
|
|
||||||
const spaces = [];
|
|
||||||
|
|
||||||
request.onsuccess = (event) => {
|
|
||||||
const cursor = event.target.result;
|
|
||||||
if (cursor) {
|
|
||||||
spaces.push(cursor.value.name);
|
|
||||||
cursor.continue();
|
|
||||||
} else {
|
|
||||||
resolve(spaces);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
console.error('Error listing spaces:', event.target.error);
|
|
||||||
reject('Error listing spaces: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove space from IndexedDB
|
|
||||||
async function removeSpaceFromStorage(spaceName) {
|
|
||||||
const db = await getDB();
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
|
||||||
const store = transaction.objectStore(STORE_NAME);
|
|
||||||
const request = store.delete(spaceName);
|
|
||||||
|
|
||||||
request.onsuccess = () => {
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
request.onerror = (event) => {
|
|
||||||
console.error('Error removing space:', event.target.error);
|
|
||||||
reject('Error removing space: ' + event.target.error);
|
|
||||||
};
|
|
||||||
|
|
||||||
transaction.oncomplete = () => {
|
|
||||||
db.close();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session state
|
// Session state
|
||||||
@ -258,7 +106,7 @@ let currentSpace = null;
|
|||||||
let selectedKeypair = null;
|
let selectedKeypair = null;
|
||||||
|
|
||||||
// Update UI based on login state
|
// Update UI based on login state
|
||||||
async function updateLoginUI() {
|
function updateLoginUI() {
|
||||||
const loginForm = document.getElementById('login-form');
|
const loginForm = document.getElementById('login-form');
|
||||||
const logoutForm = document.getElementById('logout-form');
|
const logoutForm = document.getElementById('logout-form');
|
||||||
const loginStatus = document.getElementById('login-status');
|
const loginStatus = document.getElementById('login-status');
|
||||||
@ -279,15 +127,11 @@ async function updateLoginUI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the spaces list
|
// Update the spaces list
|
||||||
try {
|
updateSpacesList();
|
||||||
await updateSpacesList();
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error updating spaces list in UI:', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the spaces dropdown list
|
// Update the spaces dropdown list
|
||||||
async function updateSpacesList() {
|
function updateSpacesList() {
|
||||||
const spacesList = document.getElementById('space-list');
|
const spacesList = document.getElementById('space-list');
|
||||||
|
|
||||||
// Clear existing options
|
// Clear existing options
|
||||||
@ -295,20 +139,16 @@ async function updateSpacesList() {
|
|||||||
spacesList.remove(1);
|
spacesList.remove(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// Get spaces list
|
||||||
// Get spaces list
|
const spaces = listSpacesFromStorage();
|
||||||
const spaces = await listSpacesFromStorage();
|
|
||||||
|
// Add options for each space
|
||||||
// Add options for each space
|
spaces.forEach(spaceName => {
|
||||||
spaces.forEach(spaceName => {
|
const option = document.createElement('option');
|
||||||
const option = document.createElement('option');
|
option.value = spaceName;
|
||||||
option.value = spaceName;
|
option.textContent = spaceName;
|
||||||
option.textContent = spaceName;
|
spacesList.appendChild(option);
|
||||||
spacesList.appendChild(option);
|
});
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error updating spaces list:', e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login to a space
|
// Login to a space
|
||||||
@ -322,46 +162,33 @@ async function performLogin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Show loading state
|
// Get encrypted space from localStorage
|
||||||
document.getElementById('space-result').textContent = 'Loading...';
|
const encryptedSpace = getSpaceFromStorage(spaceName);
|
||||||
|
|
||||||
// Get encrypted space from IndexedDB
|
|
||||||
const encryptedSpace = await getSpaceFromStorage(spaceName);
|
|
||||||
if (!encryptedSpace) {
|
if (!encryptedSpace) {
|
||||||
document.getElementById('space-result').textContent = `Space "${spaceName}" not found`;
|
document.getElementById('space-result').textContent = `Space "${spaceName}" not found`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Retrieved space from IndexedDB:', { spaceName, encryptedDataLength: encryptedSpace.length });
|
// Decrypt the space
|
||||||
|
const result = decrypt_key_space(encryptedSpace, password);
|
||||||
try {
|
if (result === 0) {
|
||||||
// Decrypt the space - this is a synchronous WebAssembly function
|
isLoggedIn = true;
|
||||||
const result = decrypt_key_space(encryptedSpace, password);
|
currentSpace = spaceName;
|
||||||
console.log('Decrypt result:', result);
|
updateLoginUI();
|
||||||
|
updateKeypairsList();
|
||||||
|
document.getElementById('space-result').textContent = `Successfully logged in to space "${spaceName}"`;
|
||||||
|
|
||||||
if (result === 0) {
|
// Setup auto-logout
|
||||||
isLoggedIn = true;
|
updateActivity();
|
||||||
currentSpace = spaceName;
|
setupAutoLogout();
|
||||||
await updateLoginUI();
|
|
||||||
updateKeypairsList();
|
// Add activity listeners
|
||||||
document.getElementById('space-result').textContent = `Successfully logged in to space "${spaceName}"`;
|
document.addEventListener('click', updateActivity);
|
||||||
|
document.addEventListener('keypress', updateActivity);
|
||||||
// Setup auto-logout
|
} else {
|
||||||
updateActivity();
|
document.getElementById('space-result').textContent = `Error logging in: ${result}`;
|
||||||
setupAutoLogout();
|
|
||||||
|
|
||||||
// Add activity listeners
|
|
||||||
document.addEventListener('click', updateActivity);
|
|
||||||
document.addEventListener('keypress', updateActivity);
|
|
||||||
} else {
|
|
||||||
document.getElementById('space-result').textContent = `Error logging in: ${result}`;
|
|
||||||
}
|
|
||||||
} catch (decryptErr) {
|
|
||||||
console.error('Decryption error:', decryptErr);
|
|
||||||
document.getElementById('space-result').textContent = `Decryption error: ${decryptErr}`;
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Login error:', e);
|
|
||||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
document.getElementById('space-result').textContent = `Error: ${e}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,61 +203,37 @@ async function performCreateSpace() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if space already exists
|
||||||
|
if (getSpaceFromStorage(spaceName)) {
|
||||||
|
document.getElementById('space-result').textContent = `Space "${spaceName}" already exists`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Show loading state
|
// Create new space
|
||||||
document.getElementById('space-result').textContent = 'Loading...';
|
const result = create_key_space(spaceName);
|
||||||
|
if (result === 0) {
|
||||||
// Check if space already exists
|
// Encrypt and save the space
|
||||||
const existingSpace = await getSpaceFromStorage(spaceName);
|
const encryptedSpace = encrypt_key_space(password);
|
||||||
if (existingSpace) {
|
saveSpaceToStorage(spaceName, encryptedSpace);
|
||||||
document.getElementById('space-result').textContent = `Space "${spaceName}" already exists`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Create new space
|
|
||||||
console.log('Creating new space:', spaceName);
|
|
||||||
const result = create_key_space(spaceName);
|
|
||||||
console.log('Create space result:', result);
|
|
||||||
|
|
||||||
if (result === 0) {
|
isLoggedIn = true;
|
||||||
try {
|
currentSpace = spaceName;
|
||||||
// Encrypt and save the space
|
updateLoginUI();
|
||||||
console.log('Encrypting space with password');
|
updateKeypairsList();
|
||||||
const encryptedSpace = encrypt_key_space(password);
|
document.getElementById('space-result').textContent = `Successfully created space "${spaceName}"`;
|
||||||
console.log('Encrypted space length:', encryptedSpace.length);
|
|
||||||
|
// Setup auto-logout
|
||||||
// Save to IndexedDB
|
updateActivity();
|
||||||
console.log('Saving to IndexedDB');
|
setupAutoLogout();
|
||||||
await saveSpaceToStorage(spaceName, encryptedSpace);
|
|
||||||
console.log('Save completed');
|
// Add activity listeners
|
||||||
|
document.addEventListener('click', updateActivity);
|
||||||
isLoggedIn = true;
|
document.addEventListener('keypress', updateActivity);
|
||||||
currentSpace = spaceName;
|
} else {
|
||||||
await updateLoginUI();
|
document.getElementById('space-result').textContent = `Error creating space: ${result}`;
|
||||||
updateKeypairsList();
|
|
||||||
document.getElementById('space-result').textContent = `Successfully created space "${spaceName}"`;
|
|
||||||
|
|
||||||
// Setup auto-logout
|
|
||||||
updateActivity();
|
|
||||||
setupAutoLogout();
|
|
||||||
|
|
||||||
// Add activity listeners
|
|
||||||
document.addEventListener('click', updateActivity);
|
|
||||||
document.addEventListener('keypress', updateActivity);
|
|
||||||
} catch (encryptError) {
|
|
||||||
console.error('Error encrypting or saving space:', encryptError);
|
|
||||||
document.getElementById('space-result').textContent = `Error saving space: ${encryptError}`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
document.getElementById('space-result').textContent = `Error creating space: ${result}`;
|
|
||||||
}
|
|
||||||
} catch (createError) {
|
|
||||||
console.error('Error in WebAssembly create_key_space:', createError);
|
|
||||||
document.getElementById('space-result').textContent = `Error creating key space: ${createError}`;
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error checking existing space:', e);
|
|
||||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
document.getElementById('space-result').textContent = `Error: ${e}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -526,7 +329,7 @@ async function performCreateKeypair() {
|
|||||||
// Display public key
|
// Display public key
|
||||||
displaySelectedKeypairPublicKey();
|
displaySelectedKeypairPublicKey();
|
||||||
|
|
||||||
// Save the updated space to IndexedDB
|
// Save the updated space to localStorage
|
||||||
saveCurrentSpace();
|
saveCurrentSpace();
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('keypair-management-result').textContent = `Error creating keypair: ${result}`;
|
document.getElementById('keypair-management-result').textContent = `Error creating keypair: ${result}`;
|
||||||
@ -606,8 +409,8 @@ function displaySelectedKeypairPublicKey() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the current space to IndexedDB
|
// Save the current space to localStorage
|
||||||
async function saveCurrentSpace() {
|
function saveCurrentSpace() {
|
||||||
if (!isLoggedIn || !currentSpace) return;
|
if (!isLoggedIn || !currentSpace) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -621,37 +424,30 @@ async function saveCurrentSpace() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const encryptedSpace = encrypt_key_space(password);
|
const encryptedSpace = encrypt_key_space(password);
|
||||||
await saveSpaceToStorage(currentSpace, encryptedSpace);
|
saveSpaceToStorage(currentSpace, encryptedSpace);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error saving space:', e);
|
console.error('Error saving space:', e);
|
||||||
alert('Error saving space: ' + e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a space from IndexedDB
|
// Delete a space from localStorage
|
||||||
async function deleteSpace(spaceName) {
|
function deleteSpace(spaceName) {
|
||||||
if (!spaceName) return false;
|
if (!spaceName) return false;
|
||||||
|
|
||||||
try {
|
// Check if space exists
|
||||||
// Check if space exists
|
if (!getSpaceFromStorage(spaceName)) {
|
||||||
const existingSpace = await getSpaceFromStorage(spaceName);
|
|
||||||
if (!existingSpace) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from IndexedDB
|
|
||||||
await removeSpaceFromStorage(spaceName);
|
|
||||||
|
|
||||||
// If this was the current space, logout
|
|
||||||
if (isLoggedIn && currentSpace === spaceName) {
|
|
||||||
performLogout();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error deleting space:', e);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove from localStorage
|
||||||
|
removeSpaceFromStorage(spaceName);
|
||||||
|
|
||||||
|
// If this was the current space, logout
|
||||||
|
if (isLoggedIn && currentSpace === spaceName) {
|
||||||
|
performLogout();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
@ -664,24 +460,17 @@ async function run() {
|
|||||||
document.getElementById('login-button').addEventListener('click', performLogin);
|
document.getElementById('login-button').addEventListener('click', performLogin);
|
||||||
document.getElementById('create-space-button').addEventListener('click', performCreateSpace);
|
document.getElementById('create-space-button').addEventListener('click', performCreateSpace);
|
||||||
document.getElementById('logout-button').addEventListener('click', performLogout);
|
document.getElementById('logout-button').addEventListener('click', performLogout);
|
||||||
document.getElementById('delete-space-button').addEventListener('click', async () => {
|
document.getElementById('delete-space-button').addEventListener('click', () => {
|
||||||
if (confirm(`Are you sure you want to delete the space "${currentSpace}"? This action cannot be undone.`)) {
|
if (confirm(`Are you sure you want to delete the space "${currentSpace}"? This action cannot be undone.`)) {
|
||||||
document.getElementById('space-result').textContent = 'Deleting...';
|
if (deleteSpace(currentSpace)) {
|
||||||
try {
|
document.getElementById('space-result').textContent = `Space "${currentSpace}" deleted successfully`;
|
||||||
const result = await deleteSpace(currentSpace);
|
} else {
|
||||||
if (result) {
|
document.getElementById('space-result').textContent = `Error deleting space "${currentSpace}"`;
|
||||||
document.getElementById('space-result').textContent = `Space "${currentSpace}" deleted successfully`;
|
|
||||||
} else {
|
|
||||||
document.getElementById('space-result').textContent = `Error deleting space "${currentSpace}"`;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error during space deletion:', e);
|
|
||||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('delete-selected-space-button').addEventListener('click', async () => {
|
document.getElementById('delete-selected-space-button').addEventListener('click', () => {
|
||||||
const selectedSpace = document.getElementById('space-list').value;
|
const selectedSpace = document.getElementById('space-list').value;
|
||||||
if (!selectedSpace) {
|
if (!selectedSpace) {
|
||||||
document.getElementById('space-result').textContent = 'Please select a space to delete';
|
document.getElementById('space-result').textContent = 'Please select a space to delete';
|
||||||
@ -689,18 +478,11 @@ async function run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (confirm(`Are you sure you want to delete the space "${selectedSpace}"? This action cannot be undone.`)) {
|
if (confirm(`Are you sure you want to delete the space "${selectedSpace}"? This action cannot be undone.`)) {
|
||||||
document.getElementById('space-result').textContent = 'Deleting...';
|
if (deleteSpace(selectedSpace)) {
|
||||||
try {
|
document.getElementById('space-result').textContent = `Space "${selectedSpace}" deleted successfully`;
|
||||||
const result = await deleteSpace(selectedSpace);
|
updateSpacesList();
|
||||||
if (result) {
|
} else {
|
||||||
document.getElementById('space-result').textContent = `Space "${selectedSpace}" deleted successfully`;
|
document.getElementById('space-result').textContent = `Error deleting space "${selectedSpace}"`;
|
||||||
await updateSpacesList();
|
|
||||||
} else {
|
|
||||||
document.getElementById('space-result').textContent = `Error deleting space "${selectedSpace}"`;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error during space deletion:', e);
|
|
||||||
document.getElementById('space-result').textContent = `Error: ${e}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user