2024-12-10 16:32:44 -07:00
|
|
|
// Required modules
|
|
|
|
const express = require('express');
|
|
|
|
const { MongoClient, ObjectId } = require('mongodb');
|
|
|
|
const cors = require('cors');
|
|
|
|
const app = express();
|
2024-12-10 20:24:37 -07:00
|
|
|
|
|
|
|
|
2024-12-10 16:32:44 -07:00
|
|
|
const port = process.env.PORT || 3000;
|
2024-12-10 20:24:37 -07:00
|
|
|
const mongodb_uri = process.env.MONGODB_URI || 'mongodb://localhost:27017';
|
|
|
|
const mongodb_name = process.env.MONGODB_NAME || 'docucenter';
|
2024-12-10 16:32:44 -07:00
|
|
|
|
|
|
|
// Parse JSON bodies
|
|
|
|
app.use(express.json({ limit: '50mb' })); // For JSON data
|
|
|
|
app.use(express.urlencoded({ limit: '50mb', extended: true })); // For URL-encoded data
|
|
|
|
|
|
|
|
// Enabling CORS globally
|
|
|
|
app.use(cors());
|
|
|
|
|
|
|
|
// MongoDB configuration
|
2024-12-10 20:24:37 -07:00
|
|
|
const mongoUri = mongodb_uri;
|
|
|
|
const dbName = mongodb_name;
|
2024-12-10 16:32:44 -07:00
|
|
|
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 } = 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' });
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Insert the document into the database
|
|
|
|
const result = await db.collection('documents').insertOne({
|
|
|
|
title,
|
|
|
|
body,
|
|
|
|
tags,
|
|
|
|
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('/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('/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('/documents/:id', async (req, res) => {
|
|
|
|
const { id } = req.params;
|
|
|
|
const { title, body } = 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, 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('/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('/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}`);
|
|
|
|
});
|