// Required modules const express = require('express'); const { MongoClient, ObjectId } = require('mongodb'); const cors = require('cors'); const app = express(); require('dotenv').config(); const port = process.env.PORT || 3000; const mongodb_uri = process.env.MONGODB_URI || 'mongodb://localhost:27017'; const mongodb_name = process.env.MONGODB_NAME || 'docucenter'; if(process.env.MONGODB_URI){ console.log("Using ENV: " + process.env.MONGODB_URI); } // Parse JSON bodies app.use(express.json({ limit: '100mb' })); // For JSON data app.use(express.urlencoded({ limit: '100mb', extended: true })); // For URL-encoded data // Configure CORS to allow specific origins const allowedOrigins = [ 'https://docucenter.mpe.ca', // Production frontend 'http://localhost:5173', // Development frontend ]; const corsOptions = { origin: (origin, callback) => { // Allow requests with no origin (e.g., mobile apps or curl) if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } }, methods: ['GET', 'POST', 'PUT', 'DELETE'], // Specify allowed HTTP methods allowedHeaders: ['Content-Type', 'Authorization'], // Specify allowed headers credentials: true, // Enable cookies and credentials sharing }; app.use(cors(corsOptions)); // MongoDB configuration const mongoUri = mongodb_uri; const dbName = mongodb_name; let db; // Connect to MongoDB MongoClient.connect(mongoUri, { useNewUrlParser: true, useUnifiedTopology: true }) .then(client => { db = client.db(dbName); console.log(`Connected to database: ${dbName}`); }) .catch(err => { console.error('Failed to connect to MongoDB:', err); process.exit(1); }); // Middleware to handle errors app.use((err, req, res, next) => { console.error(err); res.status(500).json({ error: 'Something went wrong!' }); }); // CRUD Routes // Create a new document app.post('/documents', async (req, res) => { const { title, body, tags, created_by } = req.body; // Validate input if (!title || !body) { return res.status(400).json({ error: 'Title and body are required' }); } if (!Array.isArray(tags)) { return res.status(400).json({ error: 'Tags must be an array' }); } console.log(created_by); try { // Insert the document into the database const result = await db.collection('documents').insertOne({ title, body, tags, created_by, created_at: new Date() }); // Respond with success res.status(201).json({ message: 'Document created', id: result.insertedId }); } catch (err) { console.error('Error saving document:', err); res.status(500).json({ error: 'Failed to save document' }); } }); // Get all documents app.get('/api/documents', async (req, res) => { try { const documents = await db.collection('documents').find().toArray(); // Process each result const cleanedResults = documents.map(result => { // Remove tags from the body field let cleanedBody = result.body.replace(/]*>/g, ''); return { ...result, body: cleanedBody }; }); res.json(cleanedResults); } catch (err) { console.error(err); res.status(500).json({ error: 'Failed to retrieve documents' }); } }); // Get a document by ID app.get('/api/documents/:id', async (req, res) => { const { id } = req.params; try { const document = await db.collection('documents').findOne({ _id: new ObjectId(id) }); if (!document) { return res.status(404).json({ error: 'Document not found' }); } res.json(document); } catch (err) { console.error(err); res.status(500).json({ error: 'Failed to retrieve document' }); } }); // Update a document app.put('/api/documents/:id', async (req, res) => { const { id } = req.params; const { title, body, tags, edited_by } = req.body; if (!title || !body) { return res.status(400).json({ error: 'Title and body are required' }); } try { const result = await db.collection('documents').updateOne( { _id: new ObjectId(id) }, { $set: { title, body, tags, edited_by, updated_at: new Date() } } ); if (result.matchedCount === 0) { return res.status(404).json({ error: 'Document not found' }); } res.json({ message: 'Document updated' }); } catch (err) { console.error(err); res.status(500).json({ error: 'Failed to update document' }); } }); // Delete a document app.delete('/api/documents/:id', async (req, res) => { const { id } = req.params; try { const result = await db.collection('documents').deleteOne({ _id: new ObjectId(id) }); if (result.deletedCount === 0) { return res.status(404).json({ error: 'Document not found' }); } res.json({ message: 'Document deleted' }); } catch (err) { console.error(err); res.status(500).json({ error: 'Failed to delete document' }); } }); app.get('/api/search', async (req, res) => { const { query } = req.query; if (!query) { return res.status(400).send('Query parameter is required'); } try { // Case-insensitive search using regular expressions const results = await db.collection('documents').find({ $or: [ { title: { $regex: query, $options: 'i' } }, // Search in title { body: { $regex: query, $options: 'i' } }, // Search in body { tags: { $regex: query, $options: 'i' } } // Search in tags (exact match) ] }).toArray(); // Convert cursor to array // Process each result const cleanedResults = results.map(result => { // Remove tags from the body field let cleanedBody = result.body.replace(/]*>/g, ''); // Surround query matches in the body with const bodyRegex = new RegExp(query, 'gi'); // 'gi' for case-insensitive matching globally cleanedBody = cleanedBody.replace(bodyRegex, match => { return `${match}`; }); // Highlight the title if it matches the query let cleanedTitle = result.title; const titleRegex = new RegExp(query, 'gi'); // 'gi' for case-insensitive matching globally cleanedTitle = cleanedTitle.replace(titleRegex, match => { return `${match}`; }); // Highlight query matches in tags (tags is an array) const cleanedTags = result.tags.map(tag => { const tagRegex = new RegExp(query, 'gi'); return tag.replace(tagRegex, match => { return `${match}`; }); }); // Return the updated result with highlighted title, body, and tags return { ...result, title: cleanedTitle, body: cleanedBody, tags: cleanedTags }; }); res.json(cleanedResults); } catch (error) { console.error('Error performing search:', error); res.status(500).send('Server error'); } }); // Handle invalid routes (404) app.use((req, res) => { res.status(404).json({ error: 'Route not found' }); }); // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); console.log("Searching for database: URI: "+mongodb_uri); });