lsh-framework 3.2.5 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +72 -34
- package/dist/commands/ipfs.js +7 -12
- package/dist/commands/self.js +22 -16
- package/dist/commands/sync.js +49 -38
- package/dist/constants/config.js +3 -0
- package/dist/lib/floating-point-arithmetic.js +2 -2
- package/dist/lib/ipfs-client-manager.js +51 -13
- package/dist/lib/ipfs-secrets-storage.js +21 -16
- package/dist/lib/ipfs-sync.js +88 -14
- package/dist/lib/secrets-manager.js +117 -47
- package/dist/lib/sync-key-store.js +87 -0
- package/dist/services/secrets/secrets.js +77 -39
- package/package.json +16 -16
- package/dist/__tests__/fixtures/job-fixtures.js +0 -204
- package/dist/__tests__/fixtures/supabase-mocks.js +0 -252
- package/dist/daemon/job-registry.js +0 -556
- package/dist/daemon/lshd.js +0 -968
- package/dist/daemon/saas-api-routes.js +0 -599
- package/dist/daemon/saas-api-server.js +0 -231
- package/dist/examples/supabase-integration.js +0 -106
- package/dist/lib/api-response.js +0 -226
- package/dist/lib/base-command-registrar.js +0 -287
- package/dist/lib/base-job-manager.js +0 -295
- package/dist/lib/cloud-config-manager.js +0 -348
- package/dist/lib/cron-job-manager.js +0 -368
- package/dist/lib/daemon-client-helper.js +0 -145
- package/dist/lib/daemon-client.js +0 -513
- package/dist/lib/database-persistence.js +0 -727
- package/dist/lib/database-schema.js +0 -259
- package/dist/lib/database-types.js +0 -90
- package/dist/lib/enhanced-history-system.js +0 -247
- package/dist/lib/history-system.js +0 -246
- package/dist/lib/job-manager.js +0 -436
- package/dist/lib/job-storage-database.js +0 -164
- package/dist/lib/job-storage-memory.js +0 -73
- package/dist/lib/local-storage-adapter.js +0 -507
- package/dist/lib/optimized-job-scheduler.js +0 -356
- package/dist/lib/saas-audit.js +0 -215
- package/dist/lib/saas-auth.js +0 -465
- package/dist/lib/saas-billing.js +0 -503
- package/dist/lib/saas-email.js +0 -403
- package/dist/lib/saas-encryption.js +0 -221
- package/dist/lib/saas-organizations.js +0 -662
- package/dist/lib/saas-secrets.js +0 -408
- package/dist/lib/saas-types.js +0 -165
- package/dist/lib/supabase-client.js +0 -125
- package/dist/lib/supabase-utils.js +0 -396
- package/dist/services/cron/cron-registrar.js +0 -240
- package/dist/services/cron/cron.js +0 -9
- package/dist/services/daemon/daemon-registrar.js +0 -585
- package/dist/services/daemon/daemon.js +0 -9
- package/dist/services/supabase/supabase-registrar.js +0 -375
- package/dist/services/supabase/supabase.js +0 -9
|
@@ -1,727 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Database Persistence Layer for LSH
|
|
3
|
-
* Handles data synchronization with Supabase PostgreSQL or local storage fallback
|
|
4
|
-
*/
|
|
5
|
-
import { supabaseClient, isSupabaseConfigured } from './supabase-client.js';
|
|
6
|
-
import * as os from 'os';
|
|
7
|
-
import { LocalStorageAdapter } from './local-storage-adapter.js';
|
|
8
|
-
import { ENV_VARS } from '../constants/index.js';
|
|
9
|
-
export class DatabasePersistence {
|
|
10
|
-
client;
|
|
11
|
-
localStorage;
|
|
12
|
-
userId;
|
|
13
|
-
sessionId;
|
|
14
|
-
useLocalStorage;
|
|
15
|
-
constructor(userId) {
|
|
16
|
-
this.useLocalStorage = !isSupabaseConfigured();
|
|
17
|
-
if (this.useLocalStorage) {
|
|
18
|
-
// Using local storage is normal when Supabase is not configured
|
|
19
|
-
// Only show this message once per session to avoid noise
|
|
20
|
-
if (!process.env[ENV_VARS.LSH_LOCAL_STORAGE_QUIET]) {
|
|
21
|
-
console.log('ℹ️ Using local storage (Supabase not configured)');
|
|
22
|
-
}
|
|
23
|
-
this.localStorage = new LocalStorageAdapter(userId);
|
|
24
|
-
this.localStorage.initialize().catch(err => {
|
|
25
|
-
console.error('Failed to initialize local storage:', err);
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
// Supabase is configured, use it exclusively
|
|
30
|
-
try {
|
|
31
|
-
this.client = supabaseClient.getClient();
|
|
32
|
-
}
|
|
33
|
-
catch (error) {
|
|
34
|
-
// If Supabase is configured but fails, throw error instead of falling back
|
|
35
|
-
console.error('⚠️ Supabase is configured but connection failed:', error);
|
|
36
|
-
throw new Error('Supabase connection failed. Check your SUPABASE_URL and SUPABASE_ANON_KEY configuration.');
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
this.userId = userId ? this.generateUserUUID(userId) : undefined;
|
|
40
|
-
this.sessionId = this.generateSessionId();
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Generate a deterministic UUID from username
|
|
44
|
-
*/
|
|
45
|
-
generateUserUUID(username) {
|
|
46
|
-
// Create a simple UUID v5-like deterministic UUID from username
|
|
47
|
-
// In production, you'd use a proper UUID library
|
|
48
|
-
const hash = username.split('').reduce((a, b) => {
|
|
49
|
-
a = ((a << 5) - a) + b.charCodeAt(0);
|
|
50
|
-
return a & a;
|
|
51
|
-
}, 0);
|
|
52
|
-
const hex = Math.abs(hash).toString(16).padStart(8, '0');
|
|
53
|
-
return `${hex.substr(0, 8)}-${hex.substr(0, 4)}-4${hex.substr(1, 3)}-8${hex.substr(0, 3)}-${hex}${hex.substr(0, 4)}`;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Generate a unique session ID
|
|
57
|
-
*/
|
|
58
|
-
generateSessionId() {
|
|
59
|
-
return `lsh_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Initialize database schema
|
|
63
|
-
*/
|
|
64
|
-
async initializeSchema() {
|
|
65
|
-
try {
|
|
66
|
-
// Note: In a real implementation, you'd need to run the SQL schema
|
|
67
|
-
// This would typically be done through Supabase dashboard or migrations
|
|
68
|
-
console.log('Database schema initialization would be handled by Supabase migrations');
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
catch (error) {
|
|
72
|
-
console.error('Failed to initialize database schema:', error);
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Cleanup resources (stop timers, close connections)
|
|
78
|
-
* Call this when done to allow process to exit
|
|
79
|
-
*/
|
|
80
|
-
async cleanup() {
|
|
81
|
-
if (this.localStorage) {
|
|
82
|
-
await this.localStorage.cleanup();
|
|
83
|
-
}
|
|
84
|
-
// Supabase client doesn't need cleanup (no persistent connections)
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Save shell history entry
|
|
88
|
-
*/
|
|
89
|
-
async saveHistoryEntry(entry) {
|
|
90
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
91
|
-
return this.localStorage.saveHistoryEntry(entry);
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
const insertData = {
|
|
95
|
-
...entry,
|
|
96
|
-
session_id: this.sessionId,
|
|
97
|
-
created_at: new Date().toISOString(),
|
|
98
|
-
updated_at: new Date().toISOString(),
|
|
99
|
-
};
|
|
100
|
-
// Only include user_id if it's not undefined
|
|
101
|
-
if (this.userId !== undefined) {
|
|
102
|
-
insertData.user_id = this.userId;
|
|
103
|
-
}
|
|
104
|
-
const { error } = await this.client
|
|
105
|
-
.from('shell_history')
|
|
106
|
-
.insert([insertData]);
|
|
107
|
-
if (error) {
|
|
108
|
-
console.error('Failed to save history entry:', error);
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
console.error('Error saving history entry:', error);
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Get shell history entries
|
|
120
|
-
*/
|
|
121
|
-
async getHistoryEntries(limit = 100, offset = 0) {
|
|
122
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
123
|
-
return this.localStorage.getHistoryEntries(limit, offset);
|
|
124
|
-
}
|
|
125
|
-
try {
|
|
126
|
-
let query = this.client
|
|
127
|
-
.from('shell_history')
|
|
128
|
-
.select('*')
|
|
129
|
-
.order('timestamp', { ascending: false })
|
|
130
|
-
.range(offset, offset + limit - 1);
|
|
131
|
-
// Only filter by user_id if it's not undefined
|
|
132
|
-
if (this.userId !== undefined) {
|
|
133
|
-
query = query.eq('user_id', this.userId);
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
query = query.is('user_id', null);
|
|
137
|
-
}
|
|
138
|
-
const { data, error } = await query;
|
|
139
|
-
if (error) {
|
|
140
|
-
console.error('Failed to get history entries:', error);
|
|
141
|
-
return [];
|
|
142
|
-
}
|
|
143
|
-
return data || [];
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
console.error('Error getting history entries:', error);
|
|
147
|
-
return [];
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Save shell job
|
|
152
|
-
*/
|
|
153
|
-
async saveJob(job) {
|
|
154
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
155
|
-
return this.localStorage.saveJob(job);
|
|
156
|
-
}
|
|
157
|
-
try {
|
|
158
|
-
const insertData = {
|
|
159
|
-
...job,
|
|
160
|
-
session_id: this.sessionId,
|
|
161
|
-
created_at: new Date().toISOString(),
|
|
162
|
-
updated_at: new Date().toISOString(),
|
|
163
|
-
};
|
|
164
|
-
// Only include user_id if it's not undefined
|
|
165
|
-
if (this.userId !== undefined) {
|
|
166
|
-
insertData.user_id = this.userId;
|
|
167
|
-
}
|
|
168
|
-
const { error } = await this.client
|
|
169
|
-
.from('shell_jobs')
|
|
170
|
-
.insert([insertData]);
|
|
171
|
-
if (error) {
|
|
172
|
-
console.error('Failed to save job:', error);
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
console.error('Error saving job:', error);
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Update shell job status
|
|
184
|
-
*/
|
|
185
|
-
async updateJobStatus(jobId, status, exitCode) {
|
|
186
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
187
|
-
return this.localStorage.updateJobStatus(jobId, status, exitCode);
|
|
188
|
-
}
|
|
189
|
-
try {
|
|
190
|
-
const updateData = {
|
|
191
|
-
status,
|
|
192
|
-
updated_at: new Date().toISOString(),
|
|
193
|
-
};
|
|
194
|
-
if (status === 'completed' || status === 'failed') {
|
|
195
|
-
updateData.completed_at = new Date().toISOString();
|
|
196
|
-
if (exitCode !== undefined) {
|
|
197
|
-
updateData.exit_code = exitCode;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
let query = this.client
|
|
201
|
-
.from('shell_jobs')
|
|
202
|
-
.update(updateData)
|
|
203
|
-
.eq('job_id', jobId);
|
|
204
|
-
// Only filter by user_id if it's not undefined
|
|
205
|
-
if (this.userId !== undefined) {
|
|
206
|
-
query = query.eq('user_id', this.userId);
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
query = query.is('user_id', null);
|
|
210
|
-
}
|
|
211
|
-
const { error } = await query;
|
|
212
|
-
if (error) {
|
|
213
|
-
console.error('Failed to update job status:', error);
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
catch (error) {
|
|
219
|
-
console.error('Error updating job status:', error);
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Get active jobs
|
|
225
|
-
*/
|
|
226
|
-
async getActiveJobs() {
|
|
227
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
228
|
-
// Reload from disk to get latest data (in case written by another SecretsManager instance)
|
|
229
|
-
await this.localStorage.reload();
|
|
230
|
-
return this.localStorage.getActiveJobs();
|
|
231
|
-
}
|
|
232
|
-
try {
|
|
233
|
-
let query = this.client
|
|
234
|
-
.from('shell_jobs')
|
|
235
|
-
.select('*')
|
|
236
|
-
.in('status', ['running', 'stopped', 'completed', 'failed'])
|
|
237
|
-
.order('created_at', { ascending: false });
|
|
238
|
-
// Only filter by user_id if it's not undefined
|
|
239
|
-
if (this.userId !== undefined) {
|
|
240
|
-
query = query.eq('user_id', this.userId);
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
query = query.is('user_id', null);
|
|
244
|
-
}
|
|
245
|
-
const { data, error } = await query;
|
|
246
|
-
if (error) {
|
|
247
|
-
console.error('Failed to get active jobs:', error);
|
|
248
|
-
return [];
|
|
249
|
-
}
|
|
250
|
-
return data || [];
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
console.error('Error getting active jobs:', error);
|
|
254
|
-
return [];
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Save shell configuration
|
|
259
|
-
*/
|
|
260
|
-
async saveConfiguration(config) {
|
|
261
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
262
|
-
return this.localStorage.saveConfiguration(config);
|
|
263
|
-
}
|
|
264
|
-
try {
|
|
265
|
-
const upsertData = {
|
|
266
|
-
...config,
|
|
267
|
-
created_at: new Date().toISOString(),
|
|
268
|
-
updated_at: new Date().toISOString(),
|
|
269
|
-
};
|
|
270
|
-
// Only include user_id if it's not undefined
|
|
271
|
-
if (this.userId !== undefined) {
|
|
272
|
-
upsertData.user_id = this.userId;
|
|
273
|
-
}
|
|
274
|
-
const { error } = await this.client
|
|
275
|
-
.from('shell_configuration')
|
|
276
|
-
.upsert([upsertData], {
|
|
277
|
-
onConflict: 'user_id,config_key'
|
|
278
|
-
});
|
|
279
|
-
if (error) {
|
|
280
|
-
console.error('Failed to save configuration:', error);
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
catch (error) {
|
|
286
|
-
console.error('Error saving configuration:', error);
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Get shell configuration
|
|
292
|
-
*/
|
|
293
|
-
async getConfiguration(key) {
|
|
294
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
295
|
-
return this.localStorage.getConfiguration(key);
|
|
296
|
-
}
|
|
297
|
-
try {
|
|
298
|
-
let query = this.client
|
|
299
|
-
.from('shell_configuration')
|
|
300
|
-
.select('*');
|
|
301
|
-
// Only filter by user_id if it's not undefined
|
|
302
|
-
if (this.userId !== undefined) {
|
|
303
|
-
query = query.eq('user_id', this.userId);
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
// If no user_id, get only default configurations
|
|
307
|
-
query = query.is('user_id', null);
|
|
308
|
-
}
|
|
309
|
-
if (key) {
|
|
310
|
-
query = query.eq('config_key', key);
|
|
311
|
-
}
|
|
312
|
-
const { data, error } = await query;
|
|
313
|
-
if (error) {
|
|
314
|
-
console.error('Failed to get configuration:', error);
|
|
315
|
-
return [];
|
|
316
|
-
}
|
|
317
|
-
return data || [];
|
|
318
|
-
}
|
|
319
|
-
catch (error) {
|
|
320
|
-
console.error('Error getting configuration:', error);
|
|
321
|
-
return [];
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Save shell alias
|
|
326
|
-
*/
|
|
327
|
-
async saveAlias(alias) {
|
|
328
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
329
|
-
return this.localStorage.saveAlias(alias);
|
|
330
|
-
}
|
|
331
|
-
try {
|
|
332
|
-
const upsertData = {
|
|
333
|
-
...alias,
|
|
334
|
-
created_at: new Date().toISOString(),
|
|
335
|
-
updated_at: new Date().toISOString(),
|
|
336
|
-
};
|
|
337
|
-
// Only include user_id if it's not undefined
|
|
338
|
-
if (this.userId !== undefined) {
|
|
339
|
-
upsertData.user_id = this.userId;
|
|
340
|
-
}
|
|
341
|
-
const { error } = await this.client
|
|
342
|
-
.from('shell_aliases')
|
|
343
|
-
.upsert([upsertData], {
|
|
344
|
-
onConflict: 'user_id,alias_name'
|
|
345
|
-
});
|
|
346
|
-
if (error) {
|
|
347
|
-
console.error('Failed to save alias:', error);
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
350
|
-
return true;
|
|
351
|
-
}
|
|
352
|
-
catch (error) {
|
|
353
|
-
console.error('Error saving alias:', error);
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Get shell aliases
|
|
359
|
-
*/
|
|
360
|
-
async getAliases() {
|
|
361
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
362
|
-
return this.localStorage.getAliases();
|
|
363
|
-
}
|
|
364
|
-
try {
|
|
365
|
-
let query = this.client
|
|
366
|
-
.from('shell_aliases')
|
|
367
|
-
.select('*')
|
|
368
|
-
.eq('is_active', true);
|
|
369
|
-
// Only filter by user_id if it's not undefined
|
|
370
|
-
if (this.userId !== undefined) {
|
|
371
|
-
query = query.eq('user_id', this.userId);
|
|
372
|
-
}
|
|
373
|
-
else {
|
|
374
|
-
query = query.is('user_id', null);
|
|
375
|
-
}
|
|
376
|
-
const { data, error } = await query;
|
|
377
|
-
if (error) {
|
|
378
|
-
console.error('Failed to get aliases:', error);
|
|
379
|
-
return [];
|
|
380
|
-
}
|
|
381
|
-
return data || [];
|
|
382
|
-
}
|
|
383
|
-
catch (error) {
|
|
384
|
-
console.error('Error getting aliases:', error);
|
|
385
|
-
return [];
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Save shell function
|
|
390
|
-
*/
|
|
391
|
-
async saveFunction(func) {
|
|
392
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
393
|
-
return this.localStorage.saveFunction(func);
|
|
394
|
-
}
|
|
395
|
-
try {
|
|
396
|
-
const upsertData = {
|
|
397
|
-
...func,
|
|
398
|
-
created_at: new Date().toISOString(),
|
|
399
|
-
updated_at: new Date().toISOString(),
|
|
400
|
-
};
|
|
401
|
-
// Only include user_id if it's not undefined
|
|
402
|
-
if (this.userId !== undefined) {
|
|
403
|
-
upsertData.user_id = this.userId;
|
|
404
|
-
}
|
|
405
|
-
const { error } = await this.client
|
|
406
|
-
.from('shell_functions')
|
|
407
|
-
.upsert([upsertData], {
|
|
408
|
-
onConflict: 'user_id,function_name'
|
|
409
|
-
});
|
|
410
|
-
if (error) {
|
|
411
|
-
console.error('Failed to save function:', error);
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
return true;
|
|
415
|
-
}
|
|
416
|
-
catch (error) {
|
|
417
|
-
console.error('Error saving function:', error);
|
|
418
|
-
return false;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* Get shell functions
|
|
423
|
-
*/
|
|
424
|
-
async getFunctions() {
|
|
425
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
426
|
-
return this.localStorage.getFunctions();
|
|
427
|
-
}
|
|
428
|
-
try {
|
|
429
|
-
let query = this.client
|
|
430
|
-
.from('shell_functions')
|
|
431
|
-
.select('*')
|
|
432
|
-
.eq('is_active', true);
|
|
433
|
-
// Only filter by user_id if it's not undefined
|
|
434
|
-
if (this.userId !== undefined) {
|
|
435
|
-
query = query.eq('user_id', this.userId);
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
query = query.is('user_id', null);
|
|
439
|
-
}
|
|
440
|
-
const { data, error } = await query;
|
|
441
|
-
if (error) {
|
|
442
|
-
console.error('Failed to get functions:', error);
|
|
443
|
-
return [];
|
|
444
|
-
}
|
|
445
|
-
return data || [];
|
|
446
|
-
}
|
|
447
|
-
catch (error) {
|
|
448
|
-
console.error('Error getting functions:', error);
|
|
449
|
-
return [];
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Start a new shell session
|
|
454
|
-
*/
|
|
455
|
-
async startSession(workingDirectory, environmentVariables) {
|
|
456
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
457
|
-
return this.localStorage.startSession(workingDirectory, environmentVariables);
|
|
458
|
-
}
|
|
459
|
-
try {
|
|
460
|
-
const insertData = {
|
|
461
|
-
session_id: this.sessionId,
|
|
462
|
-
hostname: os.hostname(),
|
|
463
|
-
working_directory: workingDirectory,
|
|
464
|
-
environment_variables: environmentVariables,
|
|
465
|
-
started_at: new Date().toISOString(),
|
|
466
|
-
is_active: true,
|
|
467
|
-
created_at: new Date().toISOString(),
|
|
468
|
-
updated_at: new Date().toISOString(),
|
|
469
|
-
};
|
|
470
|
-
// Only include user_id if it's not undefined
|
|
471
|
-
if (this.userId !== undefined) {
|
|
472
|
-
insertData.user_id = this.userId;
|
|
473
|
-
}
|
|
474
|
-
const { error } = await this.client
|
|
475
|
-
.from('shell_sessions')
|
|
476
|
-
.insert([insertData]);
|
|
477
|
-
if (error) {
|
|
478
|
-
console.error('Failed to start session:', error);
|
|
479
|
-
return false;
|
|
480
|
-
}
|
|
481
|
-
return true;
|
|
482
|
-
}
|
|
483
|
-
catch (error) {
|
|
484
|
-
console.error('Error starting session:', error);
|
|
485
|
-
return false;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* End the current shell session
|
|
490
|
-
*/
|
|
491
|
-
async endSession() {
|
|
492
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
493
|
-
return this.localStorage.endSession();
|
|
494
|
-
}
|
|
495
|
-
try {
|
|
496
|
-
let query = this.client
|
|
497
|
-
.from('shell_sessions')
|
|
498
|
-
.update({
|
|
499
|
-
ended_at: new Date().toISOString(),
|
|
500
|
-
is_active: false,
|
|
501
|
-
updated_at: new Date().toISOString(),
|
|
502
|
-
})
|
|
503
|
-
.eq('session_id', this.sessionId);
|
|
504
|
-
// Only filter by user_id if it's not undefined
|
|
505
|
-
if (this.userId !== undefined) {
|
|
506
|
-
query = query.eq('user_id', this.userId);
|
|
507
|
-
}
|
|
508
|
-
else {
|
|
509
|
-
query = query.is('user_id', null);
|
|
510
|
-
}
|
|
511
|
-
const { error } = await query;
|
|
512
|
-
if (error) {
|
|
513
|
-
console.error('Failed to end session:', error);
|
|
514
|
-
return false;
|
|
515
|
-
}
|
|
516
|
-
return true;
|
|
517
|
-
}
|
|
518
|
-
catch (error) {
|
|
519
|
-
console.error('Error ending session:', error);
|
|
520
|
-
return false;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
/**
|
|
524
|
-
* Test database connectivity
|
|
525
|
-
*/
|
|
526
|
-
async testConnection() {
|
|
527
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
528
|
-
return this.localStorage.testConnection();
|
|
529
|
-
}
|
|
530
|
-
return await supabaseClient.testConnection();
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* Get session ID
|
|
534
|
-
*/
|
|
535
|
-
getSessionId() {
|
|
536
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
537
|
-
return this.localStorage.getSessionId();
|
|
538
|
-
}
|
|
539
|
-
return this.sessionId;
|
|
540
|
-
}
|
|
541
|
-
/**
|
|
542
|
-
* Get latest rows from all database tables
|
|
543
|
-
*/
|
|
544
|
-
async getLatestRows(limit = 5) {
|
|
545
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
546
|
-
return this.localStorage.getLatestRows(limit);
|
|
547
|
-
}
|
|
548
|
-
const result = {};
|
|
549
|
-
try {
|
|
550
|
-
// Get latest shell history entries
|
|
551
|
-
const historyQuery = this.client
|
|
552
|
-
.from('shell_history')
|
|
553
|
-
.select('*')
|
|
554
|
-
.order('created_at', { ascending: false })
|
|
555
|
-
.limit(limit);
|
|
556
|
-
if (this.userId !== undefined) {
|
|
557
|
-
historyQuery.eq('user_id', this.userId);
|
|
558
|
-
}
|
|
559
|
-
else {
|
|
560
|
-
historyQuery.is('user_id', null);
|
|
561
|
-
}
|
|
562
|
-
const { data: historyData } = await historyQuery;
|
|
563
|
-
result.shell_history = historyData || [];
|
|
564
|
-
// Get latest shell jobs
|
|
565
|
-
const jobsQuery = this.client
|
|
566
|
-
.from('shell_jobs')
|
|
567
|
-
.select('*')
|
|
568
|
-
.order('created_at', { ascending: false })
|
|
569
|
-
.limit(limit);
|
|
570
|
-
if (this.userId !== undefined) {
|
|
571
|
-
jobsQuery.eq('user_id', this.userId);
|
|
572
|
-
}
|
|
573
|
-
else {
|
|
574
|
-
jobsQuery.is('user_id', null);
|
|
575
|
-
}
|
|
576
|
-
const { data: jobsData } = await jobsQuery;
|
|
577
|
-
result.shell_jobs = jobsData || [];
|
|
578
|
-
// Get latest shell configuration
|
|
579
|
-
const configQuery = this.client
|
|
580
|
-
.from('shell_configuration')
|
|
581
|
-
.select('*')
|
|
582
|
-
.order('created_at', { ascending: false })
|
|
583
|
-
.limit(limit);
|
|
584
|
-
if (this.userId !== undefined) {
|
|
585
|
-
configQuery.eq('user_id', this.userId);
|
|
586
|
-
}
|
|
587
|
-
else {
|
|
588
|
-
configQuery.is('user_id', null);
|
|
589
|
-
}
|
|
590
|
-
const { data: configData } = await configQuery;
|
|
591
|
-
result.shell_configuration = configData || [];
|
|
592
|
-
// Get latest shell sessions
|
|
593
|
-
const sessionsQuery = this.client
|
|
594
|
-
.from('shell_sessions')
|
|
595
|
-
.select('*')
|
|
596
|
-
.order('created_at', { ascending: false })
|
|
597
|
-
.limit(limit);
|
|
598
|
-
if (this.userId !== undefined) {
|
|
599
|
-
sessionsQuery.eq('user_id', this.userId);
|
|
600
|
-
}
|
|
601
|
-
else {
|
|
602
|
-
sessionsQuery.is('user_id', null);
|
|
603
|
-
}
|
|
604
|
-
const { data: sessionsData } = await sessionsQuery;
|
|
605
|
-
result.shell_sessions = sessionsData || [];
|
|
606
|
-
// Get latest shell aliases
|
|
607
|
-
const aliasesQuery = this.client
|
|
608
|
-
.from('shell_aliases')
|
|
609
|
-
.select('*')
|
|
610
|
-
.order('created_at', { ascending: false })
|
|
611
|
-
.limit(limit);
|
|
612
|
-
if (this.userId !== undefined) {
|
|
613
|
-
aliasesQuery.eq('user_id', this.userId);
|
|
614
|
-
}
|
|
615
|
-
else {
|
|
616
|
-
aliasesQuery.is('user_id', null);
|
|
617
|
-
}
|
|
618
|
-
const { data: aliasesData } = await aliasesQuery;
|
|
619
|
-
result.shell_aliases = aliasesData || [];
|
|
620
|
-
// Get latest shell functions
|
|
621
|
-
const functionsQuery = this.client
|
|
622
|
-
.from('shell_functions')
|
|
623
|
-
.select('*')
|
|
624
|
-
.order('created_at', { ascending: false })
|
|
625
|
-
.limit(limit);
|
|
626
|
-
if (this.userId !== undefined) {
|
|
627
|
-
functionsQuery.eq('user_id', this.userId);
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
functionsQuery.is('user_id', null);
|
|
631
|
-
}
|
|
632
|
-
const { data: functionsData } = await functionsQuery;
|
|
633
|
-
result.shell_functions = functionsData || [];
|
|
634
|
-
// Get latest shell completions
|
|
635
|
-
const completionsQuery = this.client
|
|
636
|
-
.from('shell_completions')
|
|
637
|
-
.select('*')
|
|
638
|
-
.order('created_at', { ascending: false })
|
|
639
|
-
.limit(limit);
|
|
640
|
-
if (this.userId !== undefined) {
|
|
641
|
-
completionsQuery.eq('user_id', this.userId);
|
|
642
|
-
}
|
|
643
|
-
else {
|
|
644
|
-
completionsQuery.is('user_id', null);
|
|
645
|
-
}
|
|
646
|
-
const { data: completionsData } = await completionsQuery;
|
|
647
|
-
result.shell_completions = completionsData || [];
|
|
648
|
-
// Get latest politician trading disclosures (global data, no user filtering)
|
|
649
|
-
const { data: tradingData } = await this.client
|
|
650
|
-
.from('trading_disclosures')
|
|
651
|
-
.select('*')
|
|
652
|
-
.order('created_at', { ascending: false })
|
|
653
|
-
.limit(limit);
|
|
654
|
-
result.trading_disclosures = tradingData || [];
|
|
655
|
-
// Get latest politicians (global data, no user filtering)
|
|
656
|
-
const { data: politiciansData } = await this.client
|
|
657
|
-
.from('politicians')
|
|
658
|
-
.select('*')
|
|
659
|
-
.order('created_at', { ascending: false })
|
|
660
|
-
.limit(limit);
|
|
661
|
-
result.politicians = politiciansData || [];
|
|
662
|
-
// Get latest data pull jobs (global data, no user filtering)
|
|
663
|
-
const { data: dataPullJobsData } = await this.client
|
|
664
|
-
.from('data_pull_jobs')
|
|
665
|
-
.select('*')
|
|
666
|
-
.order('created_at', { ascending: false })
|
|
667
|
-
.limit(limit);
|
|
668
|
-
result.data_pull_jobs = dataPullJobsData || [];
|
|
669
|
-
return result;
|
|
670
|
-
}
|
|
671
|
-
catch (error) {
|
|
672
|
-
console.error('Error getting latest rows:', error);
|
|
673
|
-
return {};
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Get latest rows from a specific table
|
|
678
|
-
*/
|
|
679
|
-
async getLatestRowsFromTable(tableName, limit = 5) {
|
|
680
|
-
if (this.useLocalStorage && this.localStorage) {
|
|
681
|
-
return this.localStorage.getLatestRowsFromTable(tableName, limit);
|
|
682
|
-
}
|
|
683
|
-
try {
|
|
684
|
-
const validTables = [
|
|
685
|
-
'shell_history',
|
|
686
|
-
'shell_jobs',
|
|
687
|
-
'shell_configuration',
|
|
688
|
-
'shell_sessions',
|
|
689
|
-
'shell_aliases',
|
|
690
|
-
'shell_functions',
|
|
691
|
-
'shell_completions',
|
|
692
|
-
'trading_disclosures',
|
|
693
|
-
'politicians',
|
|
694
|
-
'data_pull_jobs'
|
|
695
|
-
];
|
|
696
|
-
if (!validTables.includes(tableName)) {
|
|
697
|
-
throw new Error(`Invalid table name: ${tableName}`);
|
|
698
|
-
}
|
|
699
|
-
let query = this.client
|
|
700
|
-
.from(tableName)
|
|
701
|
-
.select('*')
|
|
702
|
-
.order('created_at', { ascending: false })
|
|
703
|
-
.limit(limit);
|
|
704
|
-
// Filter by user_id if available (except for politician trading tables which are global)
|
|
705
|
-
const globalTables = ['trading_disclosures', 'politicians', 'data_pull_jobs'];
|
|
706
|
-
if (!globalTables.includes(tableName)) {
|
|
707
|
-
if (this.userId !== undefined) {
|
|
708
|
-
query = query.eq('user_id', this.userId);
|
|
709
|
-
}
|
|
710
|
-
else {
|
|
711
|
-
query = query.is('user_id', null);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
const { data, error } = await query;
|
|
715
|
-
if (error) {
|
|
716
|
-
console.error(`Failed to get latest rows from ${tableName}:`, error);
|
|
717
|
-
return [];
|
|
718
|
-
}
|
|
719
|
-
return data || [];
|
|
720
|
-
}
|
|
721
|
-
catch (error) {
|
|
722
|
-
console.error(`Error getting latest rows from ${tableName}:`, error);
|
|
723
|
-
return [];
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
export default DatabasePersistence;
|