Skip to content

Commit 5474904

Browse files
committed
重构CookieStore
1 parent c3579f3 commit 5474904

10 files changed

Lines changed: 69 additions & 270 deletions

File tree

server/config.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

server/kv/cookie.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
1-
export interface CookieEntry {
2-
key: string;
3-
token: string;
4-
cookie: string;
5-
}
1+
import { type CookieEntity } from '~/server/utils/CookieStore';
62

7-
export async function getCookie(key: string): Promise<CookieEntry | null> {
3+
export async function setMpCookie(data: Record<string, CookieEntity[]>): Promise<boolean> {
84
const kv = useStorage('kv');
9-
return await kv.get<CookieEntry>(`cookies:${key}`);
5+
await kv.set<Record<string, CookieEntity[]>>('cookies', data);
6+
return true;
107
}
118

12-
export async function setCookie(cookie: CookieEntry) {
9+
export async function getMpCookie(): Promise<Record<string, CookieEntity[]> | null> {
1310
const kv = useStorage('kv');
14-
await kv.set<CookieEntry>(`cookies:${cookie.key}`, cookie, {
15-
expirationTtl: 60 * 60 * 24 * 3.8,
16-
});
17-
return true;
11+
return await kv.get<Record<string, CookieEntity[]>>('cookies');
1812
}

server/kv/token.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export async function setMpToken(data: Record<string, string>): Promise<boolean> {
2+
const kv = useStorage('kv');
3+
await kv.set<Record<string, string>>('tokens', data);
4+
return true;
5+
}
6+
7+
export async function getMpToken(): Promise<Record<string, string> | null> {
8+
const kv = useStorage('kv');
9+
return await kv.get<Record<string, string>>('tokens');
10+
}

server/kv/user.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

server/utils/CookieStore.ts

Lines changed: 47 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import { H3Event, parseCookies } from 'h3';
2-
import { readFileSync, writeFileSync } from 'node:fs';
3-
import fs from 'node:fs';
4-
import path from 'node:path';
5-
import { root } from '~/server/config';
2+
import { getMpCookie, setMpCookie } from '~/server/kv/cookie';
3+
import { getMpToken, setMpToken } from '~/server/kv/token';
64

75
// 表示一条 set-cookie 记录的解析结果
8-
type TCookie = Record<string, string | number>;
6+
export type CookieEntity = Record<string, string | number>;
97

10-
// 公众号对应的 cookie 对象 (已解析)
8+
// 公众号所有的 set-cookie 解析结果
119
export class AccountCookie {
12-
private readonly _cookie: TCookie[];
10+
private readonly _cookie: CookieEntity[];
1311

12+
/**
13+
* @param cookies response.headers.getSetCookie() 的结果,是一个字符串数组
14+
*/
1415
constructor(cookies: string[]) {
1516
this._cookie = this.parse(cookies);
1617
}
@@ -19,11 +20,11 @@ export class AccountCookie {
1920
return this.stringify(this._cookie);
2021
}
2122

22-
public toJSON(): TCookie[] {
23+
public toJSON(): CookieEntity[] {
2324
return this._cookie;
2425
}
2526

26-
public get(name: string): TCookie | undefined {
27+
public get(name: string): CookieEntity | undefined {
2728
return this._cookie.find(cookie => cookie.name === name);
2829
}
2930

@@ -33,13 +34,13 @@ export class AccountCookie {
3334
return false;
3435
}
3536

36-
private parse(cookies: string[]): TCookie[] {
37-
// Use a Map to store cookies by name, ensuring the last occurrence overwrites duplicates
38-
const cookieMap = new Map<string, TCookie>();
37+
private parse(cookies: string[]): CookieEntity[] {
38+
// key 为 cookie 的 name
39+
const cookieMap = new Map<string, CookieEntity>();
3940

4041
for (const cookie of cookies) {
41-
const cookieObj: TCookie = {};
42-
// 分割cookie字符串为各个属性
42+
const cookieObj: CookieEntity = {};
43+
// 分割 cookie 字符串为各个属性
4344
const parts = cookie.split(';').map(str => str.trim());
4445

4546
// 第一个部分是name=value
@@ -79,11 +80,10 @@ export class AccountCookie {
7980
}
8081
}
8182

82-
// Convert Map values to array
8383
return Array.from(cookieMap.values());
8484
}
8585

86-
private stringify(parsedCookie: TCookie[]): string {
86+
private stringify(parsedCookie: CookieEntity[]): string {
8787
return parsedCookie
8888
.filter(cookie => cookie.value && cookie.value !== 'EXPIRED')
8989
.map(cookie => `${cookie.name}=${cookie.value}`)
@@ -94,17 +94,7 @@ export class AccountCookie {
9494
// 所有用户的 cookie 仓库
9595
class CookieStore {
9696
store!: Map<string, AccountCookie>;
97-
private readonly cookieDumpFilePath: string;
98-
9997
token!: Map<string, string>;
100-
private readonly tokenDumpFilePath: string;
101-
102-
constructor() {
103-
this.cookieDumpFilePath = path.resolve(root, '.data/cookies.json');
104-
this.tokenDumpFilePath = path.resolve(root, '.data/tokens.json');
105-
106-
this.load();
107-
}
10898

10999
/**
110100
* 检索用户的cookie
@@ -126,7 +116,7 @@ class CookieStore {
126116
*/
127117
setCookie(authKey: string, cookie: string[]) {
128118
this.store.set(authKey, new AccountCookie(cookie));
129-
this.dumpCookies();
119+
this.dumpCookies().catch(e => console.error(e));
130120
}
131121

132122
/**
@@ -137,23 +127,23 @@ class CookieStore {
137127
updateCookie(authKey: string, newCookies: string[]) {
138128
// Parse new cookies
139129
const newAccountCookie = new AccountCookie(newCookies);
140-
const newCookieMap = new Map<string, TCookie>(
130+
const newCookieMap = new Map<string, CookieEntity>(
141131
newAccountCookie.toJSON().map(cookie => [cookie.name as string, cookie])
142132
);
143133

144134
// Get existing cookies, if any
145135
const existingCookieObj = this.store.get(authKey);
146-
let mergedCookies: TCookie[] = [];
136+
let mergedCookies: CookieEntity[];
147137

148138
if (existingCookieObj) {
149139
// Merge existing cookies with new ones, new cookies override duplicates
150140
const existingCookies = existingCookieObj.toJSON();
151-
const existingCookieMap = new Map<string, TCookie>(
141+
const existingCookieMap = new Map<string, CookieEntity>(
152142
existingCookies.map(cookie => [cookie.name as string, cookie])
153143
);
154144

155145
// Keep all existing cookies, overwrite with new ones if they exist
156-
mergedCookies = Array.from(existingCookieMap.entries()).reduce<TCookie[]>((acc, [name, cookie]) => {
146+
mergedCookies = Array.from(existingCookieMap.entries()).reduce<CookieEntity[]>((acc, [name, cookie]) => {
157147
if (newCookieMap.has(name)) {
158148
// Use new cookie if it exists
159149
acc.push(newCookieMap.get(name)!);
@@ -185,7 +175,7 @@ class CookieStore {
185175

186176
// Update store with merged cookies
187177
this.store.set(authKey, new AccountCookie(mergedCookieStrings));
188-
this.dumpCookies();
178+
this.dumpCookies().catch(e => console.error(e));
189179
}
190180

191181
/**
@@ -203,38 +193,36 @@ class CookieStore {
203193
*/
204194
bindToken(authKey: string, token: string) {
205195
this.token.set(authKey, token);
206-
this.dumpTokens();
196+
this.dumpTokens().catch(e => console.error(e));
207197
}
208198

209199
/**
210200
* 转换为 json 格式,方便存储与传输
211201
* 返回一个对象,键为 uuid,值为解析后的 cookie 对象
212202
*/
213-
toJSON(): Record<string, TCookie[]> {
214-
const json: Record<string, TCookie[]> = {};
203+
toJSON(): Record<string, CookieEntity[]> {
204+
const json: Record<string, CookieEntity[]> = {};
215205
for (const [authKey, cookieObj] of this.store) {
216206
json[authKey] = cookieObj.toJSON();
217207
}
218208
return json;
219209
}
220210

221211
// 从文件中加载
222-
load(): void {
223-
this.loadCookies();
224-
this.loadTokens();
212+
async load() {
213+
await Promise.all([this.loadCookies(), this.loadTokens()]);
225214
}
226215

227-
loadCookies() {
216+
async loadCookies() {
228217
this.store = new Map<string, AccountCookie>();
229218

230219
try {
231-
if (!fs.existsSync(this.cookieDumpFilePath)) {
220+
const data = await getMpCookie();
221+
if (!data) {
232222
return;
233223
}
234224

235-
const data = readFileSync(this.cookieDumpFilePath, 'utf-8');
236-
const json = JSON.parse(data) as Record<string, TCookie[]>;
237-
for (const [authKey, cookies] of Object.entries(json)) {
225+
for (const [authKey, cookies] of Object.entries(data)) {
238226
// Reconstruct cookie strings from parsed objects
239227
const cookieStrings = cookies.map(cookie => {
240228
const parts = [`${cookie.name}=${cookie.value}`];
@@ -252,44 +240,49 @@ class CookieStore {
252240
}
253241
}
254242

255-
loadTokens() {
243+
async loadTokens() {
256244
this.token = new Map<string, string>();
257245

258246
try {
259-
if (!fs.existsSync(this.tokenDumpFilePath)) {
247+
const data = await getMpToken();
248+
if (!data) {
260249
return;
261250
}
262251

263-
const data = readFileSync(this.tokenDumpFilePath, 'utf-8');
264-
const json = JSON.parse(data) as Record<string, string>;
265-
for (const [authKey, token] of Object.entries(json)) {
252+
for (const [authKey, token] of Object.entries(data)) {
266253
this.token.set(authKey, token);
267254
}
268255
} catch (error) {
269256
console.error('Failed to load token store:', error);
270257
}
271258
}
272259

273-
dumpCookies() {
260+
async dumpCookies() {
274261
try {
275-
const json = JSON.stringify(this.toJSON(), null, 2);
276-
writeFileSync(this.cookieDumpFilePath, json, 'utf-8');
262+
await setMpCookie(this.toJSON());
277263
} catch (error) {
278264
console.error('Failed to save cookie store:', error);
279265
}
280266
}
281267

282-
dumpTokens() {
268+
async dumpTokens() {
283269
try {
284-
const json = JSON.stringify(Object.fromEntries(this.token), null, 2);
285-
writeFileSync(this.tokenDumpFilePath, json, 'utf-8');
270+
await setMpToken(Object.fromEntries(this.token));
286271
} catch (error) {
287272
console.error('Failed to save token store:', error);
288273
}
289274
}
290275
}
291276

292277
export const cookieStore = new CookieStore();
278+
cookieStore
279+
.load()
280+
.then(() => {
281+
console.log('cookie store load success.');
282+
})
283+
.catch(e => {
284+
console.error('cookie store load failed.', e);
285+
});
293286

294287
/**
295288
* 从 CookieStore 中获取 cookie 字符串

server/utils/cookie.ts

Lines changed: 0 additions & 70 deletions
This file was deleted.

0 commit comments

Comments
 (0)