jsql-neo 1.0.0 → 1.2.0
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/index.js +2 -1
- package/lib/database.js +316 -58
- package/lib/query.js +435 -0
- package/lib/table.js +391 -199
- package/package.json +1 -1
package/index.js
CHANGED
package/lib/database.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// © Vexify 2026 All Rights Reserved.
|
|
2
2
|
/**
|
|
3
3
|
* JSQL Database — 数据库管理器
|
|
4
|
-
*
|
|
4
|
+
* 支持加密、插件系统、迁移、版本历史、变更流、响应式查询
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const crypto = require('crypto');
|
|
9
10
|
const Table = require('./table');
|
|
10
11
|
|
|
11
12
|
class Database {
|
|
@@ -13,8 +14,10 @@ class Database {
|
|
|
13
14
|
* @param {string} filePath - 数据库文件路径,传 null 或 ':memory:' 使用内存模式
|
|
14
15
|
* @param {object} options
|
|
15
16
|
* @param {boolean} options.autoSave - 是否自动保存(默认 true)
|
|
16
|
-
* @param {number} options.autoSaveInterval - 自动保存间隔 ms(默认 0
|
|
17
|
+
* @param {number} options.autoSaveInterval - 自动保存间隔 ms(默认 0)
|
|
17
18
|
* @param {boolean} options.pretty - JSON 是否格式化(默认 true)
|
|
19
|
+
* @param {string} options.encryptKey - 加密密钥(16/24/32 字节),启用 AES-256-CBC
|
|
20
|
+
* @param {boolean} options.versioning - 是否启用版本历史(默认 false)
|
|
18
21
|
*/
|
|
19
22
|
constructor(filePath, options = {}) {
|
|
20
23
|
this._filePath = filePath;
|
|
@@ -24,10 +27,19 @@ class Database {
|
|
|
24
27
|
autoSaveInterval: options.autoSaveInterval || 0,
|
|
25
28
|
pretty: options.pretty !== false
|
|
26
29
|
};
|
|
27
|
-
this._tables = {};
|
|
30
|
+
this._tables = {};
|
|
28
31
|
this._dirty = false;
|
|
29
32
|
this._autoSaveTimer = null;
|
|
30
|
-
this._loading = false;
|
|
33
|
+
this._loading = false;
|
|
34
|
+
this._transaction = null;
|
|
35
|
+
this._encryptKey = options.encryptKey || null;
|
|
36
|
+
this._versioning = options.versioning || false;
|
|
37
|
+
this._versions = {}; // tableName -> [{ rows, timestamp }]
|
|
38
|
+
this._maxVersions = options.maxVersions || 50;
|
|
39
|
+
this._plugins = [];
|
|
40
|
+
this._migrations = [];
|
|
41
|
+
this._observers = []; // 响应式观察者
|
|
42
|
+
this._changeStream = null; // 变更流
|
|
31
43
|
|
|
32
44
|
// 从文件加载
|
|
33
45
|
if (!this._memoryMode && fs.existsSync(this._filePath)) {
|
|
@@ -41,17 +53,6 @@ class Database {
|
|
|
41
53
|
// 表管理
|
|
42
54
|
// ============================================================
|
|
43
55
|
|
|
44
|
-
/**
|
|
45
|
-
* 创建表
|
|
46
|
-
* @param {string} name - 表名
|
|
47
|
-
* @param {object} schema - 表结构定义
|
|
48
|
-
* @example
|
|
49
|
-
* db.createTable('users', {
|
|
50
|
-
* id: { type: 'integer', primaryKey: true, autoIncrement: true },
|
|
51
|
-
* name: { type: 'string', required: true },
|
|
52
|
-
* email: { type: 'string', unique: true }
|
|
53
|
-
* })
|
|
54
|
-
*/
|
|
55
56
|
createTable(name, schema) {
|
|
56
57
|
if (this._tables[name]) {
|
|
57
58
|
throw new Error(`Table '${name}' already exists`);
|
|
@@ -59,80 +60,321 @@ class Database {
|
|
|
59
60
|
const table = new Table(name, schema, this);
|
|
60
61
|
this._tables[name] = table;
|
|
61
62
|
|
|
62
|
-
// 动态代理到 Database 实例上,方便 db.users.insert() 调用
|
|
63
63
|
Object.defineProperty(this, name, {
|
|
64
64
|
get: () => this._tables[name],
|
|
65
65
|
enumerable: true,
|
|
66
66
|
configurable: true
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
if (this._versioning) {
|
|
70
|
+
this._versions[name] = [];
|
|
71
|
+
}
|
|
72
|
+
|
|
69
73
|
this._markDirty();
|
|
70
74
|
return table;
|
|
71
75
|
}
|
|
72
76
|
|
|
73
|
-
/**
|
|
74
|
-
* 删除表
|
|
75
|
-
*/
|
|
76
77
|
dropTable(name) {
|
|
77
|
-
if (!this._tables[name]) {
|
|
78
|
-
throw new Error(`Table '${name}' does not exist`);
|
|
79
|
-
}
|
|
78
|
+
if (!this._tables[name]) throw new Error(`Table '${name}' does not exist`);
|
|
80
79
|
delete this._tables[name];
|
|
81
80
|
delete this[name];
|
|
81
|
+
delete this._versions[name];
|
|
82
82
|
this._markDirty();
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
/**
|
|
86
|
-
* 判断表是否存在
|
|
87
|
-
*/
|
|
88
85
|
hasTable(name) {
|
|
89
86
|
return !!this._tables[name];
|
|
90
87
|
}
|
|
91
88
|
|
|
92
|
-
/**
|
|
93
|
-
* 获取所有表名
|
|
94
|
-
*/
|
|
95
89
|
getTables() {
|
|
96
90
|
return Object.keys(this._tables);
|
|
97
91
|
}
|
|
98
92
|
|
|
99
93
|
// ============================================================
|
|
100
|
-
//
|
|
94
|
+
// 事务
|
|
95
|
+
// ============================================================
|
|
96
|
+
|
|
97
|
+
begin() {
|
|
98
|
+
if (this._transaction) throw new Error('Transaction already in progress');
|
|
99
|
+
this._transaction = { rows: {}, autoIncrements: {} };
|
|
100
|
+
for (const [name, table] of Object.entries(this._tables)) {
|
|
101
|
+
this._transaction.rows[name] = table._rows.map(r => ({ ...r }));
|
|
102
|
+
this._transaction.autoIncrements[name] = table._autoIncrement;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
commit() {
|
|
107
|
+
if (!this._transaction) throw new Error('No transaction in progress');
|
|
108
|
+
this._transaction = null;
|
|
109
|
+
this._markDirty();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
rollback() {
|
|
113
|
+
if (!this._transaction) throw new Error('No transaction in progress');
|
|
114
|
+
for (const [name, table] of Object.entries(this._tables)) {
|
|
115
|
+
if (this._transaction.rows[name]) {
|
|
116
|
+
table._rows = this._transaction.rows[name];
|
|
117
|
+
table._autoIncrement = this._transaction.autoIncrements[name];
|
|
118
|
+
for (const field of Object.keys(table._indexes)) {
|
|
119
|
+
table.createIndex(field);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
this._transaction = null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
inTransaction() {
|
|
127
|
+
return !!this._transaction;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ============================================================
|
|
131
|
+
// 版本历史
|
|
132
|
+
// ============================================================
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 保存当前快照
|
|
136
|
+
*/
|
|
137
|
+
snapshot(tableName) {
|
|
138
|
+
if (!this._versioning) return;
|
|
139
|
+
const table = this._tables[tableName];
|
|
140
|
+
if (!table) return;
|
|
141
|
+
if (!this._versions[tableName]) this._versions[tableName] = [];
|
|
142
|
+
|
|
143
|
+
// 空表跳过
|
|
144
|
+
if (table._rows.length === 0) return;
|
|
145
|
+
|
|
146
|
+
// 去重:如果当前状态与上次快照相同,跳过
|
|
147
|
+
const currentHash = JSON.stringify(table._rows);
|
|
148
|
+
const versions = this._versions[tableName];
|
|
149
|
+
if (versions.length > 0 && versions[versions.length - 1]._hash === currentHash) return;
|
|
150
|
+
|
|
151
|
+
versions.push({
|
|
152
|
+
rows: table._rows.map(r => ({ ...r })),
|
|
153
|
+
timestamp: Date.now(),
|
|
154
|
+
_hash: currentHash
|
|
155
|
+
});
|
|
156
|
+
if (versions.length > this._maxVersions) {
|
|
157
|
+
versions.shift();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 回退到指定版本
|
|
163
|
+
*/
|
|
164
|
+
undo(tableName, steps = 1) {
|
|
165
|
+
if (!this._versioning) throw new Error('Versioning is not enabled');
|
|
166
|
+
const versions = this._versions[tableName];
|
|
167
|
+
if (!versions || versions.length < 2) throw new Error('No versions to undo');
|
|
168
|
+
const table = this._tables[tableName];
|
|
169
|
+
if (!table) throw new Error(`Table '${tableName}' not found`);
|
|
170
|
+
|
|
171
|
+
for (let i = 0; i < steps && versions.length > 1; i++) {
|
|
172
|
+
versions.pop(); // 丢弃最新版本
|
|
173
|
+
const snapshot = versions[versions.length - 1]; // 恢复到上一个版本
|
|
174
|
+
table._rows = snapshot.rows.map(r => ({ ...r }));
|
|
175
|
+
table._autoIncrement = 0;
|
|
176
|
+
for (const row of table._rows) {
|
|
177
|
+
if (table._autoIncrementField && row[table._autoIncrementField] > table._autoIncrement) {
|
|
178
|
+
table._autoIncrement = row[table._autoIncrementField];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const field of Object.keys(table._indexes)) {
|
|
182
|
+
table.createIndex(field);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
this._markDirty();
|
|
186
|
+
return versions.length;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
getVersions(tableName) {
|
|
190
|
+
return this._versions[tableName] || [];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ============================================================
|
|
194
|
+
// 变更流 & 响应式
|
|
195
|
+
// ============================================================
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 监听变更事件
|
|
199
|
+
* @param {function} callback - (event) => {} event: { type, table, data, timestamp }
|
|
200
|
+
*/
|
|
201
|
+
onChange(callback) {
|
|
202
|
+
this._observers.push(callback);
|
|
203
|
+
return () => {
|
|
204
|
+
this._observers = this._observers.filter(cb => cb !== callback);
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_emitChange(type, table, data) {
|
|
209
|
+
const event = { type, table, data, timestamp: Date.now() };
|
|
210
|
+
for (const cb of this._observers) {
|
|
211
|
+
try { cb(event); } catch (e) { /* ignore */ }
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 创建变更流(返回异步迭代器)
|
|
217
|
+
*/
|
|
218
|
+
createChangeStream() {
|
|
219
|
+
const events = [];
|
|
220
|
+
let resolve = null;
|
|
221
|
+
const unsubscribe = this.onChange(event => {
|
|
222
|
+
events.push(event);
|
|
223
|
+
if (resolve) {
|
|
224
|
+
resolve();
|
|
225
|
+
resolve = null;
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
[Symbol.asyncIterator]() {
|
|
231
|
+
return {
|
|
232
|
+
next: async () => {
|
|
233
|
+
while (events.length === 0) {
|
|
234
|
+
await new Promise(r => resolve = r);
|
|
235
|
+
}
|
|
236
|
+
return { value: events.shift(), done: false };
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
close: unsubscribe
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ============================================================
|
|
245
|
+
// 插件系统
|
|
246
|
+
// ============================================================
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 注册插件
|
|
250
|
+
* @param {object} plugin - { name, install(db) }
|
|
251
|
+
*/
|
|
252
|
+
use(plugin) {
|
|
253
|
+
if (typeof plugin.install !== 'function') {
|
|
254
|
+
throw new Error('Plugin must have an install() method');
|
|
255
|
+
}
|
|
256
|
+
this._plugins.push(plugin);
|
|
257
|
+
plugin.install(this);
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ============================================================
|
|
262
|
+
// 迁移
|
|
101
263
|
// ============================================================
|
|
102
264
|
|
|
103
265
|
/**
|
|
104
|
-
*
|
|
266
|
+
* 注册迁移
|
|
267
|
+
* @param {object} migration - { version, name, up(db), down(db) }
|
|
105
268
|
*/
|
|
269
|
+
addMigration(migration) {
|
|
270
|
+
this._migrations.push(migration);
|
|
271
|
+
this._migrations.sort((a, b) => a.version - b.version);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 执行迁移到指定版本
|
|
276
|
+
*/
|
|
277
|
+
migrate(targetVersion) {
|
|
278
|
+
const current = this._getCurrentVersion();
|
|
279
|
+
if (targetVersion === undefined) {
|
|
280
|
+
targetVersion = this._migrations.length > 0
|
|
281
|
+
? this._migrations[this._migrations.length - 1].version
|
|
282
|
+
: 0;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (targetVersion > current) {
|
|
286
|
+
for (const m of this._migrations) {
|
|
287
|
+
if (m.version > current && m.version <= targetVersion) {
|
|
288
|
+
m.up(this);
|
|
289
|
+
this._setCurrentVersion(m.version);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} else if (targetVersion < current) {
|
|
293
|
+
for (const m of this._migrations.reverse()) {
|
|
294
|
+
if (m.version <= current && m.version > targetVersion) {
|
|
295
|
+
m.down(this);
|
|
296
|
+
this._setCurrentVersion(m.version - 1);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
_getCurrentVersion() {
|
|
303
|
+
return this._tables['__meta__']
|
|
304
|
+
? (this._tables['__meta__'].findOne({ key: 'version' })?.value || 0)
|
|
305
|
+
: 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
_setCurrentVersion(version) {
|
|
309
|
+
if (!this._tables['__meta__']) {
|
|
310
|
+
this.createTable('__meta__', {
|
|
311
|
+
key: { type: 'string', primaryKey: true },
|
|
312
|
+
value: { type: 'any' }
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
this._tables['__meta__'].upsert({ key: 'version', value: version });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ============================================================
|
|
319
|
+
// 备份与恢复
|
|
320
|
+
// ============================================================
|
|
321
|
+
|
|
322
|
+
backup(filePath) {
|
|
323
|
+
const data = this.export();
|
|
324
|
+
const json = this._options.pretty
|
|
325
|
+
? JSON.stringify(data, null, 2)
|
|
326
|
+
: JSON.stringify(data);
|
|
327
|
+
this._writeFile(filePath, json);
|
|
328
|
+
return filePath;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
restore(filePath) {
|
|
332
|
+
if (!fs.existsSync(filePath)) {
|
|
333
|
+
throw new Error(`Backup file not found: ${filePath}`);
|
|
334
|
+
}
|
|
335
|
+
const json = this._readFile(filePath);
|
|
336
|
+
const data = JSON.parse(json);
|
|
337
|
+
for (const [name, rows] of Object.entries(data)) {
|
|
338
|
+
if (this._tables[name]) {
|
|
339
|
+
this._tables[name]._loadRows(rows);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
this._markDirty();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// ============================================================
|
|
346
|
+
// 持久化
|
|
347
|
+
// ============================================================
|
|
348
|
+
|
|
106
349
|
save() {
|
|
350
|
+
// 版本快照(内存模式也支持)
|
|
351
|
+
if (this._versioning) {
|
|
352
|
+
for (const name of Object.keys(this._tables)) {
|
|
353
|
+
this.snapshot(name);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
107
357
|
if (this._memoryMode) return;
|
|
108
358
|
|
|
109
|
-
const data = { __schema__: {} };
|
|
359
|
+
const data = { __schema__: {}, __meta__: { version: this._getCurrentVersion() } };
|
|
110
360
|
for (const [name, table] of Object.entries(this._tables)) {
|
|
361
|
+
if (name === '__meta__') continue;
|
|
111
362
|
data.__schema__[name] = table._schema;
|
|
112
363
|
data[name] = table.toJSON();
|
|
113
364
|
}
|
|
114
365
|
|
|
115
|
-
const dir = path.dirname(this._filePath);
|
|
116
|
-
if (!fs.existsSync(dir)) {
|
|
117
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
118
|
-
}
|
|
119
|
-
|
|
120
366
|
const json = this._options.pretty
|
|
121
367
|
? JSON.stringify(data, null, 2)
|
|
122
368
|
: JSON.stringify(data);
|
|
123
|
-
|
|
369
|
+
this._writeFile(this._filePath, json);
|
|
124
370
|
this._dirty = false;
|
|
125
371
|
}
|
|
126
372
|
|
|
127
|
-
/**
|
|
128
|
-
* 从文件加载
|
|
129
|
-
*/
|
|
130
373
|
_load() {
|
|
131
374
|
try {
|
|
132
|
-
const json =
|
|
375
|
+
const json = this._readFile(this._filePath);
|
|
133
376
|
const data = JSON.parse(json);
|
|
134
377
|
|
|
135
|
-
// 先根据 schema 重建表
|
|
136
378
|
if (data.__schema__) {
|
|
137
379
|
for (const [name, schema] of Object.entries(data.__schema__)) {
|
|
138
380
|
if (!this._tables[name]) {
|
|
@@ -141,9 +383,8 @@ class Database {
|
|
|
141
383
|
}
|
|
142
384
|
}
|
|
143
385
|
|
|
144
|
-
// 加载数据
|
|
145
386
|
for (const [name, rows] of Object.entries(data)) {
|
|
146
|
-
if (name !== '__schema__' && this._tables[name]) {
|
|
387
|
+
if (name !== '__schema__' && name !== '__meta__' && this._tables[name]) {
|
|
147
388
|
this._tables[name]._loadRows(rows);
|
|
148
389
|
}
|
|
149
390
|
}
|
|
@@ -152,12 +393,9 @@ class Database {
|
|
|
152
393
|
}
|
|
153
394
|
}
|
|
154
395
|
|
|
155
|
-
/**
|
|
156
|
-
* 标记脏数据(触发自动保存)
|
|
157
|
-
*/
|
|
158
396
|
_markDirty() {
|
|
159
397
|
this._dirty = true;
|
|
160
|
-
if (this._loading) return;
|
|
398
|
+
if (this._loading) return;
|
|
161
399
|
if (this._options.autoSave && !this._autoSaveTimer) {
|
|
162
400
|
if (this._options.autoSaveInterval > 0) {
|
|
163
401
|
this._autoSaveTimer = setTimeout(() => {
|
|
@@ -165,15 +403,11 @@ class Database {
|
|
|
165
403
|
if (this._dirty) this.save();
|
|
166
404
|
}, this._options.autoSaveInterval);
|
|
167
405
|
} else {
|
|
168
|
-
// 立即保存(同步)
|
|
169
406
|
this.save();
|
|
170
407
|
}
|
|
171
408
|
}
|
|
172
409
|
}
|
|
173
410
|
|
|
174
|
-
/**
|
|
175
|
-
* 关闭数据库(保存并清理)
|
|
176
|
-
*/
|
|
177
411
|
close() {
|
|
178
412
|
if (this._autoSaveTimer) {
|
|
179
413
|
clearTimeout(this._autoSaveTimer);
|
|
@@ -185,20 +419,15 @@ class Database {
|
|
|
185
419
|
this._tables = {};
|
|
186
420
|
}
|
|
187
421
|
|
|
188
|
-
/**
|
|
189
|
-
* 导出数据库为 JSON 对象
|
|
190
|
-
*/
|
|
191
422
|
export() {
|
|
192
423
|
const data = {};
|
|
193
424
|
for (const [name, table] of Object.entries(this._tables)) {
|
|
425
|
+
if (name === '__meta__') continue;
|
|
194
426
|
data[name] = table.toJSON();
|
|
195
427
|
}
|
|
196
428
|
return data;
|
|
197
429
|
}
|
|
198
430
|
|
|
199
|
-
/**
|
|
200
|
-
* 从 JSON 对象导入数据
|
|
201
|
-
*/
|
|
202
431
|
import(data) {
|
|
203
432
|
for (const [name, rows] of Object.entries(data)) {
|
|
204
433
|
if (this._tables[name]) {
|
|
@@ -207,6 +436,35 @@ class Database {
|
|
|
207
436
|
}
|
|
208
437
|
this._markDirty();
|
|
209
438
|
}
|
|
439
|
+
|
|
440
|
+
// ============================================================
|
|
441
|
+
// 加密(内部)
|
|
442
|
+
// ============================================================
|
|
443
|
+
|
|
444
|
+
_writeFile(filePath, content) {
|
|
445
|
+
if (this._encryptKey) {
|
|
446
|
+
const iv = crypto.randomBytes(16);
|
|
447
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(this._encryptKey.padEnd(32).slice(0, 32)), iv);
|
|
448
|
+
const encrypted = Buffer.concat([cipher.update(content, 'utf8'), cipher.final()]);
|
|
449
|
+
const data = Buffer.concat([iv, encrypted]);
|
|
450
|
+
fs.writeFileSync(filePath, data.toString('base64'), 'utf8');
|
|
451
|
+
} else {
|
|
452
|
+
const dir = path.dirname(filePath);
|
|
453
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
454
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
_readFile(filePath) {
|
|
459
|
+
if (this._encryptKey) {
|
|
460
|
+
const data = Buffer.from(fs.readFileSync(filePath, 'utf8'), 'base64');
|
|
461
|
+
const iv = data.slice(0, 16);
|
|
462
|
+
const encrypted = data.slice(16);
|
|
463
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(this._encryptKey.padEnd(32).slice(0, 32)), iv);
|
|
464
|
+
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf8');
|
|
465
|
+
}
|
|
466
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
467
|
+
}
|
|
210
468
|
}
|
|
211
469
|
|
|
212
470
|
module.exports = Database;
|