feat-ai(storage.js): 添加nodejs环境下的sqlite存储支持,调用方法与浏览器环境下保存一致

This commit is contained in:
windychen0
2026-06-06 19:51:30 +08:00
parent 4f8be6293d
commit 3994bca0ab
7 changed files with 418 additions and 90 deletions
+204 -46
View File
@@ -7,7 +7,9 @@ var log = console.warn;
* @returns {boolean}
* @private
*/
var isString = function (str) { return Object.getTypeString(str) === 'String'; };
var isString = function (str) {
return Object.getTypeString(str) === "String";
};
/**
* IndexedDB 存储实现(大容量存储优先选择)
@@ -29,24 +31,37 @@ class IDBStorage {
/** @type {number} 数据库版本号 */
this.version = version || 1;
/** @type {string} 数据库名称 */
this.dbName = dbName || 'dbName';
this.dbName = dbName || "dbName";
/** @type {string} 对象存储名称 */
this.storeName = storeName || 'storeName';
this.storeName = storeName || "storeName";
var request = indexedDB.open(this.dbName, this.version);
/** @type {Promise<IDBDatabase>} 数据库连接 Promise */
this.requestPromise = new Promise(function (resolve, reject) {
request.onupgradeneeded = function (e) {
if (e && e.target && !request.result.objectStoreNames.contains(this.storeName)) {
request.result.createObjectStore(this.storeName, { keyPath: 'key' })
.createIndex('key', 'key', { unique: false });
}
log('[Index][open]-upgrade');
resolve(request.result);
}.bind(this);
request.onsuccess = function () { log('[IndexDB][open]-success'); resolve(request.result); };
request.onerror = function (e) { log('[IndexDB][open]-error', 2); reject(e); };
}.bind(this));
this.requestPromise = new Promise(
function (resolve, reject) {
request.onupgradeneeded = function (e) {
if (
e &&
e.target &&
!request.result.objectStoreNames.contains(this.storeName)
) {
request.result
.createObjectStore(this.storeName, { keyPath: "key" })
.createIndex("key", "key", { unique: false });
}
log("[Index][open]-upgrade");
resolve(request.result);
}.bind(this);
request.onsuccess = function () {
log("[IndexDB][open]-success");
resolve(request.result);
};
request.onerror = function (e) {
log("[IndexDB][open]-error", 2);
reject(e);
};
}.bind(this),
);
}
/**
@@ -60,11 +75,16 @@ class IDBStorage {
var _this = this;
return new Promise(async function (resolve) {
var db = await _this.requestPromise;
var transaction = db.transaction(_this.storeName, 'readonly');
var transaction = db.transaction(_this.storeName, "readonly");
var objectStore = transaction.objectStore(_this.storeName);
var req = objectStore.get(key);
req.onsuccess = function () { resolve(req.result ? req.result.value : (initValue || null)); };
req.onerror = function (e) { log('[indexDB][get]-error', 2, e); resolve(initValue || null); };
req.onsuccess = function () {
resolve(req.result ? req.result.value : initValue || null);
};
req.onerror = function (e) {
log("[indexDB][get]-error", 2, e);
resolve(initValue || null);
};
});
}
@@ -79,11 +99,16 @@ class IDBStorage {
var _this = this;
return new Promise(async function (resolve) {
var db = await _this.requestPromise;
var transaction = db.transaction(_this.storeName, 'readwrite');
var transaction = db.transaction(_this.storeName, "readwrite");
var objectStore = transaction.objectStore(_this.storeName);
var req = objectStore.put({ key: key, value: value });
req.onsuccess = function () { resolve(value); };
req.onerror = function (e) { log('[indexDB][set]-error', 2, e); resolve(value); };
req.onsuccess = function () {
resolve(value);
};
req.onerror = function (e) {
log("[indexDB][set]-error", 2, e);
resolve(value);
};
});
}
}
@@ -108,11 +133,12 @@ class ILocalStorage {
load() {
var _this = this;
Object.entries(localStorage).forEach(function (entry) {
var key = entry[0], value = entry[1];
var key = entry[0],
value = entry[1];
try {
_this.cache.set(key, JSON.parse(value));
} catch (error) {
log('[localStorage][load]-error', 2, error, key, value);
log("[localStorage][load]-error", 2, error, key, value);
}
});
}
@@ -125,9 +151,13 @@ class ILocalStorage {
* @returns {Promise<T>} 缓存的值或默认值
*/
get(key, initValue) {
return new Promise(function (resolve) {
resolve(this.cache.get(key) !== undefined ? this.cache.get(key) : initValue);
}.bind(this));
return new Promise(
function (resolve) {
resolve(
this.cache.get(key) !== undefined ? this.cache.get(key) : initValue,
);
}.bind(this),
);
}
/**
@@ -138,31 +168,159 @@ class ILocalStorage {
* @returns {Promise<T>} 返回写入的值
*/
set(key, value) {
return new Promise(function (resolve) {
var _value = isString(value) ? value : '';
try {
_value = JSON.stringify(value);
} catch (e) {
log('[localStorage][set]-error', 2, e);
}
localStorage.setItem(key, _value);
this.cache.set(key, _value);
resolve(value);
}.bind(this));
return new Promise(
function (resolve) {
var _value = isString(value) ? value : "";
try {
_value = JSON.stringify(value);
} catch (e) {
log("[localStorage][set]-error", 2, e);
}
localStorage.setItem(key, _value);
this.cache.set(key, _value);
resolve(value);
}.bind(this),
);
}
}
// 浏览器环境自动选择存储方式
if (typeof window !== 'undefined') {
/**
* 纯内存存储实现(无持久化,适用于所有环境兜底)
*
* @class MemoryStorage
* @example
* const storage = new MemoryStorage();
* await storage.set('key', { data: 789 });
* await storage.get('key', null);
*/
class MemoryStorage {
constructor() {
/** @type {Map<string, *>} 内存缓存 */
this.cache = new Map();
}
/**
* 当前环境可用的存储类(IDBStorage 或 ILocalStorage
* @type {Function|null}
* @global
* 从内存获取值
* @template T
* @param {string} key - 键名
* @param {T} initValue - 默认值
* @returns {Promise<T>} 缓存的值或默认值
*/
window.IStorage = window.indexedDB ? IDBStorage : (window.localStorage ? ILocalStorage : null);
if (typeof module !== 'undefined') {
get(key, initValue) {
return Promise.resolve(
this.cache.has(key)
? this.cache.get(key)
: initValue !== undefined
? initValue
: null,
);
}
/**
* 写入内存
* @template T
* @param {string} key - 键名
* @param {T} value - 要存储的值
* @returns {Promise<T>} 返回写入的值
*/
set(key, value) {
this.cache.set(key, value);
return Promise.resolve(value);
}
}
/**
* Node.js SQLite 持久化存储实现
* 仅在 Node.js 环境下可用,浏览器/webpack 打包中为 null
*
* @class SqliteStorage
* @example
* const storage = new SqliteStorage('./data/storage.db');
* await storage.set('key', { data: 123 });
* await storage.get('key', null);
* @private
*/
var _SqliteStorageClass = function () {
var Database = require("better-sqlite3");
var pathModule = require("path");
var fs = require("fs");
return class SqliteStorage {
/**
* 创建 SQLite 存储实例
* @param {string} [dbPath='.windychen-storage/storage.db'] - 数据库文件路径
*/
constructor(dbPath) {
this.dbPath =
dbPath || pathModule.resolve(".windychen-storage", "storage.db");
var dir = pathModule.dirname(this.dbPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
this.db = new Database(this.dbPath);
this.db.pragma("journal_mode = WAL");
this.db.exec(
"CREATE TABLE IF NOT EXISTS kv_store (key TEXT PRIMARY KEY, value TEXT)",
);
}
/**
* 获取存储值
* @template T
* @param {string} key - 键名
* @param {T} initValue - 默认值(未找到时返回)
* @returns {Promise<T>} 存储的值或默认值
*/
async get(key, initValue) {
var row = this.db
.prepare("SELECT value FROM kv_store WHERE key = ?")
.get(key);
if (row) {
try {
return JSON.parse(row.value);
} catch (e) {
log("[SqliteStorage][get]-parse-error", 2, e);
}
}
return initValue !== undefined ? initValue : null;
}
/**
* 设置存储值
* @template T
* @param {string} key - 键名
* @param {T} value - 要存储的值
* @returns {Promise<T>} 返回写入的值
*/
async set(key, value) {
var json = JSON.stringify(value);
this.db
.prepare("INSERT OR REPLACE INTO kv_store (key, value) VALUES (?, ?)")
.run(key, json);
return value;
}
};
};
var SqliteStorage = null;
try {
SqliteStorage = _SqliteStorageClass();
} catch (e) {
// better-sqlite3 不可用(浏览器 / webpack 打包环境),SqliteStorage 保持为 null
}
// ===== 环境检测与自动导出 =====
if (typeof window !== "undefined") {
// 浏览器环境:优先 IndexedDB → localStorage → 内存
window.IStorage = window.indexedDB
? IDBStorage
: window.localStorage
? ILocalStorage
: MemoryStorage;
if (typeof module !== "undefined") {
module.exports = new window.IStorage();
}
} else if (typeof module !== 'undefined') {
module.exports = null;
} else if (typeof module !== "undefined") {
// Node.js 环境:SQLite 存储 → 内存
module.exports = SqliteStorage ? new SqliteStorage() : new MemoryStorage();
}