pathMapping.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. /**
  2. * Map Qsirch items back into our NAS convention:
  3. *
  4. * Qsirch fields observed:
  5. * - item.path = "Niederlassungen/NL20/2025/12/18" (directory)
  6. * - item.name = "Stapel_Seiten-4_Zeit-141039" (basename without extension)
  7. * - item.extension = "pdf"
  8. *
  9. * We strictly accept only:
  10. * <prefix>/<branch>/<year>/<month>/<day>
  11. *
  12. * And we build:
  13. * - filename: "<name>.<extension>"
  14. * - relativePath: "<branch>/<year>/<month>/<day>/<filename>"
  15. *
  16. * Any unexpected shape is rejected (returns null) as defense-in-depth.
  17. */
  18. const BRANCH_RE = /^NL\d+$/;
  19. const YEAR_RE = /^\d{4}$/;
  20. const MONTH_RE = /^(0[1-9]|1[0-2])$/;
  21. const DAY_RE = /^(0[1-9]|[12]\d|3[01])$/;
  22. function stripLeadingSlash(p) {
  23. const s = String(p || "");
  24. return s.startsWith("/") ? s.slice(1) : s;
  25. }
  26. function normalizePrefix(prefix) {
  27. let p = stripLeadingSlash(prefix);
  28. p = p.trim();
  29. if (p.endsWith("/")) p = p.replace(/\/+$/, "");
  30. return p;
  31. }
  32. function stripPrefix(pathValue, prefix) {
  33. const p = stripLeadingSlash(pathValue).trim();
  34. const pref = normalizePrefix(prefix);
  35. if (!pref) return p;
  36. if (p === pref) return "";
  37. if (p.startsWith(`${pref}/`)) return p.slice(pref.length + 1);
  38. return null;
  39. }
  40. function toFilename(baseName, ext) {
  41. const safeBase = String(baseName || "").trim();
  42. const safeExt = String(ext || "").trim();
  43. if (!safeBase) return null;
  44. if (!safeExt) return null;
  45. const lowerExt = safeExt.toLowerCase();
  46. // We only support PDFs for the Lieferscheine workflow.
  47. if (lowerExt !== "pdf") return null;
  48. // Qsirch usually returns name without extension. If it already ends with ".pdf",
  49. // keep it as-is to avoid double extensions.
  50. if (safeBase.toLowerCase().endsWith(`.${lowerExt}`)) {
  51. return safeBase;
  52. }
  53. return `${safeBase}.${safeExt}`;
  54. }
  55. /**
  56. * @param {any} item - Qsirch item
  57. * @param {{ pathPrefix: string }} options
  58. * @returns {null | {
  59. * branch: string,
  60. * date: string,
  61. * year: string,
  62. * month: string,
  63. * day: string,
  64. * filename: string,
  65. * relativePath: string
  66. * }}
  67. */
  68. export function mapQsirchItemToSearchItem(item, { pathPrefix }) {
  69. const dirPath = typeof item?.path === "string" ? item.path : null;
  70. const name = typeof item?.name === "string" ? item.name : null;
  71. const ext = typeof item?.extension === "string" ? item.extension : null;
  72. if (!dirPath || !name || !ext) return null;
  73. const stripped = stripPrefix(dirPath, pathPrefix);
  74. if (stripped === null) return null;
  75. const parts = stripped.split("/").filter(Boolean);
  76. // Must be exactly NLxx/YYYY/MM/DD
  77. if (parts.length !== 4) return null;
  78. const [branch, year, month, day] = parts;
  79. if (!BRANCH_RE.test(branch)) return null;
  80. if (!YEAR_RE.test(year)) return null;
  81. if (!MONTH_RE.test(month)) return null;
  82. if (!DAY_RE.test(day)) return null;
  83. const filename = toFilename(name, ext);
  84. if (!filename) return null;
  85. const date = `${year}-${month}-${day}`;
  86. const relativePath = `${branch}/${year}/${month}/${day}/${filename}`;
  87. return { branch, date, year, month, day, filename, relativePath };
  88. }