Spaces:
Runtime error
Runtime error
Download server.js from hello-yaaash25/DoclessAPIAgent: direct link, hf CLI and curl.
- Browser
- Download file 18 kB
-
https://huggingface.co/spaces/hello-yaaash25/DoclessAPIAgent/resolve/main/server.js
- Command line
-
hf download hf://spaces/hello-yaaash25/DoclessAPIAgent/server.js
-
curl -L -o server.js https://huggingface.co/spaces/hello-yaaash25/DoclessAPIAgent/resolve/main/server.js
18 kB
| const express = require('express'); | |
| const path = require('path'); | |
| const cors = require('cors'); | |
| const fs = require('fs').promises; | |
| const DemoStreamingService = require('./demo-streaming'); | |
| const SpacesStorageManager = require('./storage-manager'); | |
| const app = express(); | |
| const PORT = process.env.PORT || 3000; | |
| const IS_SPACES = process.env.IS_SPACES === 'true'; | |
| const WORKSPACE_DIR = process.env.WORKSPACE_DIR || './demo-sessions'; | |
| // Initialize services | |
| const streamingService = new DemoStreamingService(); | |
| const storageManager = new SpacesStorageManager(WORKSPACE_DIR); | |
| // Hugging Face Spaces optimized middleware | |
| if (IS_SPACES) { | |
| // Trust proxy for Spaces environment | |
| app.set('trust proxy', true); | |
| // Add Spaces-specific headers and force HTTPS | |
| app.use((req, res, next) => { | |
| // Force HTTPS redirect in Spaces | |
| if (req.header('x-forwarded-proto') !== 'https') { | |
| res.redirect(`https://${req.header('host')}${req.url}`); | |
| return; | |
| } | |
| res.setHeader('X-Frame-Options', 'SAMEORIGIN'); | |
| res.setHeader('X-Content-Type-Options', 'nosniff'); | |
| res.setHeader('X-Powered-By', 'Doc-less API Agent on Spaces'); | |
| res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); | |
| res.setHeader('X-XSS-Protection', '1; mode=block'); | |
| res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin'); | |
| next(); | |
| }); | |
| } | |
| // Enhanced CORS configuration for Spaces | |
| const corsOptions = { | |
| origin: IS_SPACES ? true : (process.env.CORS_ORIGIN || '*'), | |
| methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], | |
| allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], | |
| credentials: false | |
| }; | |
| app.use(cors(corsOptions)); | |
| // Body parsing middleware | |
| app.use(express.json({ limit: '10mb' })); | |
| app.use(express.urlencoded({ extended: true, limit: '10mb' })); | |
| // Serve static files with proper headers | |
| app.use(express.static('.', { | |
| setHeaders: (res, path) => { | |
| if (path.endsWith('.html')) { | |
| res.setHeader('Content-Type', 'text/html; charset=utf-8'); | |
| } else if (path.endsWith('.js')) { | |
| res.setHeader('Content-Type', 'application/javascript; charset=utf-8'); | |
| } else if (path.endsWith('.css')) { | |
| res.setHeader('Content-Type', 'text/css; charset=utf-8'); | |
| } else if (path.endsWith('.json')) { | |
| res.setHeader('Content-Type', 'application/json; charset=utf-8'); | |
| } else if (path.endsWith('.md')) { | |
| res.setHeader('Content-Type', 'text/markdown; charset=utf-8'); | |
| } else if (path.endsWith('.yaml') || path.endsWith('.yml')) { | |
| res.setHeader('Content-Type', 'text/yaml; charset=utf-8'); | |
| } | |
| // Cache static assets appropriately | |
| if (path.includes('demo-output') || path.includes('deliverables')) { | |
| res.setHeader('Cache-Control', 'public, max-age=300'); // 5 minutes | |
| } else { | |
| res.setHeader('Cache-Control', 'no-cache'); | |
| } | |
| } | |
| })); | |
| // Main route - serve the demo interface | |
| app.get('/', (req, res) => { | |
| try { | |
| console.log(`π± Serving demo interface to ${req.ip} - User Agent: ${req.get('User-Agent')?.substring(0, 50)}...`); | |
| // Add headers for better compatibility | |
| res.setHeader('Content-Type', 'text/html; charset=utf-8'); | |
| res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); | |
| res.setHeader('Pragma', 'no-cache'); | |
| res.setHeader('Expires', '0'); | |
| // Send the file with error handling | |
| res.sendFile(path.join(__dirname, 'demo-interface.html'), (err) => { | |
| if (err) { | |
| console.error('β Error serving demo interface:', err); | |
| res.status(500).send(` | |
| <!DOCTYPE html> | |
| <html> | |
| <head><title>Doc-less API Agent</title></head> | |
| <body style="font-family: Arial, sans-serif; padding: 20px;"> | |
| <h1>π€ Doc-less API Agent</h1> | |
| <p>Demo interface is loading...</p> | |
| <p>Server Status: β Running</p> | |
| <p>Environment: ${IS_SPACES ? 'Hugging Face Spaces' : 'Local'}</p> | |
| <p>Port: ${PORT}</p> | |
| <p>Time: ${new Date().toISOString()}</p> | |
| <a href="/health">Health Check</a> | | |
| <a href="/test">Test Endpoint</a> | | |
| <a href="/api/status">API Status</a> | |
| </body> | |
| </html> | |
| `); | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('β Fatal error in main route:', error); | |
| res.status(500).json({ | |
| error: 'Internal Server Error', | |
| message: 'Unable to serve demo interface', | |
| timestamp: new Date().toISOString() | |
| }); | |
| } | |
| }); | |
| app.get('/favicon.ico', (req, res) => { | |
| res.status(204).end(); | |
| }); | |
| // Simple test route for debugging | |
| app.get('/test', (req, res) => { | |
| console.log(`π§ͺ Test endpoint accessed from ${req.ip}`); | |
| res.json({ | |
| status: 'success', | |
| message: 'Doc-less API Agent is working!', | |
| timestamp: new Date().toISOString(), | |
| environment: IS_SPACES ? 'Hugging Face Spaces' : 'Local', | |
| port: PORT, | |
| uptime: process.uptime() | |
| }); | |
| }); | |
| // Debug page for troubleshooting | |
| app.get('/debug', (req, res) => { | |
| console.log(`π§ Debug page accessed from ${req.ip}`); | |
| res.sendFile(path.join(__dirname, 'debug.html')); | |
| }); | |
| // Health check endpoint with detailed information | |
| app.get('/health', async (req, res) => { | |
| const health = { | |
| status: 'healthy', | |
| timestamp: new Date().toISOString(), | |
| uptime: process.uptime(), | |
| environment: IS_SPACES ? 'spaces' : 'local', | |
| port: PORT, | |
| version: process.env.npm_package_version || '1.0.0', | |
| memory: process.memoryUsage(), | |
| workspace: WORKSPACE_DIR, | |
| request_info: { | |
| ip: req.ip, | |
| user_agent: req.get('User-Agent'), | |
| headers: req.headers | |
| }, | |
| services: { | |
| streaming: { | |
| active_sessions: streamingService.getActiveSessionsCount(), | |
| active_streams: streamingService.getActiveStreamsCount() | |
| }, | |
| storage: await storageManager.getStorageStats() | |
| } | |
| }; | |
| // Check workspace directory | |
| try { | |
| await fs.access(WORKSPACE_DIR); | |
| health.workspace_accessible = true; | |
| } catch (error) { | |
| health.workspace_accessible = false; | |
| health.workspace_error = error.message; | |
| } | |
| // Add CORS headers for health check | |
| res.setHeader('Access-Control-Allow-Origin', '*'); | |
| res.setHeader('Access-Control-Allow-Methods', 'GET'); | |
| res.setHeader('Cache-Control', 'no-cache'); | |
| console.log(`π₯ Health check from ${req.ip} - Status: ${health.status}`); | |
| res.json(health); | |
| }); | |
| // Spaces-specific metadata endpoint | |
| app.get('/spaces/info', (req, res) => { | |
| res.json({ | |
| title: 'π€ Doc-less API Agent', | |
| description: 'Interactive API documentation and SDK generator demo', | |
| version: '1.0.0', | |
| features: [ | |
| 'Live API Discovery', | |
| 'OpenAPI Generation', | |
| 'TypeScript SDK Creation', | |
| 'Postman Collections', | |
| 'Mock Lab Environment', | |
| 'Evidence Reports', | |
| 'Real-time Demo Streaming' | |
| ], | |
| demo_paths: [ | |
| '/ - Interactive Demo Interface', | |
| '/api/status - Service Status', | |
| '/api/demo/stream - Live Demo Stream', | |
| '/deliverables - Generated Files', | |
| '/spaces/info - This Information' | |
| ], | |
| github: 'https://github.com/ysrastogi/apiWitness', | |
| is_spaces: IS_SPACES, | |
| uptime: process.uptime() | |
| }); | |
| }); | |
| // Server-Sent Events endpoint for real-time demo streaming | |
| app.get('/api/demo/stream', streamingService.createSSEEndpoint()); | |
| // API endpoints for demo integration | |
| app.get('/api/status', async (req, res) => { | |
| const status = { | |
| services: { | |
| main_server: `http://localhost:${PORT}`, | |
| demo_interface: `http://localhost:${PORT}/`, | |
| deliverables: `http://localhost:${PORT}/deliverables`, | |
| demo_stream: `http://localhost:${PORT}/api/demo/stream` | |
| }, | |
| environment: { | |
| is_spaces: IS_SPACES, | |
| workspace: WORKSPACE_DIR, | |
| node_env: process.env.NODE_ENV || 'development' | |
| }, | |
| demo: { | |
| interface_url: `http://localhost:${PORT}`, | |
| deliverables_url: `http://localhost:${PORT}/deliverables`, | |
| health_check: `http://localhost:${PORT}/health`, | |
| streaming_url: `http://localhost:${PORT}/api/demo/stream` | |
| }, | |
| streaming: { | |
| active_sessions: streamingService.getActiveSessionsCount(), | |
| active_streams: streamingService.getActiveStreamsCount() | |
| }, | |
| timestamp: new Date().toISOString() | |
| }; | |
| // Check if demo assets exist | |
| try { | |
| const demoOutputs = await fs.readdir('.', { withFileTypes: true }); | |
| const demoFolders = demoOutputs | |
| .filter(dirent => dirent.isDirectory() && dirent.name.startsWith('demo-output-')) | |
| .map(dirent => dirent.name); | |
| status.available_demos = demoFolders; | |
| } catch (error) { | |
| status.demo_scan_error = error.message; | |
| } | |
| res.json(status); | |
| }); | |
| // Demo session management for Spaces | |
| app.post('/api/demo/start', async (req, res) => { | |
| try { | |
| const { sessionId, config = {} } = req.body; | |
| const finalSessionId = sessionId || `demo-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; | |
| // Create storage session | |
| const session = await storageManager.createSession(finalSessionId); | |
| // Start streaming demo | |
| const demoConfig = streamingService.startDemoSession(finalSessionId, { | |
| delays: config.delays !== false, // Default to true | |
| realExecution: config.realExecution || false, | |
| ...config | |
| }); | |
| console.log(`π¬ Demo started: ${finalSessionId}`); | |
| res.json({ | |
| sessionId: finalSessionId, | |
| session, | |
| demoConfig, | |
| streamUrl: `/api/demo/stream?sessionId=${finalSessionId}` | |
| }); | |
| } catch (error) { | |
| console.error('β Error starting demo:', error); | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // Get demo session status | |
| app.get('/api/demo/:sessionId/status', (req, res) => { | |
| const { sessionId } = req.params; | |
| const status = streamingService.getSessionStatus(sessionId); | |
| if (status) { | |
| res.json(status); | |
| } else { | |
| res.status(404).json({ error: 'Session not found' }); | |
| } | |
| }); | |
| // Save demo result file | |
| app.post('/api/demo/:sessionId/save', async (req, res) => { | |
| try { | |
| const { sessionId } = req.params; | |
| const { fileName, content, metadata } = req.body; | |
| if (!fileName || content === undefined) { | |
| return res.status(400).json({ error: 'fileName and content are required' }); | |
| } | |
| const filePath = await storageManager.saveFile(sessionId, fileName, content, metadata); | |
| res.json({ | |
| success: true, | |
| fileName, | |
| path: filePath, | |
| sessionId | |
| }); | |
| } catch (error) { | |
| console.error('β Error saving file:', error); | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // Get demo session files | |
| app.get('/api/demo/:sessionId/files', async (req, res) => { | |
| try { | |
| const { sessionId } = req.params; | |
| const session = await storageManager.getSession(sessionId); | |
| if (!session) { | |
| return res.status(404).json({ error: 'Session not found' }); | |
| } | |
| res.json({ | |
| sessionId, | |
| files: session.files || [] | |
| }); | |
| } catch (error) { | |
| console.error('β Error getting session files:', error); | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // Get specific file from session | |
| app.get('/api/demo/:sessionId/files/:fileName', async (req, res) => { | |
| try { | |
| const { sessionId, fileName } = req.params; | |
| const content = await storageManager.getFile(sessionId, fileName); | |
| if (content === null) { | |
| return res.status(404).json({ error: 'File not found' }); | |
| } | |
| // Set appropriate content type | |
| if (fileName.endsWith('.json')) { | |
| res.setHeader('Content-Type', 'application/json'); | |
| } else if (fileName.endsWith('.md')) { | |
| res.setHeader('Content-Type', 'text/markdown'); | |
| } else if (fileName.endsWith('.yaml') || fileName.endsWith('.yml')) { | |
| res.setHeader('Content-Type', 'text/yaml'); | |
| } else if (fileName.endsWith('.ts')) { | |
| res.setHeader('Content-Type', 'text/typescript'); | |
| } | |
| res.send(content); | |
| } catch (error) { | |
| console.error('β Error getting file:', error); | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // Serve deliverables directory with better error handling | |
| app.use('/deliverables', express.static('demo-output-*', { | |
| setHeaders: (res, path) => { | |
| res.setHeader('Access-Control-Allow-Origin', '*'); | |
| if (path.endsWith('.json')) { | |
| res.setHeader('Content-Type', 'application/json'); | |
| } else if (path.endsWith('.md')) { | |
| res.setHeader('Content-Type', 'text/markdown'); | |
| } else if (path.endsWith('.yaml') || path.endsWith('.yml')) { | |
| res.setHeader('Content-Type', 'text/yaml'); | |
| } | |
| } | |
| })); | |
| // Serve static demo outputs if available | |
| app.get('/demo-assets/:asset', async (req, res) => { | |
| const asset = req.params.asset; | |
| try { | |
| // Look for the asset in any demo-output directory | |
| const dirs = await fs.readdir('.'); | |
| const demoDir = dirs.find(dir => dir.startsWith('demo-output-')); | |
| if (demoDir) { | |
| const assetPath = path.join(demoDir, asset); | |
| await fs.access(assetPath); | |
| res.sendFile(path.resolve(assetPath)); | |
| } else { | |
| res.status(404).json({ error: 'Demo assets not found' }); | |
| } | |
| } catch (error) { | |
| console.error(`β Error serving asset ${asset}:`, error); | |
| res.status(404).json({ error: 'Asset not found' }); | |
| } | |
| }); | |
| // Analytics endpoint (privacy-compliant) | |
| app.post('/api/analytics', (req, res) => { | |
| const { event, category, label, value } = req.body; | |
| // Log analytics data (can be enhanced to send to analytics service) | |
| console.log(`π Analytics: ${category}/${event}`, { | |
| label, | |
| value, | |
| timestamp: new Date().toISOString(), | |
| ip: req.ip, | |
| userAgent: req.get('User-Agent') | |
| }); | |
| res.json({ status: 'logged' }); | |
| }); | |
| // Storage management endpoints | |
| app.get('/api/storage/stats', async (req, res) => { | |
| try { | |
| const stats = await storageManager.getStorageStats(); | |
| res.json(stats); | |
| } catch (error) { | |
| console.error('β Error getting storage stats:', error); | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.post('/api/storage/cleanup', async (req, res) => { | |
| try { | |
| const deletedCount = await storageManager.cleanup(); | |
| res.json({ success: true, deleted: deletedCount }); | |
| } catch (error) { | |
| console.error('β Error during cleanup:', error); | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // 404 handler - redirect to demo interface for SPA behavior | |
| app.use((req, res) => { | |
| console.log(`π 404 for ${req.path}, redirecting to demo interface`); | |
| res.status(404).sendFile(path.join(__dirname, 'demo-interface.html')); | |
| }); | |
| // Enhanced error handler | |
| app.use((error, req, res, next) => { | |
| console.error('β Server Error:', { | |
| message: error.message, | |
| stack: error.stack, | |
| url: req.url, | |
| method: req.method, | |
| timestamp: new Date().toISOString() | |
| }); | |
| res.status(error.status || 500).json({ | |
| error: IS_SPACES ? 'Internal Server Error' : error.message, | |
| timestamp: new Date().toISOString(), | |
| path: req.path | |
| }); | |
| }); | |
| // Graceful shutdown handling | |
| process.on('SIGTERM', () => { | |
| console.log('π SIGTERM received, shutting down gracefully'); | |
| process.exit(0); | |
| }); | |
| process.on('SIGINT', () => { | |
| console.log('π SIGINT received, shutting down gracefully'); | |
| process.exit(0); | |
| }); | |
| // Start server | |
| app.listen(PORT, '0.0.0.0', () => { | |
| console.log(`π Doc-less API Agent ${IS_SPACES ? '(Spaces)' : '(Local)'} running on:`); | |
| console.log(` Local: http://localhost:${PORT}`); | |
| console.log(` Network: http://0.0.0.0:${PORT}`); | |
| console.log(` Health: http://localhost:${PORT}/health`); | |
| console.log(` API: http://localhost:${PORT}/api/status`); | |
| console.log(` Stream: http://localhost:${PORT}/api/demo/stream`); | |
| console.log(` Info: http://localhost:${PORT}/spaces/info`); | |
| console.log(''); | |
| console.log('π Serving files from:', __dirname); | |
| console.log('πΎ Workspace directory:', WORKSPACE_DIR); | |
| console.log('π― Environment:', IS_SPACES ? 'Hugging Face Spaces' : 'Local Development'); | |
| console.log(`π₯ Streaming service: ${streamingService.getActiveSessionsCount()} active sessions`); | |
| // Initialize storage manager after server starts | |
| console.log('πΎ Storage manager initialized:', WORKSPACE_DIR); | |
| console.log('β° Cleanup timer started (every 60 minutes)'); | |
| }); | |
| module.exports = app; | |