mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-05-16 17:16:19 +00:00
refactor: remove sessions names db
This commit is contained in:
@@ -5,7 +5,7 @@ import path from 'path';
|
||||
import os from 'os';
|
||||
import TOML from '@iarna/toml';
|
||||
import { getCodexSessions, deleteCodexSession } from '../../../projects.js';
|
||||
import { applyCustomSessionNames, sessionNamesDb } from '@/shared/database/repositories/session-names.js';
|
||||
import { sessionsDb } from '@/shared/database/repositories/sessions.db.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -60,7 +60,7 @@ router.get('/sessions', async (req, res) => {
|
||||
}
|
||||
|
||||
const sessions = await getCodexSessions(projectPath);
|
||||
applyCustomSessionNames(sessions, 'codex');
|
||||
sessionsDb.applyCustomSessionNames(sessions, 'codex');
|
||||
res.json({ success: true, sessions });
|
||||
} catch (error) {
|
||||
console.error('Error fetching Codex sessions:', error);
|
||||
@@ -72,7 +72,7 @@ router.delete('/sessions/:sessionId', async (req, res) => {
|
||||
try {
|
||||
const { sessionId } = req.params;
|
||||
await deleteCodexSession(sessionId);
|
||||
sessionNamesDb.deleteSessionName(sessionId, 'codex');
|
||||
sessionsDb.deleteSession(sessionId);
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error(`Error deleting Codex session ${req.params.sessionId}:`, error);
|
||||
|
||||
@@ -7,7 +7,7 @@ import sqlite3 from 'sqlite3';
|
||||
import { open } from 'sqlite';
|
||||
import crypto from 'crypto';
|
||||
import { CURSOR_MODELS } from '../../../../shared/modelConstants.js';
|
||||
import { applyCustomSessionNames } from '@/shared/database/repositories/session-names.js';
|
||||
import { sessionsDb } from '@/shared/database/repositories/sessions.db.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -561,7 +561,7 @@ router.get('/sessions', async (req, res) => {
|
||||
return new Date(b.createdAt) - new Date(a.createdAt);
|
||||
});
|
||||
|
||||
applyCustomSessionNames(sessions, 'cursor');
|
||||
sessionsDb.applyCustomSessionNames(sessions, 'cursor');
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import express from 'express';
|
||||
import sessionManager from '../../../sessionManager.js';
|
||||
import { sessionNamesDb } from '@/shared/database/repositories/session-names.js';
|
||||
import { sessionsDb } from '@/shared/database/repositories/sessions.db.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -13,7 +13,7 @@ router.delete('/sessions/:sessionId', async (req, res) => {
|
||||
}
|
||||
|
||||
await sessionManager.deleteSession(sessionId);
|
||||
sessionNamesDb.deleteSessionName(sessionId, 'gemini');
|
||||
sessionsDb.deleteSession(sessionId);
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error(`Error deleting Gemini session ${req.params.sessionId}:`, error);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
deleteProject,
|
||||
searchConversations
|
||||
} from '../../../projects.js';
|
||||
import { applyCustomSessionNames, sessionNamesDb } from '@/shared/database/repositories/session-names.js';
|
||||
import { sessionsDb } from '@/shared/database/repositories/sessions.db.js';
|
||||
import { workspaceOriginalPathsDb } from '@/shared/database/repositories/workspace-original-paths.db.js';
|
||||
import { authenticateToken } from '../auth/auth.middleware.js';
|
||||
import { getWorkspaceNameFromPath, WORKSPACES_ROOT, validateWorkspacePath } from './projects.utils.js';
|
||||
@@ -46,7 +46,7 @@ router.get('/api/projects/:projectName/sessions', authenticateToken, async (req,
|
||||
try {
|
||||
const { limit = 5, offset = 0 } = req.query;
|
||||
const result = await getSessions(req.params.projectName, parseInt(limit), parseInt(offset));
|
||||
applyCustomSessionNames(result.sessions, 'claude');
|
||||
sessionsDb.applyCustomSessionNames(result.sessions, 'claude');
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
@@ -70,7 +70,7 @@ router.delete('/api/projects/:projectName/sessions/:sessionId', authenticateToke
|
||||
const { projectName, sessionId } = req.params;
|
||||
console.log(`[API] Deleting session: ${sessionId} from project: ${projectName}`);
|
||||
await deleteSession(projectName, sessionId);
|
||||
sessionNamesDb.deleteSessionName(sessionId, 'claude');
|
||||
sessionsDb.deleteSession(sessionId);
|
||||
console.log(`[API] Session ${sessionId} deleted successfully`);
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import fsp from 'node:fs/promises';
|
||||
import { sessionsDb } from '@/shared/database/repositories/sessions.db.js';
|
||||
import { buildLookupMap, extractFirstValidJsonlData, findFilesRecursivelyCreatedAfterLastScan } from '@/modules/providers/shared/session-parser.utils.js';
|
||||
import { SessionData } from '@/shared/types/session.js';
|
||||
@@ -28,7 +29,23 @@ export async function processClaudeSessions() {
|
||||
const result = await processClaudeSessionFile(file, nameMap);
|
||||
|
||||
if (result) {
|
||||
sessionsDb.createSession(result.sessionId, 'claude', result.workspacePath, result.sessionName);
|
||||
let createdAt: string | undefined;
|
||||
let updatedAt: string | undefined;
|
||||
try {
|
||||
const stat = await fsp.stat(file);
|
||||
createdAt = stat.birthtime.toISOString();
|
||||
updatedAt = stat.mtime.toISOString();
|
||||
} catch {
|
||||
// Ignore stat failures and let DB defaults handle created_at/updated_at.
|
||||
}
|
||||
sessionsDb.createSession(
|
||||
result.sessionId,
|
||||
'claude',
|
||||
result.workspacePath,
|
||||
result.sessionName,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import fsp from 'node:fs/promises';
|
||||
import { sessionsDb } from '@/shared/database/repositories/sessions.db.js';
|
||||
import { buildLookupMap, extractFirstValidJsonlData, findFilesRecursivelyCreatedAfterLastScan } from '@/modules/providers/shared/session-parser.utils.js';
|
||||
import { SessionData } from '@/shared/types/session.js';
|
||||
@@ -29,7 +30,23 @@ export async function processCodexSessions() {
|
||||
const result = await processCodexSessionFile(file, nameMap);
|
||||
|
||||
if (result) {
|
||||
sessionsDb.createSession(result.sessionId, 'codex', result.workspacePath, result.sessionName);
|
||||
let createdAt: string | undefined;
|
||||
let updatedAt: string | undefined;
|
||||
try {
|
||||
const stat = await fsp.stat(file);
|
||||
createdAt = stat.birthtime.toISOString();
|
||||
updatedAt = stat.mtime.toISOString();
|
||||
} catch {
|
||||
// Ignore stat failures and let DB defaults handle created_at/updated_at.
|
||||
}
|
||||
sessionsDb.createSession(
|
||||
result.sessionId,
|
||||
'codex',
|
||||
result.workspacePath,
|
||||
result.sessionName,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,23 @@ export async function processCursorSessions() {
|
||||
const result = await processCursorSessionFile(file);
|
||||
|
||||
if (result) {
|
||||
sessionsDb.createSession(result.sessionId, 'cursor', result.workspacePath, result.sessionName);
|
||||
let createdAt: string | undefined;
|
||||
let updatedAt: string | undefined;
|
||||
try {
|
||||
const stat = await fsp.stat(file);
|
||||
createdAt = stat.birthtime.toISOString();
|
||||
updatedAt = stat.mtime.toISOString();
|
||||
} catch {
|
||||
// Ignore stat failures and let DB defaults handle created_at/updated_at.
|
||||
}
|
||||
sessionsDb.createSession(
|
||||
result.sessionId,
|
||||
'cursor',
|
||||
result.workspacePath,
|
||||
result.sessionName,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,23 @@ export async function processGeminiSessions() {
|
||||
for (const file of files) {
|
||||
const result = await processGeminiSessionFile(file);
|
||||
if (result) {
|
||||
sessionsDb.createSession(result.sessionId, 'gemini', result.workspacePath, result.sessionName);
|
||||
let createdAt: string | undefined;
|
||||
let updatedAt: string | undefined;
|
||||
try {
|
||||
const stat = await fsp.stat(file);
|
||||
createdAt = stat.birthtime.toISOString();
|
||||
updatedAt = stat.mtime.toISOString();
|
||||
} catch {
|
||||
// Ignore stat failures and let DB defaults handle created_at/updated_at.
|
||||
}
|
||||
sessionsDb.createSession(
|
||||
result.sessionId,
|
||||
'gemini',
|
||||
result.workspacePath,
|
||||
result.sessionName,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import express from 'express';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { promises as fsPromises } from 'fs';
|
||||
import { sessionNamesDb } from '@/shared/database/repositories/session-names.js';
|
||||
import { sessionsDb } from '@/shared/database/repositories/sessions.db.js';
|
||||
import { extractProjectDirectory } from '../../../projects.js';
|
||||
import { authenticateToken } from '../auth/auth.middleware.js';
|
||||
|
||||
@@ -27,7 +27,7 @@ router.put('/api/sessions/:sessionId/rename', authenticateToken, async (req, res
|
||||
if (!provider || !VALID_PROVIDERS.includes(provider)) {
|
||||
return res.status(400).json({ error: `Provider must be one of: ${VALID_PROVIDERS.join(', ')}` });
|
||||
}
|
||||
sessionNamesDb.createSessionName(safeSessionId, provider, summary.trim());
|
||||
sessionsDb.createSessionName(safeSessionId, provider, summary.trim());
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error(`[API] Error renaming session ${req.params.sessionId}:`, error);
|
||||
|
||||
@@ -100,7 +100,24 @@ const onUpdate = async (
|
||||
}
|
||||
|
||||
if (sessionId && workspacePath) {
|
||||
sessionsDb.createSession(sessionId, provider, workspacePath, sessionName);
|
||||
let createdAt: string | undefined;
|
||||
let updatedAt: string | undefined;
|
||||
try {
|
||||
const stat = await fsPromises.stat(filePath);
|
||||
createdAt = stat.birthtime.toISOString();
|
||||
updatedAt = stat.mtime.toISOString();
|
||||
} catch {
|
||||
// Ignore stat failures and let DB defaults handle created_at/updated_at.
|
||||
}
|
||||
|
||||
sessionsDb.createSession(
|
||||
sessionId,
|
||||
provider,
|
||||
workspacePath,
|
||||
sessionName,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import webPush from 'web-push';
|
||||
|
||||
import { notificationPreferencesDb } from '@/shared/database/repositories/notification-preferences.js';
|
||||
import { pushSubscriptionsDb } from '@/shared/database/repositories/push-subscriptions.js';
|
||||
import { sessionNamesDb } from '@/shared/database/repositories/session-names.js';
|
||||
import { sessionsDb } from '@/shared/database/repositories/sessions.db.js';
|
||||
|
||||
type NotificationKind = 'action_required' | 'stop' | 'error' | 'info' | string;
|
||||
|
||||
@@ -126,7 +126,7 @@ function resolveSessionName(event: NotificationEvent): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalizeSessionName(sessionNamesDb.getSessionName(event.sessionId, event.provider));
|
||||
return normalizeSessionName(sessionsDb.getSessionName(event.sessionId, event.provider));
|
||||
}
|
||||
|
||||
function buildPushBody(event: NotificationEvent) {
|
||||
@@ -314,4 +314,3 @@ export {
|
||||
notifyRunStopped,
|
||||
notifyRunFailed,
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
APP_CONFIG_TABLE_SCHEMA_SQL,
|
||||
LAST_SCANNED_AT_SQL,
|
||||
PUSH_SUBSCRIPTIONS_TABLE_SCHEMA_SQL,
|
||||
SESSION_NAMES_TABLE_SCHEMA_SQL,
|
||||
SESSIONS_TABLE_SCHEMA_SQL,
|
||||
USER_NOTIFICATION_PREFERENCES_TABLE_SCHEMA_SQL,
|
||||
VAPID_KEYS_TABLE_SCHEMA_SQL,
|
||||
@@ -45,15 +44,17 @@ export const runMigrations = (db: Database) => {
|
||||
db.exec(PUSH_SUBSCRIPTIONS_TABLE_SCHEMA_SQL);
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_push_subscriptions_user_id ON push_subscriptions(user_id)");
|
||||
|
||||
// Create session_names table if it doesn't exist (for existing installations)
|
||||
db.exec(SESSION_NAMES_TABLE_SCHEMA_SQL);
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_session_names_lookup ON session_names(session_id, provider)");
|
||||
|
||||
// Create sessions table if it doesn't exist (for existing installations)
|
||||
db.exec(SESSIONS_TABLE_SCHEMA_SQL);
|
||||
db.exec(
|
||||
"CREATE INDEX IF NOT EXISTS idx_session_ids_lookup ON sessions(session_id)"
|
||||
);
|
||||
const sessionsTableInfo = db.prepare("PRAGMA table_info(sessions)").all() as { name: string }[];
|
||||
const sessionColumnNames = sessionsTableInfo.map((col) => col.name);
|
||||
addColumnToTableIfNotExists(db, "sessions", sessionColumnNames, "created_at", "DATETIME");
|
||||
addColumnToTableIfNotExists(db, "sessions", sessionColumnNames, "updated_at", "DATETIME");
|
||||
db.exec("UPDATE sessions SET created_at = COALESCE(created_at, CURRENT_TIMESTAMP)");
|
||||
db.exec("UPDATE sessions SET updated_at = COALESCE(updated_at, CURRENT_TIMESTAMP)");
|
||||
|
||||
db.exec(WORK_SPACE_PATH_SQL);
|
||||
const workspaceOriginalPathsTableInfo = db.prepare("PRAGMA table_info(workspace_original_paths)").all() as { name: string }[];
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/**
|
||||
* Session names repository.
|
||||
*
|
||||
* Stores provider-scoped custom names for sessions and exposes helpers
|
||||
* to overlay those names onto in-memory session lists.
|
||||
*/
|
||||
|
||||
import { getConnection } from '@/shared/database/connection.js';
|
||||
import type {
|
||||
SessionNameLookupRow,
|
||||
SessionWithSummary,
|
||||
} from '@/shared/database/types.js';
|
||||
|
||||
export const sessionNamesDb = {
|
||||
/** Upserts a custom session name for a provider-scoped session id. */
|
||||
createSessionName(sessionId: string, provider: string, customName: string): void {
|
||||
const db = getConnection();
|
||||
db.prepare(
|
||||
`INSERT INTO session_names (session_id, provider, custom_name)
|
||||
VALUES (?, ?, ?)
|
||||
ON CONFLICT(session_id, provider)
|
||||
DO UPDATE SET custom_name = excluded.custom_name, updated_at = CURRENT_TIMESTAMP`
|
||||
).run(sessionId, provider, customName);
|
||||
},
|
||||
|
||||
/** Alias to keep write semantics explicit when callers perform edits. */
|
||||
updateSessionName(sessionId: string, provider: string, customName: string): void {
|
||||
sessionNamesDb.createSessionName(sessionId, provider, customName);
|
||||
},
|
||||
|
||||
/** Returns a custom name for one session/provider pair or null if unset. */
|
||||
getSessionName(sessionId: string, provider: string): string | null {
|
||||
const db = getConnection();
|
||||
const row = db
|
||||
.prepare(
|
||||
'SELECT custom_name FROM session_names WHERE session_id = ? AND provider = ?'
|
||||
)
|
||||
.get(sessionId, provider) as { custom_name: string } | undefined;
|
||||
return row?.custom_name ?? null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Batch lookup for multiple session ids.
|
||||
* Returns a Map<sessionId, customName> for efficient overlay onto lists.
|
||||
*/
|
||||
getSessionNames(sessionIds: string[], provider: string): Map<string, string> {
|
||||
if (sessionIds.length === 0) return new Map();
|
||||
|
||||
const db = getConnection();
|
||||
const placeholders = sessionIds.map(() => '?').join(',');
|
||||
const rows = db
|
||||
.prepare(
|
||||
`SELECT session_id, custom_name FROM session_names
|
||||
WHERE session_id IN (${placeholders}) AND provider = ?`
|
||||
)
|
||||
.all(...sessionIds, provider) as SessionNameLookupRow[];
|
||||
|
||||
return new Map(rows.map((row) => [row.session_id, row.custom_name]));
|
||||
},
|
||||
|
||||
/** Deletes a custom name. Returns true if a row was removed. */
|
||||
deleteSessionName(sessionId: string, provider: string): boolean {
|
||||
const db = getConnection();
|
||||
return (
|
||||
db
|
||||
.prepare(
|
||||
'DELETE FROM session_names WHERE session_id = ? AND provider = ?'
|
||||
)
|
||||
.run(sessionId, provider).changes > 0
|
||||
);
|
||||
},
|
||||
|
||||
// Legacy aliases used by existing routes/services
|
||||
setName(sessionId: string, provider: string, customName: string): void {
|
||||
sessionNamesDb.createSessionName(sessionId, provider, customName);
|
||||
},
|
||||
getName(sessionId: string, provider: string): string | null {
|
||||
return sessionNamesDb.getSessionName(sessionId, provider);
|
||||
},
|
||||
getNames(sessionIds: string[], provider: string): Map<string, string> {
|
||||
return sessionNamesDb.getSessionNames(sessionIds, provider);
|
||||
},
|
||||
deleteName(sessionId: string, provider: string): boolean {
|
||||
return sessionNamesDb.deleteSessionName(sessionId, provider);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Overlay custom names onto a session list in place.
|
||||
* If a custom name exists, `summary` is replaced.
|
||||
*/
|
||||
export function applyCustomSessionNames(
|
||||
sessions: SessionWithSummary[] | undefined | null,
|
||||
provider: string
|
||||
): void {
|
||||
if (!sessions?.length) return;
|
||||
|
||||
const ids = sessions.map((session) => session.id);
|
||||
const customNames = sessionNamesDb.getSessionNames(ids, provider);
|
||||
for (const session of sessions) {
|
||||
const customName = customNames.get(session.id);
|
||||
if (customName) {
|
||||
session.summary = customName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,92 @@
|
||||
import { workspaceOriginalPathsDb } from '@/shared/database/repositories/workspace-original-paths.db.js';
|
||||
import { getConnection } from '@/shared/database/connection.js';
|
||||
import type { SessionWithSummary } from '@/shared/database/types.js';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Queries
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type SessionNameLookupRow = {
|
||||
session_id: string;
|
||||
custom_name: string;
|
||||
};
|
||||
|
||||
function normalizeTimestamp(value?: string): string | null {
|
||||
if (!value) return null;
|
||||
|
||||
const parsed = new Date(value);
|
||||
if (Number.isNaN(parsed.getTime())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parsed.toISOString();
|
||||
}
|
||||
|
||||
export const sessionsDb = {
|
||||
|
||||
createSession(session_id: string, provider: string, workspacePath: string, customName?: string): void {
|
||||
createSession(
|
||||
session_id: string,
|
||||
provider: string,
|
||||
workspacePath: string,
|
||||
customName?: string,
|
||||
createdAt?: string,
|
||||
updatedAt?: string,
|
||||
): void {
|
||||
const db = getConnection();
|
||||
const createdAtValue = normalizeTimestamp(createdAt);
|
||||
const updatedAtValue = normalizeTimestamp(updatedAt);
|
||||
|
||||
// First, ensure the workspace path is recorded in the workspace_original_paths table
|
||||
// since it's a foreign key in the sessions table.
|
||||
workspaceOriginalPathsDb.createWorkspacePath(workspacePath);
|
||||
|
||||
db.prepare(
|
||||
'INSERT OR IGNORE INTO sessions (session_id, provider, custom_name, workspace_path) VALUES (?, ?, ?, ?)'
|
||||
).run(session_id, provider, customName, workspacePath);
|
||||
`INSERT INTO sessions (session_id, provider, custom_name, workspace_path, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, COALESCE(?, CURRENT_TIMESTAMP), COALESCE(?, CURRENT_TIMESTAMP))
|
||||
ON CONFLICT(session_id) DO UPDATE SET updated_at = excluded.updated_at
|
||||
WHERE sessions.provider = excluded.provider`
|
||||
).run(session_id, provider, customName, workspacePath, createdAtValue, updatedAtValue);
|
||||
},
|
||||
|
||||
/** Updates a custom session name for an existing session row. */
|
||||
createSessionName(sessionId: string, provider: string, customName: string): void {
|
||||
const db = getConnection();
|
||||
db.prepare(
|
||||
`UPDATE sessions
|
||||
SET custom_name = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE session_id = ? AND provider = ?`
|
||||
).run(customName, sessionId, provider);
|
||||
},
|
||||
|
||||
getSessionName(sessionId: string, provider: string): string | null {
|
||||
const db = getConnection();
|
||||
const row = db
|
||||
.prepare(
|
||||
`SELECT custom_name
|
||||
FROM sessions
|
||||
WHERE session_id = ? AND provider = ?`
|
||||
)
|
||||
.get(sessionId, provider) as { custom_name: string | null } | undefined;
|
||||
|
||||
return row?.custom_name ?? null;
|
||||
},
|
||||
|
||||
getSessionNames(sessionIds: string[], provider: string): Map<string, string> {
|
||||
if (sessionIds.length === 0) return new Map();
|
||||
|
||||
const db = getConnection();
|
||||
const placeholders = sessionIds.map(() => '?').join(',');
|
||||
const rows = db
|
||||
.prepare(
|
||||
`SELECT session_id, custom_name
|
||||
FROM sessions
|
||||
WHERE session_id IN (${placeholders})
|
||||
AND provider = ?
|
||||
AND custom_name IS NOT NULL`
|
||||
)
|
||||
.all(...sessionIds, provider) as SessionNameLookupRow[];
|
||||
|
||||
return new Map(rows.map((row) => [row.session_id, row.custom_name]));
|
||||
},
|
||||
|
||||
deleteSession(session_id: string): void {
|
||||
@@ -24,59 +94,18 @@ export const sessionsDb = {
|
||||
db.prepare('DELETE FROM sessions WHERE session_id = ?').run(session_id);
|
||||
},
|
||||
|
||||
applyCustomSessionNames(sessions: SessionWithSummary[] | undefined | null, provider: string): void {
|
||||
if (!sessions?.length) return;
|
||||
|
||||
// /** Inserts or updates a custom session name (upsert on session_id + provider). */
|
||||
// setName(sessionId: string, provider: string, customName: string): void {
|
||||
// const db = getConnection();
|
||||
// db.prepare(
|
||||
// `INSERT INTO session_names (session_id, provider, custom_name)
|
||||
// VALUES (?, ?, ?)
|
||||
// ON CONFLICT(session_id, provider)
|
||||
// DO UPDATE SET custom_name = excluded.custom_name,
|
||||
// updated_at = CURRENT_TIMESTAMP`
|
||||
// ).run(sessionId, provider, customName);
|
||||
// },
|
||||
const ids = sessions.map((session) => session.id);
|
||||
const customNames = sessionsDb.getSessionNames(ids, provider);
|
||||
|
||||
/** Returns the custom name for a single session, or null if unset. */
|
||||
// getName(sessionId: string, provider: string): string | null {
|
||||
// const db = getConnection();
|
||||
// const row = db
|
||||
// .prepare(
|
||||
// 'SELECT custom_name FROM session_names WHERE session_id = ? AND provider = ?'
|
||||
// )
|
||||
// .get(sessionId, provider) as { custom_name: string } | undefined;
|
||||
// return row?.custom_name ?? null;
|
||||
// },
|
||||
|
||||
/**
|
||||
* Batch lookup for multiple session IDs.
|
||||
* Returns a Map<sessionId, customName> for efficient overlay onto session lists.
|
||||
*/
|
||||
// getNames(sessionIds: string[], provider: string): Map<string, string> {
|
||||
// if (sessionIds.length === 0) return new Map();
|
||||
|
||||
// const db = getConnection();
|
||||
// const placeholders = sessionIds.map(() => '?').join(',');
|
||||
// const rows = db
|
||||
// .prepare(
|
||||
// `SELECT session_id, custom_name FROM session_names
|
||||
// WHERE session_id IN (${placeholders}) AND provider = ?`
|
||||
// )
|
||||
// .all(...sessionIds, provider) as SessionNameLookupRow[];
|
||||
|
||||
// return new Map(rows.map((r) => [r.session_id, r.custom_name]));
|
||||
// },
|
||||
|
||||
/** Removes a custom session name. Returns true if a row was deleted. */
|
||||
// deleteName(sessionId: string, provider: string): boolean {
|
||||
// const db = getConnection();
|
||||
// return (
|
||||
// db
|
||||
// .prepare(
|
||||
// 'DELETE FROM session_names WHERE session_id = ? AND provider = ?'
|
||||
// )
|
||||
// .run(sessionId, provider).changes > 0
|
||||
// );
|
||||
// },
|
||||
for (const session of sessions) {
|
||||
const customName = customNames.get(session.id);
|
||||
if (customName) {
|
||||
session.summary = customName;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -69,24 +69,14 @@ CREATE TABLE IF NOT EXISTS push_subscriptions (
|
||||
);
|
||||
`;
|
||||
|
||||
export const SESSION_NAMES_TABLE_SCHEMA_SQL = `
|
||||
CREATE TABLE IF NOT EXISTS session_names (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT NOT NULL,
|
||||
provider TEXT NOT NULL DEFAULT 'claude',
|
||||
custom_name TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(session_id, provider)
|
||||
);
|
||||
`;
|
||||
|
||||
export const SESSIONS_TABLE_SCHEMA_SQL = `
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
session_id TEXT PRIMARY KEY NOT NULL,
|
||||
provider TEXT NOT NULL,
|
||||
custom_name TEXT,
|
||||
workspace_path TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (workspace_path) REFERENCES workspace_original_paths(workspace_path)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
@@ -143,9 +133,6 @@ ${VAPID_KEYS_TABLE_SCHEMA_SQL}
|
||||
${PUSH_SUBSCRIPTIONS_TABLE_SCHEMA_SQL}
|
||||
CREATE INDEX IF NOT EXISTS idx_push_subscriptions_user_id ON push_subscriptions(user_id);
|
||||
|
||||
${SESSION_NAMES_TABLE_SCHEMA_SQL}
|
||||
CREATE INDEX IF NOT EXISTS idx_session_names_lookup ON session_names(session_id, provider);
|
||||
|
||||
${SESSIONS_TABLE_SCHEMA_SQL}
|
||||
CREATE INDEX IF NOT EXISTS idx_session_ids_lookup ON sessions(session_id);
|
||||
|
||||
|
||||
@@ -101,6 +101,8 @@ export type SessionsRow = {
|
||||
provider: LLMProvider;
|
||||
workspace_path: string;
|
||||
custom_name: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type SessionNameRow = {
|
||||
|
||||
Reference in New Issue
Block a user