const express = require('express'); const cors = require('cors'); const dotenv = require('dotenv'); const path = require('path'); // Import models const News = require('./models/News'); const Dataset = require('./models/Dataset'); dotenv.config(); const app = express(); // Use port 8501 for Hugging Face Spaces, or 3001 for local development const isHuggingFaceSpaces = process.env.HF_SPACES === 'true' || process.env.HF_SPACES === true || !!process.env.HF_SPACES; const PORT = isHuggingFaceSpaces ? 8501 : (process.env.PORT || 3001); // Enable CORS for all origins in development, specific origins in production if (process.env.NODE_ENV === 'development') { app.use(cors()); } else { // In production (like Hugging Face Spaces), be more specific about CORS app.use(cors({ origin: function (origin, callback) { // Allow requests with no origin (like mobile apps or curl requests) if (!origin) return callback(null, true); // Allow localhost in development if (origin.startsWith('http://localhost') || origin.startsWith('https://localhost') || origin.includes('huggingface.co') || origin.includes('hf.space') || origin === 'http://localhost:80' || origin === 'http://localhost:5173') { return callback(null, true); } // Allow same origin requests if (origin === `http://localhost:${PORT}`) { return callback(null, true); } callback(null, true); // Allow all in production for now } })); } app.use(express.json()); // Serve static files from the public directory in production if (process.env.NODE_ENV === 'production') { app.use(express.static(path.join(__dirname, 'public'))); } // Health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString(), uptime: process.uptime() }); }); // API routes app.get('/api/news', async (req, res) => { try { const news = await News.getAll(); res.json(news); } catch (error) { console.error('Error fetching news:', error); res.status(500).json({ error: 'Failed to fetch news' }); } }); app.get('/api/news/:id', async (req, res) => { try { const { id } = req.params; const newsItem = await News.getById(id); if (!newsItem) { return res.status(404).json({ error: 'News item not found' }); } res.json(newsItem); } catch (error) { console.error('Error fetching news item:', error); res.status(500).json({ error: 'Failed to fetch news item' }); } }); app.get('/api/datasets', async (req, res) => { try { const datasets = await Dataset.getAll(); res.json(datasets); } catch (error) { console.error('Error fetching datasets:', error); res.status(500).json({ error: 'Failed to fetch datasets' }); } }); app.get('/api/datasets/:id', async (req, res) => { try { const { id } = req.params; const dataset = await Dataset.getById(id); if (!dataset) { return res.status(404).json({ error: 'Dataset not found' }); } res.json(dataset); } catch (error) { console.error('Error fetching dataset:', error); res.status(500).json({ error: 'Failed to fetch dataset' }); } }); // Serve the React app for all non-API routes in production if (process.env.NODE_ENV === 'production') { // For any non-API routes, serve the React app app.get('*', (req, res) => { // Don't serve the React app for API routes or health check if (req.path.startsWith('/api/') || req.path === '/health') { // For API routes that don't exist, return 404 if (req.path.startsWith('/api/')) { return res.status(404).json({ error: 'API endpoint not found' }); } // For health check, it's already handled above return res.status(404).json({ error: 'Not found' }); } // Serve the React app for all other routes res.sendFile(path.resolve(__dirname, 'public', 'index.html')); }); } else { // In development, serve API info at root app.get('/', (req, res) => { res.json({ message: 'BioNexus Hub API', version: '1.0.0', timestamp: new Date().toISOString() }); }); } app.listen(PORT, '0.0.0.0', () => { console.log(`BioNexus Hub server running on port ${PORT}`); });