Files
claudecodeui/server/modules/database/repositories/sessions.db.ts
Haileyesus 7b75ed0b72 refactor(database): move db into typescript
- Implemented githubTokensDb for managing GitHub tokens with CRUD operations.
- Created
otificationPreferencesDb to handle user notification preferences.
- Added projectsDb for project path management and related operations.
- Introduced pushSubscriptionsDb for managing browser push subscriptions.
- Developed scanStateDb to track the last scanned timestamp.
- Established sessionsDb for session management with CRUD functionalities.
- Created userDb for user management, including authentication and onboarding.
- Implemented apidKeysDb for storing and managing VAPID keys.

feat(database): define schema for new database tables

- Added SQL schema definitions for users, API keys, user credentials, notification preferences, VAPID keys, push subscriptions, projects, sessions, scan state, and app configuration.
- Included necessary indexes for performance optimization.

refactor(shared): enhance type definitions and utility functions

- Updated shared types and interfaces for improved clarity and consistency.
- Added new types for credential management and provider-specific operations.
- Refined utility functions for better error handling and message normalization.
2026-04-23 16:59:45 +03:00

193 lines
5.5 KiB
TypeScript

import path from 'node:path';
import { getConnection } from '@/modules/database/connection.js';
import { projectsDb } from '@/modules/database/repositories/projects.db.js';
type SessionNameLookupRow = {
session_id: string;
custom_name: string;
};
type SessionRow = {
session_id: string;
provider: string;
project_path: string | null;
jsonl_path: string | null;
custom_name: string | null;
created_at: string;
updated_at: string;
};
type SessionMetadataLookupRow = Pick<
SessionRow,
'session_id' | 'provider' | 'project_path' | 'jsonl_path' | 'custom_name' | 'created_at' | 'updated_at'
>;
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();
}
function normalizeCodexProjectPath(projectPath: string): string {
const trimmedPath = projectPath.trim();
if (!trimmedPath) {
return projectPath;
}
if (process.platform !== 'win32') {
return path.normalize(trimmedPath);
}
let strippedPath = trimmedPath;
if (strippedPath.startsWith('\\\\?\\UNC\\')) {
strippedPath = `\\\\${strippedPath.slice('\\\\?\\UNC\\'.length)}`;
} else if (strippedPath.startsWith('\\\\?\\')) {
strippedPath = strippedPath.slice('\\\\?\\'.length);
}
return path.win32.normalize(strippedPath);
}
function normalizeProjectPathForProvider(provider: string, projectPath: string): string {
if (provider !== 'codex') {
return projectPath;
}
return normalizeCodexProjectPath(projectPath);
}
export const sessionsDb = {
createSession(
sessionId: string,
provider: string,
projectPath: string,
customName?: string,
createdAt?: string,
updatedAt?: string,
jsonlPath?: string | null
): void {
const db = getConnection();
const createdAtValue = normalizeTimestamp(createdAt);
const updatedAtValue = normalizeTimestamp(updatedAt);
const normalizedProjectPath = normalizeProjectPathForProvider(provider, projectPath);
// First, ensure the project path is recorded in the projects table,
// since it's a foreign key in the sessions table.
projectsDb.createProjectPath(normalizedProjectPath);
db.prepare(
`INSERT INTO sessions (session_id, provider, custom_name, project_path, jsonl_path, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, COALESCE(?, CURRENT_TIMESTAMP), COALESCE(?, CURRENT_TIMESTAMP))
ON CONFLICT(session_id, provider) DO UPDATE SET
updated_at = excluded.updated_at,
project_path = excluded.project_path,
jsonl_path = excluded.jsonl_path,
custom_name = COALESCE(excluded.custom_name, sessions.custom_name)`
).run(
sessionId,
provider,
customName ?? null,
normalizedProjectPath,
jsonlPath ?? null,
createdAtValue,
updatedAtValue
);
},
updateSessionCustomName(sessionId: string, customName: string): void {
const db = getConnection();
db.prepare(
`UPDATE sessions
SET custom_name = ?
WHERE session_id = ?`
).run(customName, sessionId);
},
createSessionName(sessionId: string, provider: string, customName: string): void {
const db = getConnection();
db.prepare(
`UPDATE sessions
SET custom_name = ?
WHERE session_id = ? AND provider = ?`
).run(customName, sessionId, provider);
},
getSessionById(sessionId: string): SessionMetadataLookupRow | null {
const db = getConnection();
const row = db
.prepare(
`SELECT session_id, provider, project_path, jsonl_path, custom_name, created_at, updated_at
FROM sessions
WHERE session_id = ?
ORDER BY updated_at DESC
LIMIT 1`
)
.get(sessionId) as SessionMetadataLookupRow | undefined;
return row ?? null;
},
getAllSessions(): SessionRow[] {
const db = getConnection();
return db
.prepare(
`SELECT session_id, provider, project_path, jsonl_path, custom_name, created_at, updated_at
FROM sessions`
)
.all() as SessionRow[];
},
getSessionsByProjectPath(projectPath: string): SessionRow[] {
const db = getConnection();
return db
.prepare(
`SELECT session_id, provider, project_path, jsonl_path, custom_name, created_at, updated_at
FROM sessions
WHERE project_path = ?`
)
.all(projectPath) as SessionRow[];
},
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(sessionId: string): void {
const db = getConnection();
db.prepare('DELETE FROM sessions WHERE session_id = ?').run(sessionId);
},
};