Spaces:
Runtime error
Runtime error
| /** | |
| * Graph Viewer API for HuggingClaw | |
| * Simple D3-based graph visualization | |
| */ | |
| const express = require('express'); | |
| const neo4j = require('neo4j-driver'); | |
| const app = express(); | |
| const PORT = process.env.GRAPH_VIEWER_PORT || 7861; | |
| // Connect to Memgraph using Bolt protocol | |
| const driver = neo4j.driver( | |
| 'bolt://localhost:7687', | |
| neo4j.auth.basic('', '') | |
| ); | |
| // Security & Robustness Configuration (Bugs #19 & #20) | |
| const cors = require('cors'); | |
| const rateLimit = require('express-rate-limit'); | |
| // CORS Configuration for Obsidian integration | |
| app.use(cors({ | |
| origin: ['obsidian://*', 'https://*.hf.space'] | |
| })); | |
| // API Rate Limiting to prevent exhaustion | |
| app.use(rateLimit({ | |
| windowMs: 15 * 60 * 1000, // 15 minutes | |
| max: 100 // limit each IP to 100 requests per windowMs | |
| })); | |
| // Serve static files | |
| app.use(express.static('public')); | |
| // API: Get full graph | |
| app.get('/api/graph', async (req, res) => { | |
| const session = driver.session(); | |
| try { | |
| const result = await session.run('MATCH (n) RETURN n'); | |
| const edgesResult = await session.run('MATCH ()-[r]->() RETURN r'); | |
| res.json({ | |
| nodes: result.records.map(record => record.get('n').properties), | |
| edges: edgesResult.records.map(record => ({ | |
| ...record.get('r').properties, | |
| type: record.get('r').type | |
| })) | |
| }); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } finally { | |
| await session.close(); | |
| } | |
| }); | |
| // API: Get graph by project | |
| app.get('/api/graph/:project', async (req, res) => { | |
| const session = driver.session(); | |
| try { | |
| const { project } = req.params; | |
| const result = await session.run( | |
| 'MATCH (n {project: $project}) RETURN n', | |
| { project } | |
| ); | |
| const edgesResult = await session.run( | |
| 'MATCH (n {project: $project})-[r]->(m {project: $project}) RETURN r', | |
| { project } | |
| ); | |
| res.json({ | |
| nodes: result.records.map(record => record.get('n').properties), | |
| edges: edgesResult.records.map(record => ({ | |
| ...record.get('r').properties, | |
| type: record.get('r').type | |
| })) | |
| }); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } finally { | |
| await session.close(); | |
| } | |
| }); | |
| // API: Health check | |
| app.get('/health', async (req, res) => { | |
| let memgraphReady = false; | |
| const session = driver.session(); | |
| try { | |
| const result = await session.run('RETURN 1'); | |
| memgraphReady = result.records.length > 0; | |
| } catch (e) { | |
| memgraphReady = false; | |
| } finally { | |
| await session.close(); | |
| } | |
| res.json({ | |
| status: memgraphReady ? 'healthy' : 'degraded', | |
| version: '1.2.0', | |
| build_date: new Date().toISOString().split('T')[0], | |
| services: { | |
| memgraph: memgraphReady, | |
| graph_viewer: true, | |
| omniroute: 'check-port-20128' // Placeholder for port-based check | |
| }, | |
| timestamp: new Date().toISOString() | |
| }); | |
| }); | |
| // Start server | |
| app.listen(PORT, '0.0.0.0', () => { | |
| console.log(`π Graph Viewer running on port ${PORT}`); | |
| console.log(` http://localhost:${PORT}`); | |
| console.log(` API: http://localhost:${PORT}/api/graph`); | |
| }); | |
| // Graceful driver shutdown | |
| process.on('SIGTERM', () => { | |
| driver.close(); | |
| }); | |
| module.exports = app; | |