Ctrl K
ComunidadGuías…

lib/knowledge-draft-store.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
var crypto = require("crypto");
var fs = require("fs");
var path = require("path");

var MAX_DRAFT_BYTES = 2 * 1024 * 1024;

function safePart(value) { return crypto.createHash("sha256").update(String(value || "unknown")).digest("hex").slice(0, 24); }
function createKnowledgeDraftStore(baseDir, scopeKey) {
  var root = path.join(baseDir, "knowledge-document-drafts", safePart(scopeKey));
  function safeDirectory(directory, create) {
    var stat;
    try { stat = fs.lstatSync(directory); } catch (error) { if (!create || error.code !== "ENOENT") throw error; fs.mkdirSync(directory, { recursive: true, mode: 0o700 }); stat = fs.lstatSync(directory); }
    if (!stat.isDirectory() || stat.isSymbolicLink()) throw Object.assign(new Error("Knowledge draft path is not a safe directory."), { code: "UNSAFE_PATH" });
  }
  function userRoot(userId) { return path.join(root, safePart(userId)); }
  function target(userId, documentId) { return path.join(userRoot(userId), safePart(documentId) + ".json"); }
  function read(userId, documentId) {
    try { safeDirectory(root, false); safeDirectory(userRoot(userId), false); var file = target(userId, documentId); var stat = fs.lstatSync(file);
      if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_DRAFT_BYTES + 4096) throw Object.assign(new Error("Knowledge draft is not a safe file."), { code: "UNSAFE_PATH" });
      var value = JSON.parse(fs.readFileSync(file, "utf8")); return value && value.version === 1 ? value : null; }
    catch (error) { if (error.code === "ENOENT" || error instanceof SyntaxError) return null; throw error; }
  }
  function save(userId, documentId, draft) {
    var content = draft && draft.content;
    if (typeof content !== "string" || Buffer.byteLength(content, "utf8") > MAX_DRAFT_BYTES) throw new Error("Knowledge draft is invalid or too large.");
    var current = read(userId, documentId); var clientVersion = Math.max(0, Number(draft.clientVersion) || 0);
    if (current && Number(current.clientVersion) > clientVersion) return current;
    var value = { version: 1, documentId: String(documentId), name: String(draft.name || "").slice(0, 500), content: content,
      baseRevision: draft.baseRevision || null, cursor: draft.cursor && typeof draft.cursor === "object" ? { line: Number(draft.cursor.line) || 0, ch: Number(draft.cursor.ch) || 0 } : null,
      clientVersion: clientVersion, newDocument: draft.newDocument === true, savedAt: Date.now() };
    safeDirectory(root, true); safeDirectory(userRoot(userId), true); var file = target(userId, documentId);
    var temporary = file + ".tmp-" + process.pid + "-" + crypto.randomBytes(5).toString("hex");
    try { fs.writeFileSync(temporary, JSON.stringify(value), { flag: "wx", mode: 0o600 }); fs.renameSync(temporary, file); }
    finally { try { fs.unlinkSync(temporary); } catch (error) {} }
    return value;
  }
  function remove(userId, documentId, throughVersion) {
    var current = read(userId, documentId); if (!current) return false;
    if (throughVersion != null && Number(current.clientVersion) > Number(throughVersion)) return false;
    try { fs.unlinkSync(target(userId, documentId)); return true; } catch (error) { if (error.code === "ENOENT") return false; throw error; }
  }
  function list(userId) {
    var folder = userRoot(userId); var names; try { safeDirectory(root, false); safeDirectory(folder, false); } catch (error) { if (error.code === "ENOENT") return []; throw error; }
    try { names = fs.readdirSync(folder); } catch (error) { if (error.code === "ENOENT") return []; throw error; }
    var values = [];
    for (var i = 0; i < names.length; i++) {
      if (!/^[a-f0-9]{24}\.json$/.test(names[i])) continue;
      try { var file = path.join(folder, names[i]); var stat = fs.lstatSync(file); if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_DRAFT_BYTES + 4096) continue;
        var value = JSON.parse(fs.readFileSync(file, "utf8")); if (value && value.version === 1) values.push(value); } catch (error) {}
    }
    return values.sort(function (a, b) { return b.savedAt - a.savedAt; });
  }
  return { read: read, save: save, remove: remove, list: list };
}

module.exports = { createKnowledgeDraftStore: createKnowledgeDraftStore };