optimized

This commit is contained in:
proddy
2025-10-20 22:18:43 +02:00
parent 87bcd4598a
commit 812c6ac475

View File

@@ -1,33 +1,24 @@
// used to simulate
// - file uploads
// - EventSource (SSE) for log messages
// Mock server for development
// Simulates file uploads and EventSource (SSE) for log messages
import formidable from 'formidable';
function pad(number) {
let r = String(number);
if (r.length === 1) {
r = '0' + r;
}
return r;
}
// Optimized padding function
const pad = (number) => String(number).padStart(2, '0');
// e.g. 2024-03-29 07:02:37.856
Date.prototype.toISOString = function () {
return (
this.getUTCFullYear() +
'-' +
pad(this.getUTCMonth() + 1) +
'-' +
pad(this.getUTCDate()) +
' ' +
pad(this.getUTCHours()) +
':' +
pad(this.getUTCMinutes()) +
':' +
pad(this.getUTCSeconds()) +
'.' +
String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5)
// Cached date formatter to avoid prototype pollution
const formatDate = (date) => {
const year = date.getUTCFullYear();
const month = pad(date.getUTCMonth() + 1);
const day = pad(date.getUTCDate());
const hours = pad(date.getUTCHours());
const minutes = pad(date.getUTCMinutes());
const seconds = pad(date.getUTCSeconds());
const milliseconds = String((date.getUTCMilliseconds() / 1000).toFixed(3)).slice(
2,
5
);
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
};
export default () => {
@@ -35,97 +26,129 @@ export default () => {
name: 'vite:mockserver',
configureServer: async (server) => {
server.middlewares.use(async (req, res, next) => {
// catch any file uploads
// Handle file uploads
if (req.url.startsWith('/rest/uploadFile')) {
// show progress
const fileSize = parseInt(req.headers['content-length'] || '0', 10);
let progress = 0;
const file_size = req.headers['content-length'];
console.log('File size: ' + file_size);
req.on('data', async (chunk) => {
// Track upload progress
req.on('data', (chunk) => {
progress += chunk.length;
const percentage = (progress / file_size) * 100;
console.log(`Progress: ${Math.round(percentage)}%`);
// await new Promise((resolve) => setTimeout(() => resolve(), 3000)); // slow it down
if (fileSize > 0) {
const percentage = Math.round((progress / fileSize) * 100);
console.log(`Upload progress: ${percentage}%`);
}
});
const form = formidable({});
let fields;
let files;
try {
[fields, files] = await form.parse(req);
} catch (err) {
console.error('Not json form content');
res.writeHead(err.httpCode || 400, {
'Content-Type': 'text/plain'
const form = formidable({
maxFileSize: 50 * 1024 * 1024, // 50MB limit
keepExtensions: true
});
res.end(String(err));
const [fields, files] = await form.parse(req);
if (Object.keys(files).length === 0) {
res.statusCode = 400;
res.end('No file uploaded');
return;
}
// only process when we have a file
if (Object.keys(files).length > 0) {
const uploaded_file = files.file[0];
const file_name = uploaded_file.originalFilename;
const file_extension = file_name.substring(
file_name.lastIndexOf('.') + 1
const uploadedFile = files.file[0];
const fileName = uploadedFile.originalFilename;
const fileExtension = fileName
.substring(fileName.lastIndexOf('.') + 1)
.toLowerCase();
console.log(
`File uploaded: ${fileName} (${fileExtension}, ${fileSize} bytes)`
);
console.log('Filename: ' + file_name);
console.log('Extension: ' + file_extension);
console.log('File size: ' + file_size);
// Validate file extension
const validExtensions = new Set(['bin', 'json', 'md5']);
if (!validExtensions.has(fileExtension)) {
res.statusCode = 406;
res.end('Invalid file extension');
return;
}
if (file_extension === 'bin' || file_extension === 'json') {
console.log('File uploaded successfully!');
} else if (file_extension === 'md5') {
console.log('MD5 hash generated successfully!');
// Handle different file types
if (fileExtension === 'md5') {
res.setHeader('Content-Type', 'application/json');
res.end(
JSON.stringify({
md5: 'ef4304fc4d9025a58dcf25d71c882d2c'
})
);
} else {
res.statusCode = 406;
console.log('Invalid file extension!');
}
}
console.log('File uploaded successfully!');
res.end();
}
} catch (err) {
console.error('Upload error:', err.message);
res.statusCode = err.httpCode || 400;
res.setHeader('Content-Type', 'text/plain');
res.end(err.message);
}
}
// SSE Eventsource
// Handle Server-Sent Events (SSE) for log streaming
else if (req.url.startsWith('/es/log')) {
// Set SSE headers
res.writeHead(200, {
Connection: 'keep-alive',
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream'
'Content-Type': 'text/event-stream',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control'
});
let count = 0;
const interval = setInterval(() => {
let message = 'message #' + count;
if (count % 6 === 1) {
let messageCount = 0;
const logLevels = [3, 4, 5, 6, 7, 8]; // Different log levels
const logNames = ['system', 'ems', 'wifi', 'mqtt', 'ntp', 'api'];
const sendLogMessage = () => {
const level = logLevels[messageCount % logLevels.length];
const name = logNames[messageCount % logNames.length];
let message = `Log message #${messageCount}`;
// Add long message every 6th message
if (messageCount % 6 === 1) {
message +=
' that is a long message that will be wrapped, to test if it gets truncated';
' - This is a longer message to test text wrapping and truncation behavior in the UI';
}
const data = {
t: new Date().toISOString(),
l: 3 + (count % 6),
i: count,
n: 'system',
const logData = {
t: formatDate(new Date()),
l: level,
i: messageCount,
n: name,
m: message
};
count++;
res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 1000);
// if client closes connection
res.on('close', () => {
console.log('Closing ES connection');
res.write(`data: ${JSON.stringify(logData)}\n\n`);
messageCount++;
};
// Send initial message
sendLogMessage();
// Set up interval for periodic messages
const interval = setInterval(sendLogMessage, 1000);
// Clean up on connection close
const cleanup = () => {
console.log('SSE connection closed');
clearInterval(interval);
if (!res.destroyed) {
res.end();
});
}
};
res.on('close', cleanup);
res.on('error', cleanup);
} else {
next(); // move on to the next middleware function in chain
next(); // Continue to next middleware
}
});
}