Kraft102 Claude Opus 4.5 commited on
Commit
4942106
·
1 Parent(s): 8674975

feat: add email inbox hook and dynamic API URLs for cloud deployment

Browse files

- Update useLiveData.ts with dynamic API/WebSocket URL detection
- Add useEmailInbox hook for email polling
- Add email source type and endpoint mapping
- Point to new HuggingFace Space (widgettdc-api)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

apps/backend/src/index.ts CHANGED
@@ -72,6 +72,7 @@ import { memoryRouter } from './services/memory/memoryController.js';
72
  import { sragRouter } from './services/srag/sragController.js';
73
  import { evolutionRouter } from './services/evolution/evolutionController.js';
74
  import { palRouter } from './services/pal/palController.js';
 
75
  import {
76
  cmaContextHandler,
77
  cmaIngestHandler,
 
72
  import { sragRouter } from './services/srag/sragController.js';
73
  import { evolutionRouter } from './services/evolution/evolutionController.js';
74
  import { palRouter } from './services/pal/palController.js';
75
+ import datasourcesRouter from './routes/datasources.js';
76
  import {
77
  cmaContextHandler,
78
  cmaIngestHandler,
apps/matrix-frontend/src/hooks/useLiveData.ts CHANGED
@@ -11,7 +11,35 @@
11
 
12
  import { useState, useEffect, useCallback, useRef } from 'react';
13
 
14
- const API_BASE = 'http://localhost:3001/api';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  // Source types that widgets can request
17
  export type DataSourceType =
@@ -24,7 +52,8 @@ export type DataSourceType =
24
  | 'systemHealth' // System services health
25
  | 'metrics' // Platform metrics
26
  | 'osint' // External OSINT feeds (future)
27
- | 'threatIntel'; // Threat intelligence (future)
 
28
 
29
  // Recommended external sources that can be fetched on demand
30
  export interface RecommendedSource {
@@ -68,6 +97,7 @@ const sourceEndpoints: Record<DataSourceType, string> = {
68
  metrics: '/sys/metrics',
69
  osint: '/acquisition/osint',
70
  threatIntel: '/acquisition/threats',
 
71
  };
72
 
73
  // Recommended sources per data type
@@ -100,6 +130,10 @@ const recommendedSourcesMap: Record<DataSourceType, RecommendedSource[]> = {
100
  { id: 'otx', name: 'AlienVault OTX', description: 'Open Threat Exchange IOCs', category: 'threatIntel', requiresApproval: true, isAvailable: false },
101
  { id: 'mitre', name: 'MITRE ATT&CK', description: 'Adversary tactics & techniques', category: 'threatIntel', requiresApproval: false, isAvailable: true },
102
  ],
 
 
 
 
103
  };
104
 
105
  export function useLiveData<T = any>(options: UseLiveDataOptions<T>): UseLiveDataResult<T> {
@@ -222,7 +256,7 @@ export function useLiveData<T = any>(options: UseLiveDataOptions<T>): UseLiveDat
222
  useEffect(() => {
223
  if (!enabled || wsEvents.length === 0) return;
224
 
225
- const wsUrl = 'ws://localhost:3001/mcp/ws';
226
 
227
  try {
228
  wsRef.current = new WebSocket(wsUrl);
@@ -332,3 +366,17 @@ export function useDecisionHistory(pollInterval = 30000) {
332
  transform: (data) => data.decisions || [],
333
  });
334
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  import { useState, useEffect, useCallback, useRef } from 'react';
13
 
14
+ // Dynamic API base - use environment variable or HuggingFace Spaces URL in production
15
+ const getApiBase = () => {
16
+ // Check for Vite environment variable first
17
+ if (typeof import.meta !== 'undefined' && import.meta.env?.VITE_API_URL) {
18
+ return `${import.meta.env.VITE_API_URL}/api`;
19
+ }
20
+ // Production default: HuggingFace Spaces backend
21
+ if (typeof window !== 'undefined' && window.location.hostname !== 'localhost') {
22
+ return 'https://kraft102-widgettdc-api.hf.space/api';
23
+ }
24
+ // Development default
25
+ return 'http://localhost:3001/api';
26
+ };
27
+
28
+ const getWsBase = () => {
29
+ // Check for Vite environment variable first
30
+ if (typeof import.meta !== 'undefined' && import.meta.env?.VITE_WS_URL) {
31
+ return import.meta.env.VITE_WS_URL;
32
+ }
33
+ // Production default: HuggingFace Spaces backend
34
+ if (typeof window !== 'undefined' && window.location.hostname !== 'localhost') {
35
+ return 'wss://kraft102-widgettdc-api.hf.space';
36
+ }
37
+ // Development default
38
+ return 'ws://localhost:3001';
39
+ };
40
+
41
+ const API_BASE = getApiBase();
42
+ const WS_BASE = getWsBase();
43
 
44
  // Source types that widgets can request
45
  export type DataSourceType =
 
52
  | 'systemHealth' // System services health
53
  | 'metrics' // Platform metrics
54
  | 'osint' // External OSINT feeds (future)
55
+ | 'threatIntel' // Threat intelligence (future)
56
+ | 'email'; // Email inbox data
57
 
58
  // Recommended external sources that can be fetched on demand
59
  export interface RecommendedSource {
 
97
  metrics: '/sys/metrics',
98
  osint: '/acquisition/osint',
99
  threatIntel: '/acquisition/threats',
100
+ email: '/email/inbox',
101
  };
102
 
103
  // Recommended sources per data type
 
130
  { id: 'otx', name: 'AlienVault OTX', description: 'Open Threat Exchange IOCs', category: 'threatIntel', requiresApproval: true, isAvailable: false },
131
  { id: 'mitre', name: 'MITRE ATT&CK', description: 'Adversary tactics & techniques', category: 'threatIntel', requiresApproval: false, isAvailable: true },
132
  ],
133
+ email: [
134
+ { id: 'outlook', name: 'Outlook/Office365', description: 'Microsoft email via IMAP', category: 'internal', requiresApproval: false, isAvailable: true },
135
+ { id: 'gmail', name: 'Gmail', description: 'Google email via IMAP', category: 'internal', requiresApproval: false, isAvailable: true },
136
+ ],
137
  };
138
 
139
  export function useLiveData<T = any>(options: UseLiveDataOptions<T>): UseLiveDataResult<T> {
 
256
  useEffect(() => {
257
  if (!enabled || wsEvents.length === 0) return;
258
 
259
+ const wsUrl = `${WS_BASE}/mcp/ws`;
260
 
261
  try {
262
  wsRef.current = new WebSocket(wsUrl);
 
366
  transform: (data) => data.decisions || [],
367
  });
368
  }
369
+
370
+ export function useEmailInbox(pollInterval = 60000) {
371
+ return useLiveData({
372
+ sourceType: 'email',
373
+ pollInterval,
374
+ wsEvents: ['email:refresh'],
375
+ transform: (data) => ({
376
+ emails: data.emails || [],
377
+ unreadCount: data.unreadCount || 0,
378
+ source: data.source || 'unknown',
379
+ lastFetch: data.lastFetch || null,
380
+ }),
381
+ });
382
+ }