Skip to content

Instantly share code, notes, and snippets.

@AlexMachin1997
Created March 29, 2023 13:56
Show Gist options
  • Save AlexMachin1997/09fb1632aadf7cd191ae6510dbd1e516 to your computer and use it in GitHub Desktop.
Save AlexMachin1997/09fb1632aadf7cd191ae6510dbd1e516 to your computer and use it in GitHub Desktop.
const metricsService = require("../services/MetricsService");
const logger = require("../utils/Logger");
/**
* The GET action for /api/v1.0.0/metrics
*/
exports.download = (_, res) => {
try {
// Get the in memory metrics file (Either comes from /metrics folder in the project or the kubernetes persistent storage)
const { name, contentType, lastModified, size } =
metricsService.getMetadata();
// Create a read stream to retrieve the file from storage
const stream = metricsService.createReadStream();
// When a stream error occurs throw that error and pass it to the catch block below
stream.on("error", (error) => new Error(error));
// Provided the stream hasn't error log the successful download message
logger.info("Metrics file has been downloaded successfully");
// Set the downloader headers only after no more errors have occurred
res.header("Content-Disposition", `attachment; filename="${name}"`);
res.header("Content-Type", contentType);
res.header("Content-Length", size);
res.header("Last-Modified", lastModified);
// Return the found file in a binary format
stream.pipe(res);
} catch (error) {
// error retrieving the file from disk
logger.error(`Cannot download metrics file: ${error.message}`, error);
// We do not want to disclose the actual detailed error to the client
const fileNotFound = error?.message?.includes("ENOENT") ?? false;
// Since the download failed either set the status to 404 (File not found) or 500 (Internal server error)
res.statusCode = fileNotFound ? 404 : 500;
// Return the error object
res.send({
name: error.name,
message: fileNotFound
? "The file is not found"
: "An unexpected error occurred retrieving the file",
});
}
};
/**
* The PUT action for /api/v1.0.0/metrics
*/
exports.upload = (req, res) => {
try {
const stream = metricsService.createWriteStream();
// When a stream error occurs throw that error and pass it to the catch block below
stream.on("error", (error) => new Error(error));
// Provided the stream hasn't error log the successful upload message
logger.info("Metrics file has been updated successfully");
// Since the upload has been successful and there is no content return a 204 http status
res.statusCode = 204;
// Required to make sure that the request ends after doing everything above
// NOTE: Without piping the contents of the request you won't be able to retrieve then properly next time you request the file you uploaded (Weird I know......)
req.pipe(stream);
} catch (error) {
logger.error(`Cannot upload metrics file: ${error.message}`, error);
// Since the upload failed set the status 500
res.statusCode = 500;
// Return the error object
res.send({
name: error.name,
message: "An unexpected error occurred saving the file",
});
}
};
const path = require("path");
const fs = require("fs");
const mime = require("mime");
const configService = require("./ConfigService");
const BaseService = require("./BaseService");
/**
* The metrics service.
*/
class MetricsService extends BaseService {
constructor() {
super();
const getMetricsFileName = () => {
const { filePath } = configService.getMetrics();
return filePath;
};
/**
* Gets the metrics metadata.
* @returns
*/
this.getMetadata = () => {
const fileName = getMetricsFileName();
const name = path.basename(fileName);
const contentType = mime.getType(fileName);
const stats = fs.statSync(fileName);
const { size, mtime: lastModified } = stats;
return {
name,
contentType,
lastModified,
size,
};
};
/**
* Gets the metrics readable stream.
* @Returns The metrics stream.
*/
this.createReadStream = () => {
const fileName = getMetricsFileName();
return fs.createReadStream(fileName);
};
/**
* Gets the metrics writeable stream.
* @Returns The metrics stream.
*/
this.createWriteStream = () => {
const fileName = getMetricsFileName();
return fs.createWriteStream(fileName);
};
}
}
module.exports = new MetricsService(); // expose the singleton instance so NodeJS can cache it
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment