|
|
@@ -0,0 +1,89 @@
|
|
|
+// lib/storage.js
|
|
|
+import fs from "fs/promises";
|
|
|
+import path from "path";
|
|
|
+
|
|
|
+const ROOT = process.env.NAS_ROOT_PATH;
|
|
|
+
|
|
|
+if (!ROOT) {
|
|
|
+ console.warn(
|
|
|
+ "[storage] NAS_ROOT_PATH ist nicht gesetzt – Dateizugriff wird fehlschlagen"
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+function fullPath(...segments) {
|
|
|
+ if (!ROOT) {
|
|
|
+ throw new Error("NAS_ROOT_PATH ist nicht gesetzt");
|
|
|
+ }
|
|
|
+
|
|
|
+ return path.join(ROOT, ...segments.map(String));
|
|
|
+}
|
|
|
+
|
|
|
+// Hilfsfunktion: sortiert numerische Strings wie "1","2","10" korrekt
|
|
|
+function sortNumericStrings(a, b) {
|
|
|
+ const na = parseInt(a, 10);
|
|
|
+ const nb = parseInt(b, 10);
|
|
|
+ if (!Number.isNaN(na) && !Number.isNaN(nb)) {
|
|
|
+ return na - nb;
|
|
|
+ }
|
|
|
+ return a.localeCompare(b, "de");
|
|
|
+}
|
|
|
+
|
|
|
+export async function listBranches() {
|
|
|
+ const entries = await fs.readdir(fullPath(), { withFileTypes: true });
|
|
|
+
|
|
|
+ return entries
|
|
|
+ .filter(
|
|
|
+ (e) =>
|
|
|
+ e.isDirectory() &&
|
|
|
+ e.name !== "@Recently-Snapshot" &&
|
|
|
+ /^NL\d+$/i.test(e.name)
|
|
|
+ )
|
|
|
+ .map((e) => e.name)
|
|
|
+ .sort((a, b) =>
|
|
|
+ sortNumericStrings(a.replace("NL", ""), b.replace("NL", ""))
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export async function listYears(branch) {
|
|
|
+ const dir = fullPath(branch);
|
|
|
+ const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
|
+
|
|
|
+ return entries
|
|
|
+ .filter((e) => e.isDirectory() && /^\d{4}$/.test(e.name))
|
|
|
+ .map((e) => e.name)
|
|
|
+ .sort(sortNumericStrings);
|
|
|
+}
|
|
|
+
|
|
|
+export async function listMonths(branch, year) {
|
|
|
+ const dir = fullPath(branch, year);
|
|
|
+ const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
|
+
|
|
|
+ return entries
|
|
|
+ .filter((e) => e.isDirectory() && /^\d{1,2}$/.test(e.name))
|
|
|
+ .map((e) => e.name.padStart(2, "0"))
|
|
|
+ .sort(sortNumericStrings);
|
|
|
+}
|
|
|
+
|
|
|
+export async function listDays(branch, year, month) {
|
|
|
+ const dir = fullPath(branch, month.length === 1 ? year : year, month);
|
|
|
+ const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
|
+
|
|
|
+ return entries
|
|
|
+ .filter((e) => e.isDirectory() && /^\d{1,2}$/.test(e.name))
|
|
|
+ .map((e) => e.name.padStart(2, "0"))
|
|
|
+ .sort(sortNumericStrings);
|
|
|
+}
|
|
|
+
|
|
|
+export async function listFiles(branch, year, month, day) {
|
|
|
+ const dir = fullPath(branch, year, month, day);
|
|
|
+ const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
|
+
|
|
|
+ return entries
|
|
|
+ .filter((name) => name.toLowerCase().endsWith(".pdf"))
|
|
|
+ .sort((a, b) => a.localeCompare(b, "de"))
|
|
|
+ .map((name) => ({
|
|
|
+ name,
|
|
|
+ // relativer Pfad, den du später für Downloads nutzen kannst
|
|
|
+ relativePath: `${branch}/${year}/${month}/${day}/${name}`,
|
|
|
+ }));
|
|
|
+}
|