mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-05-08 22:19:36 +00:00
* Update DataTab.tsx ## API Key Import Fix We identified and fixed an issue with the API key import functionality in the DataTab component. The problem was that API keys were being stored in localStorage instead of cookies, and the key format was being incorrectly processed. ### Changes Made: 1. **Updated `handleImportAPIKeys` function**: - Changed to store API keys in cookies instead of localStorage - Modified to use provider names directly as keys (e.g., "OpenAI", "Google") - Added logic to skip comment fields (keys starting with "_") - Added page reload after successful import to apply changes immediately 2. **Updated `handleDownloadTemplate` function**: - Changed template format to use provider names as keys - Added explanatory comment in the template - Removed URL-related keys that weren't being used properly 3. **Fixed template format**: - Template now uses the correct format with provider names as keys - Added support for all available providers including Hyperbolic These changes ensure that when users download the template, fill it with their API keys, and import it back, the keys are properly stored in cookies with the correct format that the application expects. * backwards compatible old import template * Update the export / import settings Settings Export/Import Improvements We've completely redesigned the settings export and import functionality to ensure all application settings are properly backed up and restored: Key Improvements Comprehensive Export Format: Now captures ALL settings from both localStorage and cookies, organized into logical categories (core, providers, features, UI, connections, debug, updates) Robust Import System: Automatically detects format version and handles both new and legacy formats with detailed error handling Complete Settings Coverage: Properly exports and imports settings from ALL tabs including: Local provider configurations (Ollama, LMStudio, etc.) Cloud provider API keys (OpenAI, Anthropic, etc.) Feature toggles and preferences UI configurations and tab settings Connection settings (GitHub, Netlify) Debug configurations and logs Technical Details Added version tracking to export files for better compatibility Implemented fallback mechanisms if primary import methods fail Added detailed logging for troubleshooting import/export issues Created helper functions for safer data handling Maintained backward compatibility with older export formats Feature Settings: Feature flags and viewed features Developer mode settings Energy saver mode configurations User Preferences: User profile information Theme settings Tab configurations Connection Settings: Netlify connections Git authentication credentials Any other service connections Debug and System Settings: Debug flags and acknowledged issues Error logs and event logs Update settings and preferences * Update DataTab.tsx * Update GithubConnection.tsx revert the code back as asked * feat: enhance style to match the project * feat:small improvements * feat: add major improvements * Update Dialog.tsx * Delete DataTab.tsx.bak * feat: small updates * Update DataVisualization.tsx * feat: dark mode fix
696 lines
20 KiB
TypeScript
696 lines
20 KiB
TypeScript
import Cookies from 'js-cookie';
|
|
import { type Message } from 'ai';
|
|
import { getAllChats, deleteChat } from '~/lib/persistence/chats';
|
|
|
|
interface ExtendedMessage extends Message {
|
|
name?: string;
|
|
function_call?: any;
|
|
timestamp?: number;
|
|
}
|
|
|
|
/**
|
|
* Service for handling import and export operations of application data
|
|
*/
|
|
export class ImportExportService {
|
|
/**
|
|
* Export all chats to a JSON file
|
|
* @param db The IndexedDB database instance
|
|
* @returns A promise that resolves to the export data
|
|
*/
|
|
static async exportAllChats(db: IDBDatabase): Promise<{ chats: any[]; exportDate: string }> {
|
|
if (!db) {
|
|
throw new Error('Database not initialized');
|
|
}
|
|
|
|
try {
|
|
// Get all chats from the database using the getAllChats helper
|
|
const chats = await getAllChats(db);
|
|
|
|
// Validate and sanitize each chat before export
|
|
const sanitizedChats = chats.map((chat) => ({
|
|
id: chat.id,
|
|
description: chat.description || '',
|
|
messages: chat.messages.map((msg: ExtendedMessage) => ({
|
|
id: msg.id,
|
|
role: msg.role,
|
|
content: msg.content,
|
|
name: msg.name,
|
|
function_call: msg.function_call,
|
|
timestamp: msg.timestamp,
|
|
})),
|
|
timestamp: chat.timestamp,
|
|
urlId: chat.urlId || null,
|
|
metadata: chat.metadata || null,
|
|
}));
|
|
|
|
console.log(`Successfully prepared ${sanitizedChats.length} chats for export`);
|
|
|
|
return {
|
|
chats: sanitizedChats,
|
|
exportDate: new Date().toISOString(),
|
|
};
|
|
} catch (error) {
|
|
console.error('Error exporting chats:', error);
|
|
throw new Error(`Failed to export chats: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export application settings to a JSON file
|
|
* @returns A promise that resolves to the settings data
|
|
*/
|
|
static async exportSettings(): Promise<any> {
|
|
try {
|
|
// Get all cookies
|
|
const allCookies = Cookies.get();
|
|
|
|
// Create a comprehensive settings object
|
|
return {
|
|
// Core settings
|
|
core: {
|
|
// User profile and main settings
|
|
bolt_user_profile: this._safeGetItem('bolt_user_profile'),
|
|
bolt_settings: this._safeGetItem('bolt_settings'),
|
|
bolt_profile: this._safeGetItem('bolt_profile'),
|
|
theme: this._safeGetItem('theme'),
|
|
},
|
|
|
|
// Provider settings (both local and cloud)
|
|
providers: {
|
|
// Provider configurations from localStorage
|
|
provider_settings: this._safeGetItem('provider_settings'),
|
|
|
|
// API keys from cookies
|
|
apiKeys: allCookies.apiKeys,
|
|
|
|
// Selected provider and model
|
|
selectedModel: allCookies.selectedModel,
|
|
selectedProvider: allCookies.selectedProvider,
|
|
|
|
// Provider-specific settings
|
|
providers: allCookies.providers,
|
|
},
|
|
|
|
// Feature settings
|
|
features: {
|
|
// Feature flags
|
|
viewed_features: this._safeGetItem('bolt_viewed_features'),
|
|
developer_mode: this._safeGetItem('bolt_developer_mode'),
|
|
|
|
// Context optimization
|
|
contextOptimizationEnabled: this._safeGetItem('contextOptimizationEnabled'),
|
|
|
|
// Auto-select template
|
|
autoSelectTemplate: this._safeGetItem('autoSelectTemplate'),
|
|
|
|
// Latest branch
|
|
isLatestBranch: this._safeGetItem('isLatestBranch'),
|
|
|
|
// Event logs
|
|
isEventLogsEnabled: this._safeGetItem('isEventLogsEnabled'),
|
|
|
|
// Energy saver settings
|
|
energySaverMode: this._safeGetItem('energySaverMode'),
|
|
autoEnergySaver: this._safeGetItem('autoEnergySaver'),
|
|
},
|
|
|
|
// UI configuration
|
|
ui: {
|
|
// Tab configuration
|
|
bolt_tab_configuration: this._safeGetItem('bolt_tab_configuration'),
|
|
tabConfiguration: allCookies.tabConfiguration,
|
|
|
|
// Prompt settings
|
|
promptId: this._safeGetItem('promptId'),
|
|
cachedPrompt: allCookies.cachedPrompt,
|
|
},
|
|
|
|
// Connections
|
|
connections: {
|
|
// Netlify connection
|
|
netlify_connection: this._safeGetItem('netlify_connection'),
|
|
|
|
// GitHub connections
|
|
...this._getGitHubConnections(allCookies),
|
|
},
|
|
|
|
// Debug and logs
|
|
debug: {
|
|
// Debug settings
|
|
isDebugEnabled: allCookies.isDebugEnabled,
|
|
acknowledged_debug_issues: this._safeGetItem('bolt_acknowledged_debug_issues'),
|
|
acknowledged_connection_issue: this._safeGetItem('bolt_acknowledged_connection_issue'),
|
|
|
|
// Error logs
|
|
error_logs: this._safeGetItem('error_logs'),
|
|
bolt_read_logs: this._safeGetItem('bolt_read_logs'),
|
|
|
|
// Event logs
|
|
eventLogs: allCookies.eventLogs,
|
|
},
|
|
|
|
// Update settings
|
|
updates: {
|
|
update_settings: this._safeGetItem('update_settings'),
|
|
last_acknowledged_update: this._safeGetItem('bolt_last_acknowledged_version'),
|
|
},
|
|
|
|
// Chat snapshots (for chat history)
|
|
chatSnapshots: this._getChatSnapshots(),
|
|
|
|
// Raw data (for debugging and complete backup)
|
|
_raw: {
|
|
localStorage: this._getAllLocalStorage(),
|
|
cookies: allCookies,
|
|
},
|
|
|
|
// Export metadata
|
|
_meta: {
|
|
exportDate: new Date().toISOString(),
|
|
version: '2.0',
|
|
appVersion: process.env.NEXT_PUBLIC_VERSION || 'unknown',
|
|
},
|
|
};
|
|
} catch (error) {
|
|
console.error('Error exporting settings:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Import settings from a JSON file
|
|
* @param importedData The imported data
|
|
*/
|
|
static async importSettings(importedData: any): Promise<void> {
|
|
// Check if this is the new comprehensive format (v2.0)
|
|
const isNewFormat = importedData._meta?.version === '2.0';
|
|
|
|
if (isNewFormat) {
|
|
// Import using the new comprehensive format
|
|
await this._importComprehensiveFormat(importedData);
|
|
} else {
|
|
// Try to handle older formats
|
|
await this._importLegacyFormat(importedData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Import API keys from a JSON file
|
|
* @param keys The API keys to import
|
|
*/
|
|
static importAPIKeys(keys: Record<string, any>): Record<string, string> {
|
|
// Get existing keys from cookies
|
|
const existingKeys = (() => {
|
|
const storedApiKeys = Cookies.get('apiKeys');
|
|
return storedApiKeys ? JSON.parse(storedApiKeys) : {};
|
|
})();
|
|
|
|
// Validate and save each key
|
|
const newKeys = { ...existingKeys };
|
|
Object.entries(keys).forEach(([key, value]) => {
|
|
// Skip comment fields
|
|
if (key.startsWith('_')) {
|
|
return;
|
|
}
|
|
|
|
// Skip base URL fields (they should be set in .env.local)
|
|
if (key.includes('_API_BASE_URL')) {
|
|
return;
|
|
}
|
|
|
|
if (typeof value !== 'string') {
|
|
throw new Error(`Invalid value for key: ${key}`);
|
|
}
|
|
|
|
// Handle both old and new template formats
|
|
let normalizedKey = key;
|
|
|
|
// Check if this is the old format (e.g., "Anthropic_API_KEY")
|
|
if (key.includes('_API_KEY')) {
|
|
// Extract the provider name from the old format
|
|
normalizedKey = key.replace('_API_KEY', '');
|
|
}
|
|
|
|
/*
|
|
* Only add non-empty keys
|
|
* Use the normalized key in the correct format
|
|
* (e.g., "OpenAI", "Google", "Anthropic")
|
|
*/
|
|
if (value) {
|
|
newKeys[normalizedKey] = value;
|
|
}
|
|
});
|
|
|
|
return newKeys;
|
|
}
|
|
|
|
/**
|
|
* Create an API keys template
|
|
* @returns The API keys template
|
|
*/
|
|
static createAPIKeysTemplate(): Record<string, any> {
|
|
/*
|
|
* Create a template with provider names as keys
|
|
* This matches how the application stores API keys in cookies
|
|
*/
|
|
const template = {
|
|
Anthropic: '',
|
|
OpenAI: '',
|
|
Google: '',
|
|
Groq: '',
|
|
HuggingFace: '',
|
|
OpenRouter: '',
|
|
Deepseek: '',
|
|
Mistral: '',
|
|
OpenAILike: '',
|
|
Together: '',
|
|
xAI: '',
|
|
Perplexity: '',
|
|
Cohere: '',
|
|
AzureOpenAI: '',
|
|
};
|
|
|
|
// Add a comment to explain the format
|
|
return {
|
|
_comment:
|
|
"Fill in your API keys for each provider. Keys will be stored with the provider name (e.g., 'OpenAI'). The application also supports the older format with keys like 'OpenAI_API_KEY' for backward compatibility.",
|
|
...template,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Reset all settings to default values
|
|
* @param db The IndexedDB database instance
|
|
*/
|
|
static async resetAllSettings(db: IDBDatabase): Promise<void> {
|
|
// 1. Clear all localStorage items related to application settings
|
|
const localStorageKeysToPreserve: string[] = ['debug_mode']; // Keys to preserve if needed
|
|
|
|
// Get all localStorage keys
|
|
const allLocalStorageKeys = Object.keys(localStorage);
|
|
|
|
// Clear all localStorage items except those to preserve
|
|
allLocalStorageKeys.forEach((key) => {
|
|
if (!localStorageKeysToPreserve.includes(key)) {
|
|
try {
|
|
localStorage.removeItem(key);
|
|
} catch (err) {
|
|
console.error(`Error removing localStorage item ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 2. Clear all cookies related to application settings
|
|
const cookiesToPreserve: string[] = []; // Cookies to preserve if needed
|
|
|
|
// Get all cookies
|
|
const allCookies = Cookies.get();
|
|
const cookieKeys = Object.keys(allCookies);
|
|
|
|
// Clear all cookies except those to preserve
|
|
cookieKeys.forEach((key) => {
|
|
if (!cookiesToPreserve.includes(key)) {
|
|
try {
|
|
Cookies.remove(key);
|
|
} catch (err) {
|
|
console.error(`Error removing cookie ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 3. Clear all data from IndexedDB
|
|
if (!db) {
|
|
console.warn('Database not initialized, skipping IndexedDB reset');
|
|
} else {
|
|
// Get all chats and delete them
|
|
const chats = await getAllChats(db);
|
|
|
|
const deletePromises = chats.map((chat) => deleteChat(db, chat.id));
|
|
await Promise.all(deletePromises);
|
|
}
|
|
|
|
// 4. Clear any chat snapshots
|
|
const snapshotKeys = Object.keys(localStorage).filter((key) => key.startsWith('snapshot:'));
|
|
snapshotKeys.forEach((key) => {
|
|
try {
|
|
localStorage.removeItem(key);
|
|
} catch (err) {
|
|
console.error(`Error removing snapshot ${key}:`, err);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Delete all chats from the database
|
|
* @param db The IndexedDB database instance
|
|
*/
|
|
static async deleteAllChats(db: IDBDatabase): Promise<void> {
|
|
// Clear chat history from localStorage
|
|
localStorage.removeItem('bolt_chat_history');
|
|
|
|
// Clear chats from IndexedDB
|
|
if (!db) {
|
|
throw new Error('Database not initialized');
|
|
}
|
|
|
|
// Get all chats and delete them one by one
|
|
const chats = await getAllChats(db);
|
|
const deletePromises = chats.map((chat) => deleteChat(db, chat.id));
|
|
await Promise.all(deletePromises);
|
|
}
|
|
|
|
// Private helper methods
|
|
|
|
/**
|
|
* Import settings from a comprehensive format
|
|
* @param data The imported data
|
|
*/
|
|
private static async _importComprehensiveFormat(data: any): Promise<void> {
|
|
// Import core settings
|
|
if (data.core) {
|
|
Object.entries(data.core).forEach(([key, value]) => {
|
|
if (value !== null && value !== undefined) {
|
|
try {
|
|
this._safeSetItem(key, value);
|
|
} catch (err) {
|
|
console.error(`Error importing core setting ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Import provider settings
|
|
if (data.providers) {
|
|
// Import provider_settings to localStorage
|
|
if (data.providers.provider_settings) {
|
|
try {
|
|
this._safeSetItem('provider_settings', data.providers.provider_settings);
|
|
} catch (err) {
|
|
console.error('Error importing provider settings:', err);
|
|
}
|
|
}
|
|
|
|
// Import API keys and other provider cookies
|
|
const providerCookies = ['apiKeys', 'selectedModel', 'selectedProvider', 'providers'];
|
|
providerCookies.forEach((key) => {
|
|
if (data.providers[key]) {
|
|
try {
|
|
this._safeSetCookie(key, data.providers[key]);
|
|
} catch (err) {
|
|
console.error(`Error importing provider cookie ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Import feature settings
|
|
if (data.features) {
|
|
Object.entries(data.features).forEach(([key, value]) => {
|
|
if (value !== null && value !== undefined) {
|
|
try {
|
|
this._safeSetItem(key, value);
|
|
} catch (err) {
|
|
console.error(`Error importing feature setting ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Import UI configuration
|
|
if (data.ui) {
|
|
// Import localStorage UI settings
|
|
if (data.ui.bolt_tab_configuration) {
|
|
try {
|
|
this._safeSetItem('bolt_tab_configuration', data.ui.bolt_tab_configuration);
|
|
} catch (err) {
|
|
console.error('Error importing tab configuration:', err);
|
|
}
|
|
}
|
|
|
|
if (data.ui.promptId) {
|
|
try {
|
|
this._safeSetItem('promptId', data.ui.promptId);
|
|
} catch (err) {
|
|
console.error('Error importing prompt ID:', err);
|
|
}
|
|
}
|
|
|
|
// Import UI cookies
|
|
const uiCookies = ['tabConfiguration', 'cachedPrompt'];
|
|
uiCookies.forEach((key) => {
|
|
if (data.ui[key]) {
|
|
try {
|
|
this._safeSetCookie(key, data.ui[key]);
|
|
} catch (err) {
|
|
console.error(`Error importing UI cookie ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Import connections
|
|
if (data.connections) {
|
|
// Import Netlify connection
|
|
if (data.connections.netlify_connection) {
|
|
try {
|
|
this._safeSetItem('netlify_connection', data.connections.netlify_connection);
|
|
} catch (err) {
|
|
console.error('Error importing Netlify connection:', err);
|
|
}
|
|
}
|
|
|
|
// Import GitHub connections
|
|
Object.entries(data.connections).forEach(([key, value]) => {
|
|
if (key.startsWith('github_') && value !== null && value !== undefined) {
|
|
try {
|
|
this._safeSetItem(key, value);
|
|
} catch (err) {
|
|
console.error(`Error importing GitHub connection ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Import debug settings
|
|
if (data.debug) {
|
|
// Import debug localStorage settings
|
|
const debugLocalStorageKeys = [
|
|
'bolt_acknowledged_debug_issues',
|
|
'bolt_acknowledged_connection_issue',
|
|
'error_logs',
|
|
'bolt_read_logs',
|
|
];
|
|
|
|
debugLocalStorageKeys.forEach((key) => {
|
|
if (data.debug[key] !== null && data.debug[key] !== undefined) {
|
|
try {
|
|
this._safeSetItem(key, data.debug[key]);
|
|
} catch (err) {
|
|
console.error(`Error importing debug setting ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Import debug cookies
|
|
const debugCookies = ['isDebugEnabled', 'eventLogs'];
|
|
debugCookies.forEach((key) => {
|
|
if (data.debug[key]) {
|
|
try {
|
|
this._safeSetCookie(key, data.debug[key]);
|
|
} catch (err) {
|
|
console.error(`Error importing debug cookie ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Import update settings
|
|
if (data.updates) {
|
|
if (data.updates.update_settings) {
|
|
try {
|
|
this._safeSetItem('update_settings', data.updates.update_settings);
|
|
} catch (err) {
|
|
console.error('Error importing update settings:', err);
|
|
}
|
|
}
|
|
|
|
if (data.updates.last_acknowledged_update) {
|
|
try {
|
|
this._safeSetItem('bolt_last_acknowledged_version', data.updates.last_acknowledged_update);
|
|
} catch (err) {
|
|
console.error('Error importing last acknowledged update:', err);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Import chat snapshots
|
|
if (data.chatSnapshots) {
|
|
Object.entries(data.chatSnapshots).forEach(([key, value]) => {
|
|
if (value !== null && value !== undefined) {
|
|
try {
|
|
this._safeSetItem(key, value);
|
|
} catch (err) {
|
|
console.error(`Error importing chat snapshot ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Import settings from a legacy format
|
|
* @param data The imported data
|
|
*/
|
|
private static async _importLegacyFormat(data: any): Promise<void> {
|
|
/**
|
|
* Handle legacy format (v1.0 or earlier)
|
|
* This is a simplified version that tries to import whatever is available
|
|
*/
|
|
|
|
// Try to import settings directly
|
|
Object.entries(data).forEach(([key, value]) => {
|
|
if (value !== null && value !== undefined) {
|
|
// Skip metadata fields
|
|
if (key === 'exportDate' || key === 'version' || key === 'appVersion') {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Try to determine if this should be a cookie or localStorage item
|
|
const isCookie = [
|
|
'apiKeys',
|
|
'selectedModel',
|
|
'selectedProvider',
|
|
'providers',
|
|
'tabConfiguration',
|
|
'cachedPrompt',
|
|
'isDebugEnabled',
|
|
'eventLogs',
|
|
].includes(key);
|
|
|
|
if (isCookie) {
|
|
this._safeSetCookie(key, value);
|
|
} else {
|
|
this._safeSetItem(key, value);
|
|
}
|
|
} catch (err) {
|
|
console.error(`Error importing legacy setting ${key}:`, err);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Safely get an item from localStorage
|
|
* @param key The key to get
|
|
* @returns The value or null if not found
|
|
*/
|
|
private static _safeGetItem(key: string): any {
|
|
try {
|
|
const item = localStorage.getItem(key);
|
|
return item ? JSON.parse(item) : null;
|
|
} catch (err) {
|
|
console.error(`Error getting localStorage item ${key}:`, err);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all localStorage items
|
|
* @returns All localStorage items
|
|
*/
|
|
private static _getAllLocalStorage(): Record<string, any> {
|
|
const result: Record<string, any> = {};
|
|
|
|
try {
|
|
for (let i = 0; i < localStorage.length; i++) {
|
|
const key = localStorage.key(i);
|
|
|
|
if (key) {
|
|
try {
|
|
const value = localStorage.getItem(key);
|
|
result[key] = value ? JSON.parse(value) : null;
|
|
} catch {
|
|
result[key] = null;
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error('Error getting all localStorage items:', err);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get GitHub connections from cookies
|
|
* @param _cookies The cookies object
|
|
* @returns GitHub connections
|
|
*/
|
|
private static _getGitHubConnections(_cookies: Record<string, string>): Record<string, any> {
|
|
const result: Record<string, any> = {};
|
|
|
|
// Get GitHub connections from localStorage
|
|
const localStorageKeys = Object.keys(localStorage).filter((key) => key.startsWith('github_'));
|
|
localStorageKeys.forEach((key) => {
|
|
try {
|
|
const value = localStorage.getItem(key);
|
|
result[key] = value ? JSON.parse(value) : null;
|
|
} catch (err) {
|
|
console.error(`Error getting GitHub connection ${key}:`, err);
|
|
result[key] = null;
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get chat snapshots from localStorage
|
|
* @returns Chat snapshots
|
|
*/
|
|
private static _getChatSnapshots(): Record<string, any> {
|
|
const result: Record<string, any> = {};
|
|
|
|
// Get chat snapshots from localStorage
|
|
const snapshotKeys = Object.keys(localStorage).filter((key) => key.startsWith('snapshot:'));
|
|
snapshotKeys.forEach((key) => {
|
|
try {
|
|
const value = localStorage.getItem(key);
|
|
result[key] = value ? JSON.parse(value) : null;
|
|
} catch (err) {
|
|
console.error(`Error getting chat snapshot ${key}:`, err);
|
|
result[key] = null;
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Safely set an item in localStorage
|
|
* @param key The key to set
|
|
* @param value The value to set
|
|
*/
|
|
private static _safeSetItem(key: string, value: any): void {
|
|
try {
|
|
localStorage.setItem(key, JSON.stringify(value));
|
|
} catch (err) {
|
|
console.error(`Error setting localStorage item ${key}:`, err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Safely set a cookie
|
|
* @param key The key to set
|
|
* @param value The value to set
|
|
*/
|
|
private static _safeSetCookie(key: string, value: any): void {
|
|
try {
|
|
Cookies.set(key, typeof value === 'string' ? value : JSON.stringify(value), { expires: 365 });
|
|
} catch (err) {
|
|
console.error(`Error setting cookie ${key}:`, err);
|
|
}
|
|
}
|
|
}
|