-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrulesLoader.ts
More file actions
254 lines (210 loc) · 6.74 KB
/
rulesLoader.ts
File metadata and controls
254 lines (210 loc) · 6.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// src/services/rulesLoader.ts
// 规则库加载管理 - IndexedDB 缓存 + 自动更新
import { RulesBundle } from '../types';
const DB_NAME = 'apk_analyzer_db';
const DB_VERSION = 1;
const STORE_NAME = 'rules_bundle';
const LOCAL_VERSION_KEY = 'rules_local_version';
function getLocalStorage(): Storage | null {
try {
if (typeof localStorage === 'undefined') {
return null;
}
return localStorage;
} catch {
return null;
}
}
/**
* 加载规则库
* 优先从 IndexedDB 加载,如果不存在则从 public/ 目录加载
*/
export async function loadRules(): Promise<RulesBundle | null> {
console.log('📂 开始加载规则库...');
try {
// 1. 尝试从 IndexedDB 加载
const cachedRules = await loadRulesFromIndexedDB();
if (cachedRules) {
console.log(`✓ 从 IndexedDB 加载规则库成功 (版本: ${cachedRules.version})`);
return cachedRules;
}
// 2. 从 public/ 目录加载
console.log('⬇️ 从服务器加载规则库...');
const response = await fetch('/rules-bundle.json');
if (!response.ok) {
throw new Error(`加载失败: ${response.status} ${response.statusText}`);
}
const rules: RulesBundle = await response.json();
console.log(`✓ 从服务器加载规则库成功 (版本: ${rules.version})`);
// 3. 存储到 IndexedDB
await saveRulesToIndexedDB(rules);
getLocalStorage()?.setItem(LOCAL_VERSION_KEY, rules.version);
return rules;
} catch (error) {
console.error('❌ 加载规则库失败:', error);
return null;
}
}
/**
* 从 IndexedDB 加载规则库
*/
async function loadRulesFromIndexedDB(): Promise<RulesBundle | null> {
return new Promise((resolve) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onerror = () => {
console.warn('IndexedDB 打开失败:', request.error);
resolve(null);
};
request.onsuccess = () => {
const db = request.result;
// 检查对象存储是否存在
if (!db.objectStoreNames.contains(STORE_NAME)) {
resolve(null);
return;
}
const transaction = db.transaction([STORE_NAME], 'readonly');
const store = transaction.objectStore(STORE_NAME);
const getRequest = store.get('bundle');
getRequest.onsuccess = () => {
if (getRequest.result) {
resolve(getRequest.result.data);
} else {
resolve(null);
}
};
getRequest.onerror = () => {
console.warn('IndexedDB 读取失败:', getRequest.error);
resolve(null);
};
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
}
};
});
}
/**
* 保存规则库到 IndexedDB
*/
async function saveRulesToIndexedDB(rules: RulesBundle): Promise<void> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
const db = request.result;
const transaction = db.transaction([STORE_NAME], 'readwrite');
const store = transaction.objectStore(STORE_NAME);
// 清空旧数据
store.clear();
// 存储新数据
store.put({ id: 'bundle', data: rules });
transaction.oncomplete = () => {
console.log('✓ 规则库已缓存到 IndexedDB');
resolve();
};
transaction.onerror = () => reject(transaction.error);
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
}
};
});
}
/**
* 检查并更新规则库(可选功能)
* 如果部署了云端版本,可以启用此功能
*/
export async function checkAndUpdateRules(
versionUrl: string = '/rules-version.json'
): Promise<boolean> {
try {
// 1. 获取本地版本
const localVersion = getLocalStorage()?.getItem(LOCAL_VERSION_KEY);
// 2. 获取云端版本信息
const response = await fetch(versionUrl);
if (!response.ok) {
console.warn('无法检查规则库版本');
return false;
}
const cloudVersionInfo = await response.json();
const { version: cloudVersion, bundleUrl } = cloudVersionInfo;
console.log(`本地版本: ${localVersion || '无'}`);
console.log(`云端版本: ${cloudVersion}`);
// 3. 对比版本
if (localVersion === cloudVersion) {
console.log('✓ 规则库已是最新版本');
return false;
}
// 4. 下载最新规则库
console.log('⬇️ 正在下载最新规则库...');
const bundleResponse = await fetch(bundleUrl);
const rulesBundle: RulesBundle = await bundleResponse.json();
// 5. 存储到 IndexedDB
await saveRulesToIndexedDB(rulesBundle);
// 6. 更新本地版本号
getLocalStorage()?.setItem(LOCAL_VERSION_KEY, cloudVersion);
console.log('✅ 规则库更新成功!');
return true;
} catch (error) {
console.error('❌ 规则库更新失败:', error);
return false;
}
}
/**
* 清除规则库缓存
*/
export async function clearRulesCache(): Promise<void> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
const db = request.result;
const transaction = db.transaction([STORE_NAME], 'readwrite');
const store = transaction.objectStore(STORE_NAME);
store.clear();
transaction.oncomplete = () => {
getLocalStorage()?.removeItem(LOCAL_VERSION_KEY);
console.log('✓ 规则库缓存已清除');
resolve();
};
transaction.onerror = () => reject(transaction.error);
};
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
}
};
});
}
/**
* 获取规则库信息
*/
export async function getRulesInfo(): Promise<{
version: string | null;
totalRules: number;
cacheSize: number;
lastUpdated: string | null;
} | null> {
try {
const rules = await loadRulesFromIndexedDB();
if (!rules) {
return null;
}
// 估算缓存大小(粗略估计)
const cacheSize = new Blob([JSON.stringify(rules)]).size;
return {
version: rules.version,
totalRules: rules.totalRules,
cacheSize,
lastUpdated: rules.generatedAt,
};
} catch (error) {
console.error('获取规则库信息失败:', error);
return null;
}
}