File size: 4,367 Bytes
f0743f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { LocalStorageKeys } from 'librechat-data-provider';

/** Suffix for timestamp entries */
const TIMESTAMP_SUFFIX = '_TIMESTAMP';

/** Duration in milliseconds (2 days) */
const CLEANUP_THRESHOLD = 2 * 24 * 60 * 60 * 1000;

/**
 * Storage keys that should be cleaned up based on timestamps
 * These are conversation-specific keys that can accumulate over time
 */
const TIMESTAMPED_KEYS = [
  LocalStorageKeys.LAST_MCP_,
  LocalStorageKeys.LAST_CODE_TOGGLE_,
  LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_,
  LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_,
  LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_,
  LocalStorageKeys.PIN_MCP_,
];

/**
 * Set only a timestamp for a key (when the value is handled elsewhere)
 */
export function setTimestamp(key: string): void {
  localStorage.setItem(`${key}${TIMESTAMP_SUFFIX}`, Date.now().toString());
}

/**
 * Set a value in localStorage with an associated timestamp
 */
export function setTimestampedValue(key: string, value: any): void {
  localStorage.setItem(key, typeof value === 'string' ? value : JSON.stringify(value));
  localStorage.setItem(`${key}${TIMESTAMP_SUFFIX}`, Date.now().toString());
}

/**
 * Get a value from localStorage, checking if it has a valid timestamp
 * Returns null if the value is too old or has no timestamp
 */
export function getTimestampedValue(key: string): string | null {
  const timestampKey = `${key}${TIMESTAMP_SUFFIX}`;
  const timestamp = localStorage.getItem(timestampKey);

  if (!timestamp) {
    // No timestamp exists, return the value but it will be cleaned up on next startup
    return localStorage.getItem(key);
  }

  const age = Date.now() - parseInt(timestamp, 10);
  if (age > CLEANUP_THRESHOLD) {
    // Value is too old, clean it up
    localStorage.removeItem(key);
    localStorage.removeItem(timestampKey);
    return null;
  }

  return localStorage.getItem(key);
}

/**
 * Remove a value and its timestamp from localStorage
 */
export function removeTimestampedValue(key: string): void {
  localStorage.removeItem(key);
  localStorage.removeItem(`${key}${TIMESTAMP_SUFFIX}`);
}

/**
 * Clean up old localStorage entries based on timestamps
 * This should be called on app startup
 */
export function cleanupTimestampedStorage(): void {
  try {
    const keysToRemove: string[] = [];
    const now = Date.now();

    // Iterate through all localStorage keys
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      if (!key) continue;
      if (key === LocalStorageKeys.PIN_MCP_) {
        continue;
      }

      // Check if this key should be timestamped
      const isTimestampedKey = TIMESTAMPED_KEYS.some(
        (prefix) => key.startsWith(prefix) && !key.includes('pinned'),
      );

      if (isTimestampedKey && !key.endsWith(TIMESTAMP_SUFFIX)) {
        const timestampKey = `${key}${TIMESTAMP_SUFFIX}`;
        const timestamp = localStorage.getItem(timestampKey);

        if (!timestamp) {
          // No timestamp exists for a key that should have one - mark for cleanup
          keysToRemove.push(key);
          continue;
        }

        const age = now - parseInt(timestamp, 10);
        if (age > CLEANUP_THRESHOLD) {
          // Entry is too old - mark for cleanup
          keysToRemove.push(key);
          keysToRemove.push(timestampKey);
        }
      }
    }

    keysToRemove.forEach((key) => localStorage.removeItem(key));

    if (keysToRemove.length > 0) {
      console.log(`Cleaned up ${keysToRemove.length} old localStorage entries`);
    }
  } catch (error) {
    console.error('Error during cleanup of timestamped storage:', error);
  }
}

/**
 * Migration function to add timestamps to existing entries
 * This ensures existing entries don't get immediately cleaned up
 */
export function migrateExistingEntries(): void {
  const now = Date.now().toString();

  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    if (!key) continue;

    const isTimestampedKey = TIMESTAMPED_KEYS.some((prefix) => key.startsWith(prefix));

    if (isTimestampedKey && !key.endsWith(TIMESTAMP_SUFFIX)) {
      const timestampKey = `${key}${TIMESTAMP_SUFFIX}`;
      const hasTimestamp = localStorage.getItem(timestampKey);

      if (!hasTimestamp) {
        // Add timestamp to existing entry
        localStorage.setItem(timestampKey, now);
      }
    }
  }
}