DocuCenter/backend/app.js
2024-12-12 10:37:37 -07:00

240 lines
7.2 KiB
JavaScript

// 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 <img> tags from the body field
let cleanedBody = result.body.replace(/<img[^>]*>/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 <img> tags from the body field
let cleanedBody = result.body.replace(/<img[^>]*>/g, '');
// Surround query matches in the body with <span style="background: yellow;">
const bodyRegex = new RegExp(query, 'gi'); // 'gi' for case-insensitive matching globally
cleanedBody = cleanedBody.replace(bodyRegex, match => {
return `<span style="background: yellow;">${match}</span>`;
});
// 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 `<span style="background: yellow;">${match}</span>`;
});
// 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 `<span style="background: yellow; color: black;">${match}</span>`;
});
});
// 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);
});