Files
Multi-Debrid-Downloader/src/main/history-reveal.ts
T
Sucukdeluxe 069babfd54 feat: deliver the redesigned desktop workspace
Rebuild downloads, link collection, settings, history, and statistics around a responsive desktop shell with compact account and queue tables, contextual navigation, persistent update affordances, unified overlays, and accessible keyboard interactions.

Add safe history-folder reveal IPC, responsive 2560/1920/1366/1120 coverage, deterministic visual fixtures, focused component regressions, and release-tree exclusions for internal working files. Bump the public application version to 2.0.13.
2026-08-10 14:15:57 +02:00

105 lines
3.3 KiB
TypeScript

import { win32 } from "node:path";
import type { HistoryEntry, HistoryRevealResult } from "../shared/types";
export interface HistoryRevealRequest {
entryId: unknown;
}
export interface HistoryRevealStat {
isDirectory: () => boolean;
}
export interface HistoryRevealDependencies {
loadHistory: () => HistoryEntry[] | Promise<HistoryEntry[]>;
stat: (directory: string) => Promise<HistoryRevealStat>;
openPath: (directory: string) => Promise<string>;
}
function normalizeHistoryDirectory(value: unknown): string | null {
if (typeof value !== "string" || value.length === 0 || /[\u0000-\u001f\u007f]/.test(value)) {
return null;
}
const candidate = value.replaceAll("/", "\\");
const lower = candidate.toLocaleLowerCase("en-US");
if (
lower.startsWith("\\\\.\\")
|| lower.startsWith("\\\\?\\")
|| lower.startsWith("\\??\\")
|| lower.startsWith("\\\\globalroot\\")
|| lower.startsWith("\\\\device\\")
) {
return null;
}
const driveAbsolute = /^[A-Za-z]:\\/.test(candidate);
const uncMatch = /^\\\\([^\\]+)\\([^\\]+)(?:\\.*)?$/.exec(candidate);
if (!driveAbsolute && !uncMatch) {
return null;
}
if (driveAbsolute) {
if (candidate.slice(2).includes(":")) {
return null;
}
} else if (candidate.includes(":")) {
return null;
}
if (/[<>|"?*]/.test(driveAbsolute ? candidate.slice(3) : candidate.slice(2))) {
return null;
}
if (uncMatch) {
const server = uncMatch[1].toLocaleLowerCase("en-US");
const share = uncMatch[2].toLocaleLowerCase("en-US");
if ([".", "..", "?", "globalroot", "device"].includes(server) || share === "." || share === "..") {
return null;
}
}
const normalized = win32.normalize(candidate);
if (/^[A-Za-z]:\\/.test(normalized)) {
return normalized;
}
if (/^\\\\[^\\]+\\[^\\]+(?:\\.*)?$/.test(normalized)) {
return normalized;
}
return null;
}
export async function revealHistoryEntry(
request: HistoryRevealRequest,
dependencies: HistoryRevealDependencies
): Promise<HistoryRevealResult> {
if (
typeof request.entryId !== "string"
|| request.entryId.length === 0
|| request.entryId.length > 256
|| request.entryId.trim() !== request.entryId
) {
return { ok: false, reason: "entry-not-found" };
}
const entries = await dependencies.loadHistory();
const entry = entries.find((candidate) => candidate.id === request.entryId);
if (!entry) {
return { ok: false, reason: "entry-not-found" };
}
const normalizedDirectory = normalizeHistoryDirectory(entry.outputDir);
if (!normalizedDirectory) {
return { ok: false, reason: "invalid-output-dir" };
}
let stat: HistoryRevealStat;
try {
stat = await dependencies.stat(normalizedDirectory);
} catch (error) {
const code = typeof error === "object" && error !== null && "code" in error
? (error as { code?: unknown }).code
: undefined;
return { ok: false, reason: code === "ENOENT" || code === "ENOTDIR" ? "output-dir-missing" : "open-failed" };
}
if (!stat.isDirectory()) {
return { ok: false, reason: "output-dir-not-directory" };
}
try {
const error = await dependencies.openPath(normalizedDirectory);
return error === "" ? { ok: true } : { ok: false, reason: "open-failed" };
} catch {
return { ok: false, reason: "open-failed" };
}
}