后端迁移会话存储模块
This commit is contained in:
+60
-15
@@ -1,5 +1,3 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
import crypto from 'node:crypto'
|
import crypto from 'node:crypto'
|
||||||
import fs from 'node:fs/promises'
|
import fs from 'node:fs/promises'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
@@ -14,9 +12,53 @@ import {
|
|||||||
SESSION_TTL_MS,
|
SESSION_TTL_MS,
|
||||||
} from './session-state.js'
|
} from './session-state.js'
|
||||||
|
|
||||||
const sessions = new Map()
|
type BrowserContextLike = {
|
||||||
|
close?: () => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
export async function registerTencentBrowserSession({ loginType, browserContext, page }) {
|
export type TencentBrowserSession = Record<string, any> & {
|
||||||
|
sessionId: string
|
||||||
|
loginType: string
|
||||||
|
status: string
|
||||||
|
notice: string
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
expiresAt: number
|
||||||
|
autoCloseAt: number
|
||||||
|
autoCloseTimer: ReturnType<typeof setTimeout> | null
|
||||||
|
sessionDir: string
|
||||||
|
browserContext?: BrowserContextLike | null
|
||||||
|
page?: unknown
|
||||||
|
browserContextClosed?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegisterSessionInput = {
|
||||||
|
loginType: string
|
||||||
|
browserContext: BrowserContextLike
|
||||||
|
page: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloseSessionDependency = {
|
||||||
|
closeSession: (session: TencentBrowserSession, options: { markClosed: boolean }) => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloseSessionOptions = {
|
||||||
|
markClosed: boolean
|
||||||
|
persistSessionState: (session: TencentBrowserSession) => Promise<unknown>
|
||||||
|
closeBrowserIfIdle: () => Promise<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
type PersistSessionStateOptions = {
|
||||||
|
buildSessionPayload: (session: TencentBrowserSession) => unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessions = new Map<string, TencentBrowserSession>()
|
||||||
|
|
||||||
|
export async function registerTencentBrowserSession({
|
||||||
|
loginType,
|
||||||
|
browserContext,
|
||||||
|
page,
|
||||||
|
}: RegisterSessionInput): Promise<TencentBrowserSession> {
|
||||||
const sessionId = createSessionId()
|
const sessionId = createSessionId()
|
||||||
const sessionDir = path.join(DATA_ROOT, sessionId)
|
const sessionDir = path.join(DATA_ROOT, sessionId)
|
||||||
await fs.mkdir(sessionDir, { recursive: true })
|
await fs.mkdir(sessionDir, { recursive: true })
|
||||||
@@ -49,11 +91,11 @@ export async function registerTencentBrowserSession({ loginType, browserContext,
|
|||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listActiveTencentBrowserSessions() {
|
export function listActiveTencentBrowserSessions(): TencentBrowserSession[] {
|
||||||
return [...sessions.values()]
|
return [...sessions.values()]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRequiredSession(sessionId) {
|
export async function getRequiredSession(sessionId: unknown): Promise<TencentBrowserSession> {
|
||||||
const key = String(sessionId || '').trim()
|
const key = String(sessionId || '').trim()
|
||||||
|
|
||||||
if (!key) {
|
if (!key) {
|
||||||
@@ -75,12 +117,12 @@ export async function getRequiredSession(sessionId) {
|
|||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyInitialQrSessionState(session) {
|
export function applyInitialQrSessionState(session: TencentBrowserSession): void {
|
||||||
resetTencentSessionAutoClose(session)
|
resetTencentSessionAutoClose(session)
|
||||||
Object.assign(session, resolveInitialQrSessionState(session.loginType))
|
Object.assign(session, resolveInitialQrSessionState(session.loginType))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function armRedeemedSessionAutoClose(session, { closeSession }) {
|
export function armRedeemedSessionAutoClose(session: TencentBrowserSession, { closeSession }: CloseSessionDependency): void {
|
||||||
resetTencentSessionAutoClose(session)
|
resetTencentSessionAutoClose(session)
|
||||||
|
|
||||||
const autoCloseAt = Date.now() + REDEEM_SUCCESS_AUTO_CLOSE_MS
|
const autoCloseAt = Date.now() + REDEEM_SUCCESS_AUTO_CLOSE_MS
|
||||||
@@ -93,7 +135,7 @@ export function armRedeemedSessionAutoClose(session, { closeSession }) {
|
|||||||
}, REDEEM_SUCCESS_AUTO_CLOSE_MS)
|
}, REDEEM_SUCCESS_AUTO_CLOSE_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetTencentSessionAutoClose(session) {
|
export function resetTencentSessionAutoClose(session?: TencentBrowserSession | null): void {
|
||||||
if (session?.autoCloseTimer) {
|
if (session?.autoCloseTimer) {
|
||||||
clearTimeout(session.autoCloseTimer)
|
clearTimeout(session.autoCloseTimer)
|
||||||
session.autoCloseTimer = null
|
session.autoCloseTimer = null
|
||||||
@@ -104,20 +146,20 @@ export function resetTencentSessionAutoClose(session) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cleanupExpiredTencentBrowserSessions({ closeSession }) {
|
export async function cleanupExpiredTencentBrowserSessions({ closeSession }: CloseSessionDependency): Promise<void> {
|
||||||
const expired = listActiveTencentBrowserSessions().filter((session) => session.expiresAt <= Date.now())
|
const expired = listActiveTencentBrowserSessions().filter((session) => session.expiresAt <= Date.now())
|
||||||
|
|
||||||
await Promise.all(expired.map((session) => closeSession(session, { markClosed: false })))
|
await Promise.all(expired.map((session) => closeSession(session, { markClosed: false })))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function closeSession(
|
export async function closeSession(
|
||||||
session,
|
session: TencentBrowserSession,
|
||||||
{
|
{
|
||||||
markClosed,
|
markClosed,
|
||||||
persistSessionState,
|
persistSessionState,
|
||||||
closeBrowserIfIdle,
|
closeBrowserIfIdle,
|
||||||
},
|
}: CloseSessionOptions,
|
||||||
) {
|
): Promise<void> {
|
||||||
resetTencentSessionAutoClose(session)
|
resetTencentSessionAutoClose(session)
|
||||||
sessions.delete(session.sessionId)
|
sessions.delete(session.sessionId)
|
||||||
|
|
||||||
@@ -139,7 +181,10 @@ export async function closeSession(
|
|||||||
await closeBrowserIfIdle()
|
await closeBrowserIfIdle()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function persistSessionState(session, { buildSessionPayload }) {
|
export async function persistSessionState(
|
||||||
|
session: TencentBrowserSession,
|
||||||
|
{ buildSessionPayload }: PersistSessionStateOptions,
|
||||||
|
): Promise<void> {
|
||||||
await fs.mkdir(session.sessionDir, { recursive: true })
|
await fs.mkdir(session.sessionDir, { recursive: true })
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
path.join(session.sessionDir, 'session.json'),
|
path.join(session.sessionDir, 'session.json'),
|
||||||
@@ -148,7 +193,7 @@ export async function persistSessionState(session, { buildSessionPayload }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSessionId() {
|
function createSessionId(): string {
|
||||||
let sessionId = ''
|
let sessionId = ''
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@@ -456,6 +456,14 @@
|
|||||||
- `npm run typecheck`
|
- `npm run typecheck`
|
||||||
- `npm run build`
|
- `npm run build`
|
||||||
- `npm test` 共 139 个用例通过
|
- `npm test` 共 139 个用例通过
|
||||||
|
92. 腾讯浏览器会话内存 store 模块迁移到 `.ts`:
|
||||||
|
- `src/services/session/session-service-store.ts`
|
||||||
|
93. 浏览器会话对象、注册入参、关闭依赖、自动关闭 timer、session 状态持久化 payload 已显式类型化
|
||||||
|
94. Docker 内验证通过:
|
||||||
|
- `src/services/session/session.test.js` 共 14 个用例通过
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run build`
|
||||||
|
- `npm test` 共 139 个用例通过
|
||||||
|
|
||||||
## 下一步建议
|
## 下一步建议
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user