From f4d5010e774728615b0143bd9dfdab624dc148b6 Mon Sep 17 00:00:00 2001 From: digua Date: Sun, 17 May 2026 13:08:48 +0800 Subject: [PATCH] refactor: unify session list/detail queries into @openchatlab/core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract duplicated session info assembly from electron/main/worker/query/sessions.ts, packages/server/src/http/routes/web.ts, and sessions.ts into shared core functions. - Add CoreSessionInfo type covering all single-DB queryable AnalysisSession fields - Add buildSessionInfo pure mapper (meta + overview → flat info) - Add getSessionInfo convenience (one-call DB query) - Add getSummaryCount and getLastPlatformMessageId helpers - Electron: thin adapter with cache-first overview, platform-specific dbPath/avatar/aiCount - Server web.ts & sessions.ts: delegate to getSessionInfo, eliminate inline assembly - Add 9 unit tests for all new core session functions Co-authored-by: Cursor --- electron/main/worker/query/sessions.ts | 241 +++++------------- packages/core/src/index.ts | 5 + .../query/__tests__/session-queries.test.ts | 175 +++++++++++++ packages/core/src/query/index.ts | 5 + packages/core/src/query/session-queries.ts | 81 ++++++ packages/server/src/http/routes/sessions.ts | 43 ++-- packages/server/src/http/routes/web.ts | 37 +-- 7 files changed, 360 insertions(+), 227 deletions(-) create mode 100644 packages/core/src/query/__tests__/session-queries.test.ts diff --git a/electron/main/worker/query/sessions.ts b/electron/main/worker/query/sessions.ts index c6e0b745..ea08b59c 100644 --- a/electron/main/worker/query/sessions.ts +++ b/electron/main/worker/query/sessions.ts @@ -1,6 +1,10 @@ /** * 会话管理查询模块 * 负责会话列表与单会话基础信息查询 + * + * Core session info logic delegated to @openchatlab/core (buildSessionInfo, getSessionMeta, etc.). + * This file retains Electron-specific concerns: filesystem scanning, caching, private chat avatar, + * dbPath, aiConversationCount, and the getChatOverview cache-first pattern. */ import Database from 'better-sqlite3' @@ -16,20 +20,25 @@ import { type OverviewCache, type MembersCache, } from '../../database/sessionCache' +import { + getSessionMeta, + getSessionOverview, + buildSessionInfo, + getSummaryCount, + getLastPlatformMessageId, + type SessionOverview, +} from '@openchatlab/core' +import type { DatabaseAdapter } from '@openchatlab/core' -interface DbMeta { - name: string - platform: string - type: string - imported_at: number - group_id: string | null - group_avatar: string | null - owner_id: string | null +/** + * Wrap a better-sqlite3 Database as a DatabaseAdapter for core query functions. + */ +function asCoreDb(db: Database.Database): DatabaseAdapter { + return db as unknown as DatabaseAdapter } /** - * 判断是否为聊天会话数据库 - * 通过核心三表(meta/member/message)存在性快速识别 + * 判断是否为聊天会话数据库(local fast check, avoids core import overhead for non-session DBs) */ function isChatSessionDb(db: Database.Database): boolean { const requiredTableCount = db @@ -42,8 +51,11 @@ function isChatSessionDb(db: Database.Database): boolean { * 获取私聊对方成员的头像 * 逻辑参考 private-chat/index.vue 的 otherMemberAvatar */ -function getPrivateChatMemberAvatar(db: Database.Database, sessionName: string, ownerId: string | null): string | null { - // 获取所有非系统消息成员(按消息数排序) +function getPrivateChatMemberAvatar( + db: Database.Database, + sessionName: string, + ownerId: string | null | undefined +): string | null { const members = db .prepare( `SELECT @@ -58,22 +70,34 @@ function getPrivateChatMemberAvatar(db: Database.Database, sessionName: string, if (members.length === 0) return null - // 1. 优先排除 ownerId,找到另一成员的头像 if (ownerId) { const other = members.find((m) => m.platformId !== ownerId) if (other?.avatar) return other.avatar } - // 2. 尝试匹配会话名称(私聊名称通常是对方昵称) const sameName = members.find((m) => m.name === sessionName) if (sameName?.avatar) return sameName.avatar - // 3. 如果只有两个成员且有 ownerId,取另一个(即使没有头像也返回 null) - // 如果没有 ownerId,返回第一个有头像的成员 const firstWithAvatar = members.find((m) => m.avatar) return firstWithAvatar?.avatar || null } +/** + * Resolve overview from cache (with compute-on-miss), or fall back to live SQL via core. + */ +function resolveOverview(db: Database.Database, sessionId: string, cacheDir: string | null): SessionOverview { + let cached = cacheDir ? getCache(sessionId, CACHE_KEY_OVERVIEW, cacheDir) : null + if (!cached && cacheDir) { + try { + cached = computeAndSetOverviewCache(db, sessionId, cacheDir) + } catch { + // cache compute failure — fall through to live query + } + } + if (cached) return cached + return getSessionOverview(asCoreDb(db)) +} + /** * 获取所有会话列表 */ @@ -94,89 +118,31 @@ export function getAllSessions(): any[] { const db = new Database(dbPath) db.pragma('journal_mode = WAL') - // 跳过非聊天会话数据库(例如内部索引库) if (!isChatSessionDb(db)) { db.close() continue } - const meta = db.prepare('SELECT * FROM meta LIMIT 1').get() as DbMeta | undefined + const coreDb = asCoreDb(db) + const meta = getSessionMeta(coreDb) if (meta) { - // 优先从缓存读取,未命中则实时查询并生成缓存 const cacheDir = getCacheDir() - let overview = getCache(sessionId, CACHE_KEY_OVERVIEW, cacheDir) - if (!overview && cacheDir) { - try { - overview = computeAndSetOverviewCache(db, sessionId, cacheDir) - } catch { - // 缓存计算失败不影响正常流程 - } - } + const overview = resolveOverview(db, sessionId, cacheDir) + const summaryCount = getSummaryCount(coreDb) + const info = buildSessionInfo(meta, overview, summaryCount) - const messageCount = - overview?.totalMessages ?? - ( - db - .prepare( - `SELECT COUNT(*) as count - FROM message msg - JOIN member m ON msg.sender_id = m.id - WHERE COALESCE(m.account_name, '') != '系统消息'` - ) - .get() as { count: number } - ).count - const memberCount = - overview?.totalMembers ?? - ( - db - .prepare( - `SELECT COUNT(*) as count - FROM member - WHERE COALESCE(account_name, '') != '系统消息'` - ) - .get() as { count: number } - ).count - - // 私聊:获取对方成员头像 let memberAvatar: string | null = null if (meta.type === 'private') { - memberAvatar = getPrivateChatMemberAvatar(db, meta.name, meta.owner_id) - } - - // 摘要数量:安全查询(chat_session 表可能不存在) - let summaryCount = 0 - try { - const hasChatSessionTable = db - .prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='chat_session'") - .get() as { cnt: number } - if (hasChatSessionTable.cnt > 0) { - summaryCount = ( - db - .prepare("SELECT COUNT(*) as count FROM chat_session WHERE summary IS NOT NULL AND summary != ''") - .get() as { count: number } - ).count - } - } catch { - // 忽略查询错误 + memberAvatar = getPrivateChatMemberAvatar(db, meta.name, meta.ownerId) } sessions.push({ + ...info, id: sessionId, - name: meta.name, - platform: meta.platform, - type: meta.type, - importedAt: meta.imported_at, - messageCount, - memberCount, dbPath, - groupId: meta.group_id || null, - groupAvatar: meta.group_avatar || null, - ownerId: meta.owner_id || null, - memberAvatar, // 私聊对方头像 - lastMessageTs: overview?.lastMessageTs ?? null, - summaryCount, - aiConversationCount: 0, // 将在 IPC 层由主进程填充 + memberAvatar, + aiConversationCount: 0, // filled by IPC layer }) } @@ -196,101 +162,47 @@ export function getSession(sessionId: string): any | null { const db = openDatabase(sessionId) if (!db) return null - const meta = db.prepare('SELECT * FROM meta LIMIT 1').get() as DbMeta | undefined + const coreDb = asCoreDb(db) + const meta = getSessionMeta(coreDb) if (!meta) return null - const overview = getCache(sessionId, CACHE_KEY_OVERVIEW, getCacheDir()) - - const messageCount = - overview?.totalMessages ?? - ( - db - .prepare( - `SELECT COUNT(*) as count - FROM message msg - JOIN member m ON msg.sender_id = m.id - WHERE COALESCE(m.account_name, '') != '系统消息'` - ) - .get() as { count: number } - ).count - - const memberCount = - overview?.totalMembers ?? - ( - db - .prepare( - `SELECT COUNT(*) as count - FROM member - WHERE COALESCE(account_name, '') != '系统消息'` - ) - .get() as { count: number } - ).count - - // 时间范围(优先从 cache 获取) - const firstTimestamp = - overview?.firstMessageTs ?? - (db.prepare('SELECT MIN(ts) as v FROM message').get() as { v: number | null })?.v ?? - null - const lastTimestamp = - overview?.lastMessageTs ?? (db.prepare('SELECT MAX(ts) as v FROM message').get() as { v: number | null })?.v ?? null - - // 最新的 platform_message_id(用于增量边界参考) - const lastPmidRow = db - .prepare('SELECT platform_message_id FROM message WHERE platform_message_id IS NOT NULL ORDER BY ts DESC LIMIT 1') - .get() as { platform_message_id: string } | undefined + const overview = resolveOverview(db, sessionId, getCacheDir()) + const info = buildSessionInfo(meta, overview) return { + ...info, id: sessionId, - name: meta.name, - platform: meta.platform, - type: meta.type, - importedAt: meta.imported_at, - messageCount, - memberCount, dbPath: getDbPath(sessionId), - groupId: meta.group_id || null, - groupAvatar: meta.group_avatar || null, - ownerId: meta.owner_id || null, - firstTimestamp, - lastTimestamp, - lastPlatformMessageId: lastPmidRow?.platform_message_id ?? null, + firstTimestamp: overview.firstMessageTs, + lastTimestamp: overview.lastMessageTs, + lastPlatformMessageId: getLastPlatformMessageId(coreDb), } } /** * 获取聊天概览(AI 工具使用) - * 优先从缓存读取,miss 则计算回填 + * Cache-first: overview cache + members cache, fallback to live SQL via core. */ export function getChatOverview(sessionId: string, topN: number = 10) { const db = openDatabase(sessionId) if (!db) return null - const meta = db.prepare('SELECT * FROM meta LIMIT 1').get() as DbMeta | undefined + const coreDb = asCoreDb(db) + const meta = getSessionMeta(coreDb) if (!meta) return null const cacheDir = getCacheDir() + const overview = resolveOverview(db, sessionId, cacheDir) - // 读取 overview 缓存 - let overview = getCache(sessionId, CACHE_KEY_OVERVIEW, cacheDir) - if (!overview) { - try { - overview = computeAndSetOverviewCache(db, sessionId, cacheDir) - } catch { - // fallback: 实时查询 - } - } - - // 读取 members 缓存 let membersCache = getCache(sessionId, CACHE_KEY_MEMBERS, cacheDir) if (!membersCache) { try { membersCache = computeAndSetMembersCache(db, sessionId, cacheDir) } catch { - // fallback: 无成员数据 + // fallback: no member data } } - // 从缓存计算 Top N 活跃成员 let topMembers: Array<{ id: number; name: string; count: number }> = [] if (membersCache?.members) { topMembers = Object.entries(membersCache.members) @@ -299,35 +211,14 @@ export function getChatOverview(sessionId: string, topN: number = 10) { .slice(0, topN) } - // fallback 统计值 - const totalMessages = - overview?.totalMessages ?? - ( - db - .prepare( - `SELECT COUNT(*) as count FROM message msg - JOIN member m ON msg.sender_id = m.id - WHERE COALESCE(m.account_name, '') != '系统消息'` - ) - .get() as { count: number } - ).count - - const totalMembers = - overview?.totalMembers ?? - ( - db.prepare(`SELECT COUNT(*) as count FROM member WHERE COALESCE(account_name, '') != '系统消息'`).get() as { - count: number - } - ).count - return { name: meta.name, platform: meta.platform, type: meta.type, - totalMessages, - totalMembers, - firstMessageTs: overview?.firstMessageTs ?? null, - lastMessageTs: overview?.lastMessageTs ?? null, + totalMessages: overview.totalMessages, + totalMembers: overview.totalMembers, + firstMessageTs: overview.firstMessageTs, + lastMessageTs: overview.lastMessageTs, topMembers, } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b48ff1fa..3d826fbd 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -29,6 +29,10 @@ export { searchSessions, getSessionMessages, getSessionSummaries, + buildSessionInfo, + getSessionInfo, + getSummaryCount, + getLastPlatformMessageId, getTimeRange, getAvailableYears, getMemberActivity, @@ -81,6 +85,7 @@ export type { SessionMeta, SessionOverview, SessionInfo, + CoreSessionInfo, ChatOverviewData, SessionSearchItem, SessionMessagesData, diff --git a/packages/core/src/query/__tests__/session-queries.test.ts b/packages/core/src/query/__tests__/session-queries.test.ts new file mode 100644 index 00000000..63c07a1e --- /dev/null +++ b/packages/core/src/query/__tests__/session-queries.test.ts @@ -0,0 +1,175 @@ +/** + * Minimal tests for session query functions. + * + * Verifies buildSessionInfo pure mapper and getSessionInfo / getSummaryCount / getLastPlatformMessageId + * against a mock DatabaseAdapter. + * + * Run: npx tsx --test packages/core/src/query/__tests__/session-queries.test.ts + */ + +import { describe, it } from 'node:test' +import assert from 'node:assert/strict' +import type { SessionMeta, SessionOverview } from '../session-queries' +import { buildSessionInfo, getSessionInfo, getSummaryCount, getLastPlatformMessageId } from '../session-queries' +import type { DatabaseAdapter } from '../../interfaces' + +// ==================== Mock helpers ==================== + +function makeMeta(overrides?: Partial): SessionMeta { + return { + name: 'Test Group', + platform: 'wechat', + type: 'group', + importedAt: 1700000000, + groupId: 'g001', + groupAvatar: null, + ownerId: 'u001', + ...overrides, + } +} + +function makeOverview(overrides?: Partial): SessionOverview { + return { + totalMessages: 500, + totalMembers: 10, + firstMessageTs: 1600000000, + lastMessageTs: 1700000000, + ...overrides, + } +} + +type QueryResult = Record + +/** + * Create a mock DatabaseAdapter whose prepare().get()/all() respond based on SQL patterns. + */ +function createMockDb(handlers: Record QueryResult | QueryResult[]>): DatabaseAdapter { + return { + prepare(sql: string) { + return { + get(...args: unknown[]) { + for (const [pattern, handler] of Object.entries(handlers)) { + if (sql.includes(pattern)) { + return handler(args) + } + } + return undefined + }, + all(...args: unknown[]) { + for (const [pattern, handler] of Object.entries(handlers)) { + if (sql.includes(pattern)) { + const result = handler(args) + return Array.isArray(result) ? result : [result] + } + } + return [] + }, + run() { + return { changes: 0, lastInsertRowid: 0 } + }, + } + }, + } as unknown as DatabaseAdapter +} + +// ==================== Tests ==================== + +describe('buildSessionInfo', () => { + it('composes meta + overview into flat CoreSessionInfo', () => { + const meta = makeMeta() + const overview = makeOverview() + const info = buildSessionInfo(meta, overview, 3) + + assert.equal(info.name, 'Test Group') + assert.equal(info.platform, 'wechat') + assert.equal(info.type, 'group') + assert.equal(info.importedAt, 1700000000) + assert.equal(info.messageCount, 500) + assert.equal(info.memberCount, 10) + assert.equal(info.firstMessageTs, 1600000000) + assert.equal(info.lastMessageTs, 1700000000) + assert.equal(info.groupId, 'g001') + assert.equal(info.groupAvatar, null) + assert.equal(info.ownerId, 'u001') + assert.equal(info.summaryCount, 3) + }) + + it('defaults summaryCount to 0 when omitted', () => { + const info = buildSessionInfo(makeMeta(), makeOverview()) + assert.equal(info.summaryCount, 0) + }) + + it('preserves null timestamps when overview has no messages', () => { + const overview = makeOverview({ firstMessageTs: null, lastMessageTs: null }) + const info = buildSessionInfo(makeMeta(), overview) + assert.equal(info.firstMessageTs, null) + assert.equal(info.lastMessageTs, null) + }) +}) + +describe('getSessionInfo', () => { + it('returns null when meta table is empty', () => { + const db = createMockDb({ + 'FROM meta': () => undefined as unknown as QueryResult, + }) + assert.equal(getSessionInfo(db), null) + }) + + it('returns combined CoreSessionInfo from DB', () => { + const db = createMockDb({ + 'FROM meta': () => ({ + name: 'Chat', + platform: 'telegram', + type: 'private', + imported_at: 1700000000, + group_id: null, + group_avatar: null, + owner_id: 'me', + }), + 'COUNT(*)': () => ({ count: 42 }), + 'MIN(ts)': () => ({ v: 1600000000 }), + 'MAX(ts)': () => ({ v: 1700000000 }), + sqlite_master: () => ({ cnt: 0 }), + }) + + const info = getSessionInfo(db) + assert.ok(info) + assert.equal(info.name, 'Chat') + assert.equal(info.platform, 'telegram') + assert.equal(info.type, 'private') + assert.equal(info.ownerId, 'me') + }) +}) + +describe('getSummaryCount', () => { + it('returns 0 when chat_session table does not exist', () => { + const db = createMockDb({ + sqlite_master: () => ({ cnt: 0 }), + }) + assert.equal(getSummaryCount(db), 0) + }) + + it('returns count when chat_session table exists', () => { + const db = createMockDb({ + sqlite_master: () => ({ cnt: 1 }), + 'FROM chat_session': () => ({ count: 7 }), + }) + assert.equal(getSummaryCount(db), 7) + }) +}) + +describe('getLastPlatformMessageId', () => { + it('returns null when no platform_message_id exists', () => { + const db = createMockDb({ + platform_message_id: () => undefined as unknown as QueryResult, + }) + assert.equal(getLastPlatformMessageId(db), null) + }) + + it('returns the latest platform_message_id', () => { + const db = createMockDb({ + platform_message_id: () => ({ platform_message_id: 'pmid-999' }), + }) + assert.equal(getLastPlatformMessageId(db), 'pmid-999') + }) +}) diff --git a/packages/core/src/query/index.ts b/packages/core/src/query/index.ts index dc17f6e3..ba95ceb0 100644 --- a/packages/core/src/query/index.ts +++ b/packages/core/src/query/index.ts @@ -9,11 +9,16 @@ export { searchSessions, getSessionMessages, getSessionSummaries, + buildSessionInfo, + getSessionInfo, + getSummaryCount, + getLastPlatformMessageId, } from './session-queries' export type { SessionMeta, SessionOverview, SessionInfo, + CoreSessionInfo, ChatOverviewData, SessionSearchItem, SessionMessagesData, diff --git a/packages/core/src/query/session-queries.ts b/packages/core/src/query/session-queries.ts index 2ba9e783..46e3222f 100644 --- a/packages/core/src/query/session-queries.ts +++ b/packages/core/src/query/session-queries.ts @@ -32,6 +32,87 @@ export interface SessionInfo extends SessionMeta { overview: SessionOverview } +// ==================== Flat session info (for AnalysisSession-like consumers) ==================== + +/** + * Platform-agnostic session info built from meta + overview. + * Covers all AnalysisSession fields that are queryable from a single DB. + * Platform-specific fields (dbPath, memberAvatar, aiConversationCount) are left to callers. + */ +export interface CoreSessionInfo { + name: string + platform: string + type: string + importedAt: number + messageCount: number + memberCount: number + groupId: string | null + groupAvatar: string | null + ownerId: string | null + firstMessageTs: number | null + lastMessageTs: number | null + summaryCount: number +} + +/** + * Pure mapper: compose SessionMeta + SessionOverview into flat CoreSessionInfo. + * Callers provide the inputs which may come from cache or fresh SQL. + */ +export function buildSessionInfo( + meta: SessionMeta, + overview: SessionOverview, + summaryCount: number = 0 +): CoreSessionInfo { + return { + name: meta.name, + platform: meta.platform, + type: meta.type, + importedAt: meta.importedAt, + messageCount: overview.totalMessages, + memberCount: overview.totalMembers, + groupId: meta.groupId, + groupAvatar: meta.groupAvatar, + ownerId: meta.ownerId, + firstMessageTs: overview.firstMessageTs, + lastMessageTs: overview.lastMessageTs, + summaryCount, + } +} + +/** + * Convenience: read meta + overview from DB and return flat CoreSessionInfo. + */ +export function getSessionInfo(db: DatabaseAdapter): CoreSessionInfo | null { + const meta = getSessionMeta(db) + if (!meta) return null + const overview = getSessionOverview(db) + const sc = getSummaryCount(db) + return buildSessionInfo(meta, overview, sc) +} + +/** + * Count of chat sessions that have an AI-generated summary. + */ +export function getSummaryCount(db: DatabaseAdapter): number { + if (!hasTable(db, 'chat_session')) return 0 + const row = db + .prepare("SELECT COUNT(*) as count FROM chat_session WHERE summary IS NOT NULL AND summary != ''") + .get() as { count: number } | undefined + return row?.count ?? 0 +} + +/** + * Get the latest platform_message_id (used as incremental import boundary). + */ +export function getLastPlatformMessageId(db: DatabaseAdapter): string | null { + const row = db + .prepare('SELECT platform_message_id FROM message WHERE platform_message_id IS NOT NULL ORDER BY ts DESC LIMIT 1') + .get() as { platform_message_id: string } | undefined + return row?.platform_message_id ?? null +} + +// ==================== Core identification ==================== + /** * 判断数据库是否为聊天会话数据库 * 通过核心三表(meta/member/message)存在性快速识别 diff --git a/packages/server/src/http/routes/sessions.ts b/packages/server/src/http/routes/sessions.ts index 4ed22f95..fa7e84d7 100644 --- a/packages/server/src/http/routes/sessions.ts +++ b/packages/server/src/http/routes/sessions.ts @@ -8,6 +8,7 @@ import type { FastifyInstance } from 'fastify' import type { DatabaseManager } from '@openchatlab/node-runtime' import { + getSessionInfo, getSessionMeta, getSessionOverview, queryMessages, @@ -35,19 +36,18 @@ export function registerSessionRoutes(server: FastifyInstance, dbManager: Databa .map((id) => { const db = dbManager.open(id) if (!db) return null - const meta = getSessionMeta(db) - if (!meta) return null - const overview = getSessionOverview(db) + const info = getSessionInfo(db) + if (!info) return null return { id, - name: meta.name, - platform: meta.platform, - type: meta.type, - groupId: meta.groupId || undefined, - messageCount: overview.totalMessages, - memberCount: overview.totalMembers, - firstTimestamp: overview.firstMessageTs, - lastTimestamp: overview.lastMessageTs, + name: info.name, + platform: info.platform, + type: info.type, + groupId: info.groupId || undefined, + messageCount: info.messageCount, + memberCount: info.memberCount, + firstTimestamp: info.firstMessageTs, + lastTimestamp: info.lastMessageTs, } }) .filter(Boolean) @@ -57,19 +57,18 @@ export function registerSessionRoutes(server: FastifyInstance, dbManager: Databa // GET /api/v1/sessions/:id — Single session detail server.get<{ Params: { id: string } }>('/api/v1/sessions/:id', async (request) => { const db = ensureDb(dbManager, request.params.id) - const meta = getSessionMeta(db) - if (!meta) throw sessionNotFound(request.params.id) - const overview = getSessionOverview(db) + const info = getSessionInfo(db) + if (!info) throw sessionNotFound(request.params.id) return successResponse({ id: request.params.id, - name: meta.name, - platform: meta.platform, - type: meta.type, - groupId: meta.groupId || undefined, - messageCount: overview.totalMessages, - memberCount: overview.totalMembers, - firstTimestamp: overview.firstMessageTs, - lastTimestamp: overview.lastMessageTs, + name: info.name, + platform: info.platform, + type: info.type, + groupId: info.groupId || undefined, + messageCount: info.messageCount, + memberCount: info.memberCount, + firstTimestamp: info.firstMessageTs, + lastTimestamp: info.lastMessageTs, }) }) diff --git a/packages/server/src/http/routes/web.ts b/packages/server/src/http/routes/web.ts index afd77577..e7c02042 100644 --- a/packages/server/src/http/routes/web.ts +++ b/packages/server/src/http/routes/web.ts @@ -12,8 +12,7 @@ import type { FastifyInstance } from 'fastify' import type { DatabaseManager } from '@openchatlab/node-runtime' import type { TimeFilter } from '@openchatlab/shared-types' import { - getSessionMeta, - getSessionOverview, + getSessionInfo, getTimeRange, getAvailableYears, getMemberActivity, @@ -80,24 +79,13 @@ export function registerWebRoutes(server: FastifyInstance, dbManager: DatabaseMa .map((id) => { const db = dbManager.open(id) if (!db) return null - const meta = getSessionMeta(db) - if (!meta) return null - const overview = getSessionOverview(db) + const info = getSessionInfo(db) + if (!info) return null return { + ...info, id, - name: meta.name, - platform: meta.platform, - type: meta.type, - importedAt: meta.importedAt ?? 0, - messageCount: overview.totalMessages, - memberCount: overview.totalMembers, dbPath: '', - groupId: meta.groupId ?? null, - groupAvatar: meta.groupAvatar ?? null, - ownerId: meta.ownerId ?? null, memberAvatar: null, - lastMessageTs: overview.lastMessageTs ?? null, - summaryCount: 0, aiConversationCount: 0, } }) @@ -106,24 +94,13 @@ export function registerWebRoutes(server: FastifyInstance, dbManager: DatabaseMa server.get<{ Params: { id: string } }>('/_web/sessions/:id', async (request) => { const db = ensureDb(dbManager, request.params.id) - const meta = getSessionMeta(db) - if (!meta) return null - const overview = getSessionOverview(db) + const info = getSessionInfo(db) + if (!info) return null return { + ...info, id: request.params.id, - name: meta.name, - platform: meta.platform, - type: meta.type, - importedAt: meta.importedAt ?? 0, - messageCount: overview.totalMessages, - memberCount: overview.totalMembers, dbPath: '', - groupId: meta.groupId ?? null, - groupAvatar: meta.groupAvatar ?? null, - ownerId: meta.ownerId ?? null, memberAvatar: null, - lastMessageTs: overview.lastMessageTs ?? null, - summaryCount: 0, aiConversationCount: 0, } })