| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import path from "path"; |
| import fs from "fs"; |
| import { getDbInstance, isCloud, isBuildPhase, DATA_DIR } from "../db/core"; |
| import { getLegacyDotDataDir, isSamePath } from "../dataPaths"; |
|
|
| export const shouldPersistToDisk = !isCloud && !isBuildPhase; |
|
|
| |
|
|
| const LEGACY_DATA_DIR = isCloud ? null : getLegacyDotDataDir(); |
|
|
| export const LOG_FILE = isCloud ? null : path.join(DATA_DIR, "log.txt"); |
| export const CALL_LOGS_DIR = isCloud ? null : path.join(DATA_DIR, "call_logs"); |
|
|
| |
| const LEGACY_DB_FILE = |
| isCloud || !LEGACY_DATA_DIR ? null : path.join(LEGACY_DATA_DIR, "usage.json"); |
| const LEGACY_LOG_FILE = isCloud || !LEGACY_DATA_DIR ? null : path.join(LEGACY_DATA_DIR, "log.txt"); |
| const LEGACY_CALL_LOGS_DB_FILE = |
| isCloud || !LEGACY_DATA_DIR ? null : path.join(LEGACY_DATA_DIR, "call_logs.json"); |
| const LEGACY_CALL_LOGS_DIR = |
| isCloud || !LEGACY_DATA_DIR ? null : path.join(LEGACY_DATA_DIR, "call_logs"); |
|
|
| |
| const USAGE_JSON_FILE = isCloud ? null : path.join(DATA_DIR, "usage.json"); |
| const CALL_LOGS_JSON_FILE = isCloud ? null : path.join(DATA_DIR, "call_logs.json"); |
|
|
| |
|
|
| function copyIfMissing(fromPath, toPath, label) { |
| if (!fromPath || !toPath) return; |
| if (!fs.existsSync(fromPath) || fs.existsSync(toPath)) return; |
|
|
| if (fs.statSync(fromPath).isDirectory()) { |
| fs.cpSync(fromPath, toPath, { recursive: true }); |
| } else { |
| fs.copyFileSync(fromPath, toPath); |
| } |
| console.log(`[usageDb] Migrated ${label}: ${fromPath} -> ${toPath}`); |
| } |
|
|
| export function migrateLegacyUsageFiles() { |
| if (!shouldPersistToDisk || !LEGACY_DATA_DIR) return; |
| if (isSamePath(DATA_DIR, LEGACY_DATA_DIR)) return; |
|
|
| try { |
| copyIfMissing(LEGACY_DB_FILE, USAGE_JSON_FILE, "usage history"); |
| copyIfMissing(LEGACY_LOG_FILE, LOG_FILE, "request log"); |
| copyIfMissing(LEGACY_CALL_LOGS_DB_FILE, CALL_LOGS_JSON_FILE, "call log index"); |
| copyIfMissing(LEGACY_CALL_LOGS_DIR, CALL_LOGS_DIR, "call log files"); |
| } catch (error) { |
| console.error("[usageDb] Legacy migration failed:", error.message); |
| } |
| } |
|
|
| |
|
|
| export function migrateUsageJsonToSqlite() { |
| if (!shouldPersistToDisk) return; |
| const db = getDbInstance(); |
|
|
| |
| if (USAGE_JSON_FILE && fs.existsSync(USAGE_JSON_FILE)) { |
| try { |
| const raw = fs.readFileSync(USAGE_JSON_FILE, "utf-8"); |
| const data = JSON.parse(raw); |
| const history = data.history || []; |
|
|
| if (history.length > 0) { |
| console.log(`[usageDb] Migrating ${history.length} usage entries from JSON β SQLite...`); |
|
|
| const insert = db.prepare(` |
| INSERT INTO usage_history (provider, model, connection_id, api_key_id, api_key_name, |
| tokens_input, tokens_output, tokens_cache_read, tokens_cache_creation, tokens_reasoning, |
| status, timestamp) |
| VALUES (@provider, @model, @connectionId, @apiKeyId, @apiKeyName, |
| @tokensInput, @tokensOutput, @tokensCacheRead, @tokensCacheCreation, @tokensReasoning, |
| @status, @timestamp) |
| `); |
|
|
| const tx = db.transaction(() => { |
| for (const entry of history) { |
| insert.run({ |
| provider: entry.provider || null, |
| model: entry.model || null, |
| connectionId: entry.connectionId || null, |
| apiKeyId: entry.apiKeyId || null, |
| apiKeyName: entry.apiKeyName || null, |
| tokensInput: entry.tokens?.input ?? entry.tokens?.prompt_tokens ?? 0, |
| tokensOutput: entry.tokens?.output ?? entry.tokens?.completion_tokens ?? 0, |
| tokensCacheRead: entry.tokens?.cacheRead ?? entry.tokens?.cached_tokens ?? 0, |
| tokensCacheCreation: |
| entry.tokens?.cacheCreation ?? entry.tokens?.cache_creation_input_tokens ?? 0, |
| tokensReasoning: entry.tokens?.reasoning ?? entry.tokens?.reasoning_tokens ?? 0, |
| status: entry.status || null, |
| timestamp: entry.timestamp || new Date().toISOString(), |
| }); |
| } |
| }); |
| tx(); |
| console.log(`[usageDb] β Migrated ${history.length} usage entries`); |
| } |
|
|
| fs.renameSync(USAGE_JSON_FILE, USAGE_JSON_FILE + ".migrated"); |
| } catch (err) { |
| console.error("[usageDb] Failed to migrate usage.json:", err.message); |
| } |
| } |
|
|
| |
| if (CALL_LOGS_JSON_FILE && fs.existsSync(CALL_LOGS_JSON_FILE)) { |
| try { |
| const raw = fs.readFileSync(CALL_LOGS_JSON_FILE, "utf-8"); |
| const data = JSON.parse(raw); |
| const logs = data.logs || []; |
|
|
| if (logs.length > 0) { |
| console.log(`[usageDb] Migrating ${logs.length} call log entries from JSON β SQLite...`); |
|
|
| const insert = db.prepare(` |
| INSERT OR IGNORE INTO call_logs (id, timestamp, method, path, status, model, provider, |
| account, connection_id, duration, tokens_in, tokens_out, source_format, target_format, |
| api_key_id, api_key_name, combo_name, request_body, response_body, error) |
| VALUES (@id, @timestamp, @method, @path, @status, @model, @provider, |
| @account, @connectionId, @duration, @tokensIn, @tokensOut, @sourceFormat, @targetFormat, |
| @apiKeyId, @apiKeyName, @comboName, @requestBody, @responseBody, @error) |
| `); |
|
|
| const tx = db.transaction(() => { |
| for (const log of logs) { |
| insert.run({ |
| id: log.id || `${Date.now()}-${Math.random().toString(36).slice(2, 6)}`, |
| timestamp: log.timestamp || new Date().toISOString(), |
| method: log.method || "POST", |
| path: log.path || null, |
| status: log.status || 0, |
| model: log.model || null, |
| provider: log.provider || null, |
| account: log.account || null, |
| connectionId: log.connectionId || null, |
| duration: log.duration || 0, |
| tokensIn: log.tokens?.in ?? 0, |
| tokensOut: log.tokens?.out ?? 0, |
| sourceFormat: log.sourceFormat || null, |
| targetFormat: log.targetFormat || null, |
| apiKeyId: log.apiKeyId || null, |
| apiKeyName: log.apiKeyName || null, |
| comboName: log.comboName || null, |
| requestBody: log.requestBody ? JSON.stringify(log.requestBody) : null, |
| responseBody: log.responseBody ? JSON.stringify(log.responseBody) : null, |
| error: log.error || null, |
| }); |
| } |
| }); |
| tx(); |
| console.log(`[usageDb] β Migrated ${logs.length} call log entries`); |
| } |
|
|
| fs.renameSync(CALL_LOGS_JSON_FILE, CALL_LOGS_JSON_FILE + ".migrated"); |
| } catch (err) { |
| console.error("[usageDb] Failed to migrate call_logs.json:", err.message); |
| } |
| } |
| } |
|
|
| |
|
|
| migrateLegacyUsageFiles(); |
|
|
| if (shouldPersistToDisk) { |
| try { |
| migrateUsageJsonToSqlite(); |
| } catch { |
| |
| } |
| } |
|
|