kopia lustrzana https://github.com/harvard-lil/archive.social
125 wiersze
3.1 KiB
JavaScript
125 wiersze
3.1 KiB
JavaScript
/**
|
|
* thread-keeper
|
|
* @module utils.SuccessLog
|
|
* @author The Harvard Library Innovation Lab
|
|
* @license MIT
|
|
*/
|
|
import fs from "fs";
|
|
import readline from "node:readline";
|
|
import crypto from "crypto";
|
|
|
|
import { DATA_PATH } from "../const.js";
|
|
|
|
/**
|
|
* Utility class for handling success logs. Keeps trace of the hashes of the PDFs that were generated.
|
|
*/
|
|
export class SuccessLog {
|
|
/**
|
|
* Complete path to `success-log.json`.
|
|
* @type {string}
|
|
*/
|
|
static filepath = `${DATA_PATH}success-log.tsv`;
|
|
|
|
/**
|
|
* Hashmap of all the sha512 hashes present in the current log file.
|
|
* Used for fast lookups.
|
|
*
|
|
* @type {object.<string, boolean>}
|
|
*/
|
|
#hashes = {};
|
|
|
|
/**
|
|
* On init:
|
|
* - Create log file if it doesn't exist
|
|
* - Load hashes from file into `this.#hashes`.
|
|
*/
|
|
constructor() {
|
|
const filepath = SuccessLog.filepath;
|
|
|
|
// Create file if it does not exist
|
|
if (!fs.existsSync(filepath)) {
|
|
this.reset();
|
|
}
|
|
|
|
// Load hashes from existing file into hashmap (asynchronous)
|
|
const readLogs = readline.createInterface({
|
|
input: fs.createReadStream(filepath),
|
|
crlfDelay: Infinity
|
|
});
|
|
|
|
readLogs.on("line", (line) => {
|
|
// Skip lines that are not log lines
|
|
if (line[0] === "d" || line[0] === "\n") {
|
|
return;
|
|
}
|
|
|
|
// Grab last 95 chars of line, check it's a sha512 hash, add to #hashes.
|
|
const lineLength = line.length;
|
|
const hash = line.substring(lineLength - 95);
|
|
|
|
if (hash.length === 95 && hash.startsWith("sha512-")) {
|
|
this.#hashes[hash] = true;
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* Calculates hash of a PDF an:
|
|
* - Creates a success log entry
|
|
* - Updates `this.#hashes` (so it doesn't need to reload from file)
|
|
*
|
|
* @param {string} identifier - Can be an IP or access key
|
|
* @param {string} why - Reason for creating this archive
|
|
* @param {Buffer} pdfBytes - Used to store a SHA512 hash of the PDF that was delivered
|
|
*/
|
|
add(identifier, why, pdfBytes) {
|
|
// Calculate SHA512 hash of the PDF
|
|
const hash = crypto.createHash('sha512').update(pdfBytes).digest('base64');
|
|
|
|
// "why" field sanitization
|
|
why = why
|
|
.replaceAll("\t", " ")
|
|
.replaceAll("\n", " ")
|
|
.replaceAll("<", "")
|
|
.replaceAll(">", "")
|
|
.replaceAll("{", "")
|
|
.replaceAll("}", "");
|
|
|
|
// Save entry
|
|
const entry = `${new Date().toISOString()}\t${identifier}\t${why}\tsha512-${hash}\n`;
|
|
fs.appendFileSync(SuccessLog.filepath, entry);
|
|
this.#hashes[`sha512-${hash}`] = true;
|
|
}
|
|
|
|
/**
|
|
* Checks whether or not a given hash is present in the logs.
|
|
* @param {string} hash
|
|
* @returns {boolean}
|
|
*/
|
|
findHashInLogs(hash) {
|
|
hash = String(hash);
|
|
|
|
// Compensate for the absence of "sha512-"
|
|
if (hash.length === 88) {
|
|
hash = `sha512-${hash}`;
|
|
}
|
|
|
|
if (hash.length < 95) {
|
|
return false;
|
|
}
|
|
|
|
return hash in this.#hashes && this.#hashes[hash] === true;
|
|
}
|
|
|
|
/**
|
|
* Resets `success-log.json`.
|
|
* Also clears `this.#hashes`.
|
|
* @returns {void}
|
|
*/
|
|
reset() {
|
|
fs.writeFileSync(SuccessLog.filepath, "date-time\tidentifier\twhy\thash\n");
|
|
this.#hashes = {};
|
|
}
|
|
}
|