aprog 1.6.3 → 1.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/secretStorage.js +103 -0
package/README.md
CHANGED
|
@@ -161,9 +161,12 @@ onInput: (event) => {
|
|
|
161
161
|
|
|
162
162
|
- *cookie, storage*
|
|
163
163
|
|
|
164
|
+
- *saveItem("id", data), loadItem("id")*
|
|
165
|
+
|
|
164
166
|
### Info
|
|
165
167
|
|
|
166
|
-
- Додано безплатний чат із Gemini
|
|
168
|
+
- Додано безплатний чат із `Gemini`
|
|
169
|
+
- Додано методи збереження зашифрованих даних `saveItem` та `loadItem`
|
|
167
170
|
- З часом будуть додаватися можливості створення додаткових інструментів
|
|
168
171
|
|
|
169
172
|
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const DB_NAME = 'secureDB';
|
|
2
|
+
const STORE_NAME = 'secureStore';
|
|
3
|
+
const DB_VERSION = 1;
|
|
4
|
+
|
|
5
|
+
const getKey = async () => {
|
|
6
|
+
const password = import.meta.env.VITE_ENCRYPTION_KEY;
|
|
7
|
+
const encoder = new TextEncoder();
|
|
8
|
+
const salt = encoder.encode('nasty');
|
|
9
|
+
|
|
10
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
11
|
+
'raw',
|
|
12
|
+
encoder.encode(password),
|
|
13
|
+
{ name: 'PBKDF2' },
|
|
14
|
+
false,
|
|
15
|
+
['deriveKey']
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
return await crypto.subtle.deriveKey(
|
|
19
|
+
{
|
|
20
|
+
name: 'PBKDF2',
|
|
21
|
+
salt: salt,
|
|
22
|
+
iterations: 100000,
|
|
23
|
+
hash: 'SHA-256'
|
|
24
|
+
},
|
|
25
|
+
keyMaterial,
|
|
26
|
+
{ name: 'AES-GCM', length: 256 },
|
|
27
|
+
true,
|
|
28
|
+
['encrypt', 'decrypt']
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const encryptData = async (data) => {
|
|
33
|
+
const key = await getKey();
|
|
34
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
35
|
+
|
|
36
|
+
const encoded = new TextEncoder().encode(JSON.stringify(data));
|
|
37
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
38
|
+
{ name: 'AES-GCM', iv },
|
|
39
|
+
key,
|
|
40
|
+
encoded
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
iv: Array.from(iv),
|
|
45
|
+
data: Array.from(new Uint8Array(encrypted))
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const decryptData = async ({ iv, data }) => {
|
|
50
|
+
const key = await getKey();
|
|
51
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
52
|
+
{ name: 'AES-GCM', iv: new Uint8Array(iv) },
|
|
53
|
+
key,
|
|
54
|
+
new Uint8Array(data)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const decoded = new TextDecoder().decode(decrypted);
|
|
58
|
+
return JSON.parse(decoded);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const openDB = () => {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
64
|
+
|
|
65
|
+
request.onupgradeneeded = function (event) {
|
|
66
|
+
const db = event.target.result;
|
|
67
|
+
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
request.onsuccess = () => resolve(request.result);
|
|
71
|
+
request.onerror = () => reject(request.error);
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const saveItem = async (id, data) => {
|
|
76
|
+
const db = await openDB();
|
|
77
|
+
const encrypted = await encryptData(data);
|
|
78
|
+
|
|
79
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
80
|
+
const store = tx.objectStore(STORE_NAME);
|
|
81
|
+
store.put({ id, ...encrypted });
|
|
82
|
+
|
|
83
|
+
return tx.complete;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const loadItem = async (id) => {
|
|
87
|
+
const db = await openDB();
|
|
88
|
+
const tx = db.transaction(STORE_NAME, 'readonly');
|
|
89
|
+
const store = tx.objectStore(STORE_NAME);
|
|
90
|
+
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const request = store.get(id);
|
|
93
|
+
request.onsuccess = async () => {
|
|
94
|
+
if (request.result) {
|
|
95
|
+
const decrypted = await decryptData(request.result);
|
|
96
|
+
resolve(decrypted);
|
|
97
|
+
} else {
|
|
98
|
+
resolve(null);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
request.onerror = () => reject(request.error);
|
|
102
|
+
});
|
|
103
|
+
};
|