lsh-framework 3.2.4 → 3.5.0

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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +72 -34
  3. package/dist/commands/ipfs.js +7 -12
  4. package/dist/commands/sync.js +51 -39
  5. package/dist/constants/config.js +3 -0
  6. package/dist/lib/floating-point-arithmetic.js +2 -2
  7. package/dist/lib/ipfs-client-manager.js +51 -13
  8. package/dist/lib/ipfs-secrets-storage.js +21 -16
  9. package/dist/lib/ipfs-sync.js +88 -14
  10. package/dist/lib/secrets-manager.js +117 -47
  11. package/dist/lib/sync-key-store.js +87 -0
  12. package/dist/services/secrets/secrets.js +77 -39
  13. package/package.json +16 -16
  14. package/dist/__tests__/fixtures/job-fixtures.js +0 -204
  15. package/dist/__tests__/fixtures/supabase-mocks.js +0 -252
  16. package/dist/daemon/job-registry.js +0 -556
  17. package/dist/daemon/lshd.js +0 -968
  18. package/dist/daemon/saas-api-routes.js +0 -599
  19. package/dist/daemon/saas-api-server.js +0 -231
  20. package/dist/examples/supabase-integration.js +0 -106
  21. package/dist/lib/api-response.js +0 -226
  22. package/dist/lib/base-command-registrar.js +0 -287
  23. package/dist/lib/base-job-manager.js +0 -295
  24. package/dist/lib/cloud-config-manager.js +0 -348
  25. package/dist/lib/cron-job-manager.js +0 -368
  26. package/dist/lib/daemon-client-helper.js +0 -145
  27. package/dist/lib/daemon-client.js +0 -513
  28. package/dist/lib/database-persistence.js +0 -727
  29. package/dist/lib/database-schema.js +0 -259
  30. package/dist/lib/database-types.js +0 -90
  31. package/dist/lib/enhanced-history-system.js +0 -247
  32. package/dist/lib/history-system.js +0 -246
  33. package/dist/lib/job-manager.js +0 -436
  34. package/dist/lib/job-storage-database.js +0 -164
  35. package/dist/lib/job-storage-memory.js +0 -73
  36. package/dist/lib/local-storage-adapter.js +0 -507
  37. package/dist/lib/optimized-job-scheduler.js +0 -356
  38. package/dist/lib/saas-audit.js +0 -215
  39. package/dist/lib/saas-auth.js +0 -465
  40. package/dist/lib/saas-billing.js +0 -503
  41. package/dist/lib/saas-email.js +0 -403
  42. package/dist/lib/saas-encryption.js +0 -221
  43. package/dist/lib/saas-organizations.js +0 -662
  44. package/dist/lib/saas-secrets.js +0 -408
  45. package/dist/lib/saas-types.js +0 -165
  46. package/dist/lib/supabase-client.js +0 -125
  47. package/dist/lib/supabase-utils.js +0 -396
  48. package/dist/services/cron/cron-registrar.js +0 -240
  49. package/dist/services/cron/cron.js +0 -9
  50. package/dist/services/daemon/daemon-registrar.js +0 -585
  51. package/dist/services/daemon/daemon.js +0 -9
  52. package/dist/services/supabase/supabase-registrar.js +0 -375
  53. 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;