apiro-db 1.0.7 → 1.0.8
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/lib/store.js +83 -65
- package/package.json +9 -2
package/lib/store.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const crypto = require("crypto");
|
|
2
2
|
const { readFile, writeFile } = require("./file");
|
|
3
3
|
const { encrypt, decrypt } = require("./crypto");
|
|
4
|
-
const {
|
|
4
|
+
const { encryptMasterKey, decryptMasterKey } = require("./masterKey");
|
|
5
5
|
const {
|
|
6
6
|
getByPath,
|
|
7
7
|
setByPath,
|
|
@@ -11,118 +11,136 @@ const {
|
|
|
11
11
|
class SecureStore {
|
|
12
12
|
constructor(options = {}) {
|
|
13
13
|
this.file = options.file || "./secure.db";
|
|
14
|
+
|
|
14
15
|
this.data = {};
|
|
15
16
|
this.masterKey = null;
|
|
17
|
+
|
|
18
|
+
this._dirty = false;
|
|
19
|
+
this._writeTimer = null;
|
|
20
|
+
this._writeInProgress = false;
|
|
21
|
+
|
|
16
22
|
this.ready = this._init();
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
/* -----------------------------
|
|
26
|
+
INIT
|
|
27
|
+
------------------------------ */
|
|
28
|
+
|
|
19
29
|
async _init() {
|
|
20
30
|
const existing = await readFile(this.file);
|
|
21
31
|
|
|
22
32
|
if (!existing) {
|
|
23
33
|
this.masterKey = crypto.randomBytes(32);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const payload = encrypt(JSON.stringify({}), this.masterKey);
|
|
27
|
-
|
|
28
|
-
await writeFile(this.file, JSON.stringify({
|
|
29
|
-
_meta: { key: encryptedKey },
|
|
30
|
-
payload
|
|
31
|
-
}));
|
|
32
|
-
|
|
34
|
+
await this._flush(true);
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
const parsed = JSON.parse(existing);
|
|
37
39
|
this.masterKey = decryptMasterKey(parsed._meta.key);
|
|
38
|
-
|
|
39
|
-
this.data = JSON.parse(decrypted);
|
|
40
|
+
this.data = JSON.parse(decrypt(parsed.payload, this.masterKey));
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
/* -----------------------------
|
|
44
|
+
WRITE-BEHIND ENGINE
|
|
45
|
+
------------------------------ */
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
payload
|
|
48
|
-
}));
|
|
49
|
-
}
|
|
47
|
+
_scheduleFlush() {
|
|
48
|
+
this._dirty = true;
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
await this.ready;
|
|
53
|
-
return getByPath(this.data, key);
|
|
54
|
-
}
|
|
50
|
+
if (this._writeTimer) return;
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return value;
|
|
52
|
+
this._writeTimer = setTimeout(() => {
|
|
53
|
+
this._writeTimer = null;
|
|
54
|
+
this._flush();
|
|
55
|
+
}, 50);
|
|
61
56
|
}
|
|
62
57
|
|
|
63
|
-
async
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
await this._save();
|
|
67
|
-
return existed;
|
|
68
|
-
}
|
|
58
|
+
async _flush(force = false) {
|
|
59
|
+
if (this._writeInProgress) return;
|
|
60
|
+
if (!this._dirty && !force) return;
|
|
69
61
|
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
this._writeInProgress = true;
|
|
63
|
+
this._dirty = false;
|
|
72
64
|
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
const payload = encrypt(
|
|
66
|
+
JSON.stringify(this.data),
|
|
67
|
+
this.masterKey
|
|
68
|
+
);
|
|
75
69
|
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
await writeFile(
|
|
71
|
+
this.file,
|
|
72
|
+
JSON.stringify({
|
|
73
|
+
_meta: { key: encryptMasterKey(this.masterKey) },
|
|
74
|
+
payload
|
|
75
|
+
})
|
|
76
|
+
);
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
return next;
|
|
78
|
+
this._writeInProgress = false;
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
|
|
81
|
+
/* -----------------------------
|
|
82
|
+
PUBLIC API (PATH-FIRST)
|
|
83
|
+
------------------------------ */
|
|
84
|
+
|
|
85
|
+
async get(path) {
|
|
84
86
|
await this.ready;
|
|
87
|
+
return getByPath(this.data, path);
|
|
88
|
+
}
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
async has(path) {
|
|
91
|
+
await this.ready;
|
|
92
|
+
return getByPath(this.data, path) !== undefined;
|
|
93
|
+
}
|
|
88
94
|
|
|
89
|
-
|
|
90
|
-
|
|
95
|
+
async set(path, value) {
|
|
96
|
+
await this.ready;
|
|
97
|
+
setByPath(this.data, path, value);
|
|
98
|
+
this._scheduleFlush();
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
91
101
|
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
async delete(path) {
|
|
103
|
+
await this.ready;
|
|
104
|
+
const existed = deleteByPath(this.data, path);
|
|
105
|
+
if (existed) this._scheduleFlush();
|
|
106
|
+
return existed;
|
|
94
107
|
}
|
|
95
108
|
|
|
96
|
-
async
|
|
109
|
+
async add(path, amount) {
|
|
97
110
|
await this.ready;
|
|
98
111
|
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
const current = getByPath(this.data, path);
|
|
113
|
+
const next = (typeof current === "number" ? current : 0) + amount;
|
|
101
114
|
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
setByPath(this.data, path, next);
|
|
116
|
+
this._scheduleFlush();
|
|
117
|
+
return next;
|
|
118
|
+
}
|
|
104
119
|
|
|
105
|
-
|
|
106
|
-
return
|
|
120
|
+
async subtract(path, amount) {
|
|
121
|
+
return this.add(path, -amount);
|
|
107
122
|
}
|
|
108
123
|
|
|
109
|
-
async
|
|
124
|
+
async push(path, value) {
|
|
110
125
|
await this.ready;
|
|
111
126
|
|
|
112
127
|
const current = getByPath(this.data, path);
|
|
128
|
+
const next = Array.isArray(current) ? current : [];
|
|
113
129
|
|
|
114
|
-
|
|
130
|
+
next.push(value);
|
|
131
|
+
setByPath(this.data, path, next);
|
|
115
132
|
|
|
116
|
-
|
|
133
|
+
this._scheduleFlush();
|
|
134
|
+
return next;
|
|
135
|
+
}
|
|
117
136
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
137
|
+
/* -----------------------------
|
|
138
|
+
SAFETY
|
|
139
|
+
------------------------------ */
|
|
122
140
|
|
|
123
|
-
|
|
141
|
+
async close() {
|
|
142
|
+
await this._flush(true);
|
|
124
143
|
}
|
|
125
|
-
|
|
126
144
|
}
|
|
127
145
|
|
|
128
146
|
module.exports = { SecureStore };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apiro-db",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "A lightweight, zero-dependency, encrypted data store for Node.js.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -8,7 +8,14 @@
|
|
|
8
8
|
},
|
|
9
9
|
"keywords": ["database", "storage", "encryption", "secure"],
|
|
10
10
|
"author": "krispowers",
|
|
11
|
-
"license": "
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"contributors": [
|
|
13
|
+
{
|
|
14
|
+
"name": "Kris Powers",
|
|
15
|
+
"githubUsername": "krispowers",
|
|
16
|
+
"url": "https://github.com/KrisPowers"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
12
19
|
"files": [
|
|
13
20
|
"Readme.md",
|
|
14
21
|
"index.js",
|