712 lines
22 KiB
JavaScript
712 lines
22 KiB
JavaScript
const express = require('express');
|
|
const mongoose = require('mongoose');
|
|
const cors = require('cors');
|
|
const dotenv = require('dotenv');
|
|
const autocadRoutes = require('./routes/autocadRoutes');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Load environment variables
|
|
dotenv.config();
|
|
|
|
// Initialize express app
|
|
const app = express();
|
|
|
|
// Middleware
|
|
app.use(cors());
|
|
app.use(express.json({ limit: '10mb' })); // Increase payload limit
|
|
|
|
// Set timeout for requests
|
|
app.use((req, res, next) => {
|
|
res.setTimeout(30000, () => {
|
|
console.error('Request timeout');
|
|
res.status(408).send('Request Timeout');
|
|
});
|
|
next();
|
|
});
|
|
|
|
// Error handling middleware
|
|
app.use((err, req, res, next) => {
|
|
console.error('Server error:', err);
|
|
res.status(500).send('Internal Server Error');
|
|
});
|
|
|
|
// Serve static files from the public directory
|
|
app.use(express.static('public'));
|
|
|
|
// Create storage directory if it doesn't exist
|
|
const dataDir = path.join(__dirname, 'data');
|
|
if (!fs.existsSync(dataDir)) {
|
|
fs.mkdirSync(dataDir);
|
|
}
|
|
|
|
// Initialize event storage by categories
|
|
global.autocadEvents = {
|
|
all: [],
|
|
blockEvents: {
|
|
added: [],
|
|
erased: [],
|
|
stretched: [],
|
|
rotated: [],
|
|
scaled: [],
|
|
moved: [],
|
|
attributesChanged: [],
|
|
modified: []
|
|
},
|
|
entityEvents: {
|
|
added: [],
|
|
erased: [],
|
|
stretched: [],
|
|
modified: []
|
|
}
|
|
};
|
|
|
|
// Add a store of SSE clients for real-time updates
|
|
const sseClients = [];
|
|
|
|
// Performance monitoring
|
|
let eventCount = 0;
|
|
let lastSaveTime = Date.now();
|
|
const MAX_EVENTS_IN_MEMORY = 10000; // Limit total events in memory
|
|
|
|
// Middleware to handle SSE connections
|
|
app.get('/api/sse', (req, res) => {
|
|
// Set headers for SSE
|
|
res.writeHead(200, {
|
|
'Content-Type': 'text/event-stream',
|
|
'Cache-Control': 'no-cache',
|
|
'Connection': 'keep-alive'
|
|
});
|
|
|
|
// Send initial connection established message
|
|
res.write('event: connected\n');
|
|
res.write('data: Connection established\n\n');
|
|
|
|
// Add this client to the list
|
|
const clientId = Date.now();
|
|
const newClient = {
|
|
id: clientId,
|
|
res
|
|
};
|
|
sseClients.push(newClient);
|
|
|
|
// Handle client disconnect
|
|
req.on('close', () => {
|
|
console.log(`SSE Client ${clientId} disconnected`);
|
|
const index = sseClients.findIndex(client => client.id === clientId);
|
|
if (index !== -1) {
|
|
sseClients.splice(index, 1);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Function to notify all SSE clients of a new event
|
|
function notifyClientsOfNewEvent(event) {
|
|
const failedClients = [];
|
|
|
|
sseClients.forEach((client, index) => {
|
|
try {
|
|
client.res.write(`event: new-event\n`);
|
|
client.res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
} catch (error) {
|
|
console.error(`Error sending to client ${client.id}:`, error);
|
|
failedClients.push(index);
|
|
}
|
|
});
|
|
|
|
// Remove failed clients (in reverse order to avoid index issues)
|
|
failedClients.reverse().forEach(index => {
|
|
console.log(`Removing failed SSE client ${sseClients[index].id}`);
|
|
sseClients.splice(index, 1);
|
|
});
|
|
}
|
|
|
|
// Routes
|
|
app.use('/api', autocadRoutes);
|
|
|
|
// Add connection test endpoint
|
|
app.post('/api/connection-test', (req, res) => {
|
|
console.log('Connection test received');
|
|
res.status(200).json({ success: true, message: 'Connection successful', serverTime: new Date().toISOString() });
|
|
});
|
|
|
|
// Add custom event handling middleware
|
|
app.post('/api/events', (req, res) => {
|
|
try {
|
|
const eventData = req.body;
|
|
|
|
// Handle connection test events specially
|
|
if (eventData.EventType === 'ConnectionTest') {
|
|
console.log('AutoCAD connection test received');
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: 'Connection test successful',
|
|
serverTime: new Date().toISOString(),
|
|
eventsCount: global.autocadEvents.all.length
|
|
});
|
|
}
|
|
|
|
// Add timestamp if not provided
|
|
if (!eventData.timestamp) {
|
|
eventData.timestamp = new Date().toISOString();
|
|
}
|
|
|
|
// Store in global all events array
|
|
global.autocadEvents.all.push(eventData);
|
|
eventCount++;
|
|
|
|
// Limit memory usage by removing old events if we exceed MAX_EVENTS_IN_MEMORY
|
|
if (global.autocadEvents.all.length > MAX_EVENTS_IN_MEMORY) {
|
|
const excess = global.autocadEvents.all.length - MAX_EVENTS_IN_MEMORY;
|
|
|
|
// Remove oldest events
|
|
const removed = global.autocadEvents.all.splice(0, excess);
|
|
console.log(`Memory limit reached: Removed ${removed.length} old events`);
|
|
|
|
// Also remove from specific event arrays
|
|
for (const category of Object.keys(global.autocadEvents.blockEvents)) {
|
|
if (global.autocadEvents.blockEvents[category].length > MAX_EVENTS_IN_MEMORY / 10) {
|
|
const categoryExcess = global.autocadEvents.blockEvents[category].length - (MAX_EVENTS_IN_MEMORY / 10);
|
|
global.autocadEvents.blockEvents[category].splice(0, categoryExcess);
|
|
}
|
|
}
|
|
|
|
for (const category of Object.keys(global.autocadEvents.entityEvents)) {
|
|
if (global.autocadEvents.entityEvents[category].length > MAX_EVENTS_IN_MEMORY / 10) {
|
|
const categoryExcess = global.autocadEvents.entityEvents[category].length - (MAX_EVENTS_IN_MEMORY / 10);
|
|
global.autocadEvents.entityEvents[category].splice(0, categoryExcess);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Categorize by event type
|
|
const eventType = eventData.EventType;
|
|
|
|
// Handle block-specific events
|
|
if (eventType.startsWith('Block')) {
|
|
switch (eventType) {
|
|
case 'BlockAdded':
|
|
global.autocadEvents.blockEvents.added.push(eventData);
|
|
break;
|
|
case 'BlockErased':
|
|
global.autocadEvents.blockEvents.erased.push(eventData);
|
|
break;
|
|
case 'BlockStretched':
|
|
global.autocadEvents.blockEvents.stretched.push(eventData);
|
|
break;
|
|
case 'BlockRotated':
|
|
global.autocadEvents.blockEvents.rotated.push(eventData);
|
|
break;
|
|
case 'BlockScaled':
|
|
global.autocadEvents.blockEvents.scaled.push(eventData);
|
|
break;
|
|
case 'BlockMoved':
|
|
global.autocadEvents.blockEvents.moved.push(eventData);
|
|
break;
|
|
case 'BlockAttributesChanged':
|
|
global.autocadEvents.blockEvents.attributesChanged.push(eventData);
|
|
break;
|
|
case 'BlockModified':
|
|
global.autocadEvents.blockEvents.modified.push(eventData);
|
|
break;
|
|
}
|
|
} else {
|
|
// Handle non-block entity events
|
|
switch (eventType) {
|
|
case 'Added':
|
|
global.autocadEvents.entityEvents.added.push(eventData);
|
|
break;
|
|
case 'Erased':
|
|
global.autocadEvents.entityEvents.erased.push(eventData);
|
|
break;
|
|
case 'Stretched':
|
|
global.autocadEvents.entityEvents.stretched.push(eventData);
|
|
break;
|
|
case 'Modified':
|
|
global.autocadEvents.entityEvents.modified.push(eventData);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Notify all connected clients of the new event
|
|
notifyClientsOfNewEvent(eventData);
|
|
|
|
// Periodically save events to disk (every 20 events or 5 minutes)
|
|
const currentTime = Date.now();
|
|
if (eventCount % 20 === 0 || (currentTime - lastSaveTime) > 300000) {
|
|
saveEventsToDisk();
|
|
lastSaveTime = currentTime;
|
|
}
|
|
|
|
// Enhanced logging based on event type
|
|
if (eventType.startsWith('Block')) {
|
|
// Basic log message with event type and block name
|
|
console.log(`Received ${eventType} event for ${eventData.BlockName}`);
|
|
|
|
// Use the formatting function for detailed output
|
|
if (process.env.VERBOSE_LOGGING === 'true') {
|
|
console.log(formatBlockData(eventData));
|
|
}
|
|
|
|
// Add specific position change logging
|
|
if (eventType === 'BlockMoved' && eventData.OldPosition && eventData.NewPosition) {
|
|
console.log(`POSITION CHANGE: From (${eventData.OldPosition.X.toFixed(2)}, ${eventData.OldPosition.Y.toFixed(2)}, ${eventData.OldPosition.Z.toFixed(2)}) to (${eventData.NewPosition.X.toFixed(2)}, ${eventData.NewPosition.Y.toFixed(2)}, ${eventData.NewPosition.Z.toFixed(2)})`);
|
|
}
|
|
} else {
|
|
// Standard logging for non-block events
|
|
console.log(`Received ${eventType} event for ${eventData.ObjectType}`);
|
|
}
|
|
|
|
res.status(200).json({ success: true, message: 'Event received' });
|
|
} catch (error) {
|
|
console.error('Error processing event:', error);
|
|
res.status(500).json({ success: false, message: 'Error processing event' });
|
|
}
|
|
});
|
|
|
|
// Add endpoints to get events by type
|
|
app.get('/api/events', (req, res) => {
|
|
res.json(global.autocadEvents.all);
|
|
});
|
|
|
|
app.get('/api/events/blocks', (req, res) => {
|
|
res.json(global.autocadEvents.blockEvents);
|
|
});
|
|
|
|
app.get('/api/events/blocks/:type', (req, res) => {
|
|
const eventType = req.params.type;
|
|
if (global.autocadEvents.blockEvents[eventType]) {
|
|
res.json(global.autocadEvents.blockEvents[eventType]);
|
|
} else {
|
|
res.status(404).json({ message: 'Event type not found' });
|
|
}
|
|
});
|
|
|
|
app.get('/api/events/entities', (req, res) => {
|
|
res.json(global.autocadEvents.entityEvents);
|
|
});
|
|
|
|
app.get('/api/events/entities/:type', (req, res) => {
|
|
const eventType = req.params.type;
|
|
if (global.autocadEvents.entityEvents[eventType]) {
|
|
res.json(global.autocadEvents.entityEvents[eventType]);
|
|
} else {
|
|
res.status(404).json({ message: 'Event type not found' });
|
|
}
|
|
});
|
|
|
|
// Add an endpoint to clear events
|
|
app.delete('/api/events', (req, res) => {
|
|
global.autocadEvents = {
|
|
all: [],
|
|
blockEvents: {
|
|
added: [],
|
|
erased: [],
|
|
stretched: [],
|
|
rotated: [],
|
|
scaled: [],
|
|
moved: [],
|
|
attributesChanged: [],
|
|
modified: []
|
|
},
|
|
entityEvents: {
|
|
added: [],
|
|
erased: [],
|
|
stretched: [],
|
|
modified: []
|
|
}
|
|
};
|
|
res.json({ success: true, message: 'All events cleared' });
|
|
});
|
|
|
|
// Function to save events to disk
|
|
function saveEventsToDisk() {
|
|
try {
|
|
const timestamp = new Date().toISOString().replace(/:/g, '-');
|
|
const filePath = path.join(dataDir, `events_${timestamp}.json`);
|
|
|
|
// Use a more memory-efficient approach for large event arrays
|
|
if (global.autocadEvents.all.length > 1000) {
|
|
// Write to file stream instead of creating a large string in memory
|
|
const fileStream = fs.createWriteStream(filePath);
|
|
fileStream.write('{\n');
|
|
fileStream.write('"all": [\n');
|
|
|
|
global.autocadEvents.all.forEach((event, index) => {
|
|
const eventJson = JSON.stringify(event);
|
|
fileStream.write(eventJson);
|
|
if (index < global.autocadEvents.all.length - 1) {
|
|
fileStream.write(',\n');
|
|
} else {
|
|
fileStream.write('\n');
|
|
}
|
|
});
|
|
|
|
fileStream.write('],\n');
|
|
fileStream.write('"blockEvents": ' + JSON.stringify(global.autocadEvents.blockEvents) + ',\n');
|
|
fileStream.write('"entityEvents": ' + JSON.stringify(global.autocadEvents.entityEvents) + '\n');
|
|
fileStream.write('}');
|
|
fileStream.end();
|
|
|
|
console.log(`Events saved to ${filePath} (stream method)`);
|
|
} else {
|
|
// For smaller datasets, use the simpler approach
|
|
fs.writeFileSync(filePath, JSON.stringify(global.autocadEvents, null, 2));
|
|
console.log(`Events saved to ${filePath}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving events to disk:', error);
|
|
}
|
|
}
|
|
|
|
// Connect to MongoDB (optional - comment out if you don't want to use it)
|
|
// mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/autocad_tracker')
|
|
// .then(() => console.log('Connected to MongoDB'))
|
|
// .catch(err => console.error('Could not connect to MongoDB', err));
|
|
|
|
// Default route
|
|
app.get('/', (req, res) => {
|
|
res.redirect('/index.html');
|
|
});
|
|
|
|
// Legacy route for backward compatibility
|
|
app.get('/attribute-history', (req, res) => {
|
|
res.redirect('/index.html');
|
|
});
|
|
|
|
// Update the formatBlockData function for better color formatting
|
|
function formatBlockData(block) {
|
|
let output = '';
|
|
|
|
// Add basic block info
|
|
output += `\n--- Block Details ---`;
|
|
output += `\nName: ${block.BlockName}`;
|
|
output += `\nObject ID: ${block.ObjectId}`;
|
|
|
|
// Add position information
|
|
if (block.NewPosition) {
|
|
output += `\nPosition: (${block.NewPosition.X.toFixed(2)}, ${block.NewPosition.Y.toFixed(2)}, ${block.NewPosition.Z.toFixed(2)})`;
|
|
|
|
// Add old position if available (for moved blocks)
|
|
if (block.OldPosition) {
|
|
output += `\nOld Position: (${block.OldPosition.X.toFixed(2)}, ${block.OldPosition.Y.toFixed(2)}, ${block.OldPosition.Z.toFixed(2)})`;
|
|
}
|
|
} else if (block.Position) {
|
|
output += `\nPosition: (${block.Position.X.toFixed(2)}, ${block.Position.Y.toFixed(2)}, ${block.Position.Z.toFixed(2)})`;
|
|
}
|
|
|
|
// Add rotation information
|
|
if (block.NewRotation !== undefined) {
|
|
output += `\nRotation: ${(block.NewRotation * 180 / Math.PI).toFixed(2)} degrees`;
|
|
|
|
// Add old rotation if available
|
|
if (block.OldRotation !== undefined) {
|
|
output += `\nOld Rotation: ${(block.OldRotation * 180 / Math.PI).toFixed(2)} degrees`;
|
|
}
|
|
}
|
|
|
|
// Add scale information
|
|
if (block.NewScale) {
|
|
output += `\nScale: X=${block.NewScale.X.toFixed(3)}, Y=${block.NewScale.Y.toFixed(3)}, Z=${block.NewScale.Z.toFixed(3)}`;
|
|
|
|
// Add old scale if available
|
|
if (block.OldScale) {
|
|
output += `\nOld Scale: X=${block.OldScale.X.toFixed(3)}, Y=${block.OldScale.Y.toFixed(3)}, Z=${block.OldScale.Z.toFixed(3)}`;
|
|
}
|
|
}
|
|
|
|
// Add attribute information
|
|
if (block.Attributes && Object.keys(block.Attributes).length > 0) {
|
|
output += `\nAttributes:`;
|
|
for (const [key, value] of Object.entries(block.Attributes)) {
|
|
output += `\n ${key}: ${value}`;
|
|
}
|
|
} else {
|
|
output += `\nNo attributes`;
|
|
}
|
|
|
|
// Add attribute changes if available
|
|
if (block.AttributeChanges && Object.keys(block.AttributeChanges).length > 0) {
|
|
output += `\nAttribute Changes:`;
|
|
for (const [key, change] of Object.entries(block.AttributeChanges)) {
|
|
output += `\n ${key}: "${change.OldValue}" -> "${change.NewValue}"`;
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
// Add an endpoint to view detailed block information
|
|
app.get('/api/events/blocks/details/:id', (req, res) => {
|
|
const blockId = req.params.id;
|
|
|
|
// Search for the block in all categories
|
|
const allBlocks = Object.values(global.autocadEvents.blockEvents)
|
|
.flat()
|
|
.filter(event => event.ObjectId === blockId);
|
|
|
|
if (allBlocks.length === 0) {
|
|
return res.status(404).json({ message: 'Block not found' });
|
|
}
|
|
|
|
// Get the latest block data
|
|
const latestBlock = allBlocks[allBlocks.length - 1];
|
|
|
|
// Format the block data
|
|
const formattedData = {
|
|
blockData: latestBlock,
|
|
formatted: formatBlockData(latestBlock),
|
|
history: allBlocks.map(block => ({
|
|
timestamp: block.Timestamp,
|
|
eventType: block.EventType
|
|
}))
|
|
};
|
|
|
|
res.json(formattedData);
|
|
});
|
|
|
|
// Add an endpoint to get a list of all blocks with their latest attributes
|
|
app.get('/api/blocks', (req, res) => {
|
|
// Get all block events
|
|
const allBlockEvents = global.autocadEvents.all.filter(event =>
|
|
event.EventType && event.EventType.startsWith('Block')
|
|
);
|
|
|
|
// Create a map to store the latest state of each block
|
|
const blockMap = new Map();
|
|
|
|
// Process events to get the latest state of each block
|
|
allBlockEvents.forEach(event => {
|
|
// Use object ID as the unique identifier
|
|
const blockId = event.ObjectId;
|
|
|
|
// Update the map with the latest event for each block
|
|
blockMap.set(blockId, event);
|
|
});
|
|
|
|
// Convert map to array of blocks
|
|
const blocks = Array.from(blockMap.values()).map(block => ({
|
|
id: block.ObjectId,
|
|
name: block.BlockName,
|
|
position: block.NewPosition || block.Position,
|
|
attributes: block.Attributes || {},
|
|
lastEventType: block.EventType,
|
|
timestamp: block.Timestamp
|
|
}));
|
|
|
|
res.json(blocks);
|
|
});
|
|
|
|
// Add an endpoint to get all attribute changes
|
|
app.get('/api/blocks/attribute-changes', (req, res) => {
|
|
const attributeChanges = global.autocadEvents.blockEvents.attributesChanged;
|
|
|
|
// Process attribute changes to make them more meaningful
|
|
const processedChanges = attributeChanges.map(event => {
|
|
// Extract basic information
|
|
const { BlockName, ObjectId, Timestamp, Attributes, AttributeChanges } = event;
|
|
|
|
// Process each changed attribute
|
|
const changes = [];
|
|
if (AttributeChanges) {
|
|
for (const [tag, change] of Object.entries(AttributeChanges)) {
|
|
changes.push({
|
|
tag,
|
|
oldValue: change.OldValue,
|
|
newValue: change.NewValue,
|
|
changeTime: Timestamp
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
blockName: BlockName,
|
|
objectId: ObjectId,
|
|
timestamp: Timestamp,
|
|
currentAttributes: Attributes || {},
|
|
changes
|
|
};
|
|
});
|
|
|
|
res.json(processedChanges);
|
|
});
|
|
|
|
// Add an endpoint to get attribute change history for a specific block
|
|
app.get('/api/blocks/:id/attribute-history', (req, res) => {
|
|
const blockId = req.params.id;
|
|
|
|
// Find all attribute change events for this block
|
|
const attributeChanges = global.autocadEvents.all
|
|
.filter(event =>
|
|
event.ObjectId === blockId &&
|
|
(event.AttributeChanges || event.EventType === 'BlockAttributesChanged')
|
|
)
|
|
.sort((a, b) => new Date(a.Timestamp) - new Date(b.Timestamp));
|
|
|
|
if (attributeChanges.length === 0) {
|
|
return res.status(404).json({ message: 'No attribute changes found for this block' });
|
|
}
|
|
|
|
// Process the attribute history
|
|
const history = [];
|
|
const latestAttributes = {};
|
|
|
|
attributeChanges.forEach(event => {
|
|
const timestamp = event.Timestamp;
|
|
|
|
// Track changes for each attribute
|
|
if (event.AttributeChanges) {
|
|
for (const [tag, change] of Object.entries(event.AttributeChanges)) {
|
|
history.push({
|
|
tag,
|
|
oldValue: change.OldValue,
|
|
newValue: change.NewValue,
|
|
timestamp,
|
|
blockName: event.BlockName
|
|
});
|
|
|
|
// Update latest attribute value
|
|
latestAttributes[tag] = change.NewValue;
|
|
}
|
|
}
|
|
|
|
// Also get initial values from Attributes field if present
|
|
if (event.Attributes && Object.keys(event.Attributes).length > 0) {
|
|
for (const [tag, value] of Object.entries(event.Attributes)) {
|
|
if (!latestAttributes[tag]) {
|
|
latestAttributes[tag] = value;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Return formatted response
|
|
res.json({
|
|
blockId,
|
|
blockName: attributeChanges[0].BlockName,
|
|
latestAttributes,
|
|
history
|
|
});
|
|
});
|
|
|
|
// Add an endpoint to get position change history for a specific block
|
|
app.get('/api/blocks/:id/position-history', (req, res) => {
|
|
const blockId = req.params.id;
|
|
|
|
// Find all position change events for this block
|
|
const positionChanges = global.autocadEvents.all
|
|
.filter(event =>
|
|
event.ObjectId === blockId &&
|
|
(event.EventType === 'BlockMoved' ||
|
|
event.EventType === 'BlockModified' ||
|
|
event.EventType === 'BlockAdded') &&
|
|
(event.NewPosition || event.Position)
|
|
)
|
|
.sort((a, b) => new Date(a.Timestamp) - new Date(b.Timestamp));
|
|
|
|
if (positionChanges.length === 0) {
|
|
return res.status(404).json({ message: 'No position changes found for this block' });
|
|
}
|
|
|
|
// Process the position history
|
|
const history = positionChanges.map(event => {
|
|
const entry = {
|
|
timestamp: event.Timestamp,
|
|
eventType: event.EventType,
|
|
blockName: event.BlockName
|
|
};
|
|
|
|
// Add position data
|
|
if (event.NewPosition) {
|
|
entry.position = {
|
|
x: event.NewPosition.X,
|
|
y: event.NewPosition.Y,
|
|
z: event.NewPosition.Z
|
|
};
|
|
|
|
// Add old position if available
|
|
if (event.OldPosition) {
|
|
entry.oldPosition = {
|
|
x: event.OldPosition.X,
|
|
y: event.OldPosition.Y,
|
|
z: event.OldPosition.Z
|
|
};
|
|
}
|
|
} else if (event.Position) {
|
|
entry.position = {
|
|
x: event.Position.X,
|
|
y: event.Position.Y,
|
|
z: event.Position.Z
|
|
};
|
|
}
|
|
|
|
return entry;
|
|
});
|
|
|
|
// Get latest position
|
|
const latestEvent = positionChanges[positionChanges.length - 1];
|
|
const latestPosition = latestEvent.NewPosition || latestEvent.Position;
|
|
|
|
// Return formatted response
|
|
res.json({
|
|
blockId,
|
|
blockName: positionChanges[0].BlockName,
|
|
latestPosition: {
|
|
x: latestPosition.X,
|
|
y: latestPosition.Y,
|
|
z: latestPosition.Z
|
|
},
|
|
history
|
|
});
|
|
});
|
|
|
|
// Add an endpoint to get server status and performance metrics
|
|
app.get('/api/status', (req, res) => {
|
|
const status = {
|
|
uptime: process.uptime(),
|
|
memoryUsage: process.memoryUsage(),
|
|
eventsCount: {
|
|
total: global.autocadEvents.all.length,
|
|
blocks: Object.keys(global.autocadEvents.blockEvents).reduce(
|
|
(total, key) => total + global.autocadEvents.blockEvents[key].length, 0
|
|
),
|
|
entities: Object.keys(global.autocadEvents.entityEvents).reduce(
|
|
(total, key) => total + global.autocadEvents.entityEvents[key].length, 0
|
|
)
|
|
},
|
|
sseClients: sseClients.length,
|
|
lastSaveTime: new Date(lastSaveTime).toISOString()
|
|
};
|
|
|
|
res.json(status);
|
|
});
|
|
|
|
// Add an endpoint for checking server status (lightweight compared to SSE)
|
|
app.get('/api/server-status', (req, res) => {
|
|
res.json({
|
|
status: 'online',
|
|
timestamp: new Date().toISOString(),
|
|
uptime: process.uptime()
|
|
});
|
|
});
|
|
|
|
// Start server
|
|
const PORT = process.env.PORT || 3000;
|
|
app.listen(PORT, () => {
|
|
console.log(`Server is running on port ${PORT}`);
|
|
console.log(`API endpoints available at:`);
|
|
console.log(` - GET /api/events: Get all events`);
|
|
console.log(` - GET /api/events/blocks: Get all block events`);
|
|
console.log(` - GET /api/events/blocks/:type: Get block events by type`);
|
|
console.log(` - GET /api/events/entities: Get all entity events`);
|
|
console.log(` - GET /api/events/entities/:type: Get entity events by type`);
|
|
console.log(` - DELETE /api/events: Clear all events`);
|
|
console.log(` - GET /api/events/blocks/details/:id: Get detailed block information`);
|
|
console.log(` - GET /api/blocks: Get a list of all blocks with their latest attributes`);
|
|
console.log(` - GET /api/blocks/attribute-changes: Get all attribute changes`);
|
|
console.log(` - GET /api/blocks/:id/attribute-history: Get attribute change history for a specific block`);
|
|
console.log(` - GET /api/blocks/:id/position-history: Get position change history for a specific block`);
|
|
console.log(` - GET /api/status: Get server status and performance metrics`);
|
|
console.log(` - GET /api/server-status: Get server status (lightweight)`);
|
|
console.log(` - Front-end interface available at: http://localhost:${PORT}`);
|
|
});
|