Compare commits

...
Author SHA1 Message Date
LudyandGitHub e28850ca6a Merge branch 'main' into bump_babel_20260701 2026-07-09 23:14:45 +02:00
LudyandGitHub ab53f7a7de Merge branch 'main' into bump_babel_20260701 2026-07-09 11:05:36 +02:00
LudyandGitHub e389ab1433 Merge branch 'main' into bump_babel_20260701 2026-07-04 14:27:05 +02:00
Ludy87 c250349261 Centralize HTTP header reading logic
Extract repeated header reading code into a reusable `readResponseHeader()` utility function that handles various header formats (get() method, lowercase, camelCase keys) and value types. This reduces duplication across FileSelectorPicker, FileManagerContext, FilesModalContext, SignatureStorageService, ShareLinkLoader, and shareLinkImport, making header access more consistent and robust.
2026-07-01 21:32:47 +02:00
Ludy87 7170a18dd7 Update package-lock.json 2026-07-01 21:23:26 +02:00
Ludy87 60d5fff490 Update package.json 2026-07-01 21:23:21 +02:00
Ludy87 e124d1f325 Update dependencies: Babel, PostHog, vitest, and remove OpenTelemetry
Update Babel packages to 7.29.7, PostHog to latest, vitest to 3.2.6, and vite to 7.3.6. Remove OpenTelemetry telemetry packages and protobufjs dependencies. Update axios and form-data for improved security and compatibility.
2026-07-01 21:13:50 +02:00
9 changed files with 495 additions and 688 deletions
@@ -20,6 +20,7 @@ import apiClient from "@app/services/apiClient";
import {
parseContentDispositionFilename,
extractLatestFilesFromBundle,
readResponseHeader,
} from "@app/services/shareBundleUtils";
import { truncateCenter } from "@app/utils/textUtils";
import { generateThumbnailForFile } from "@app/utils/thumbnailUtils";
@@ -270,14 +271,8 @@ export function FileSelectorPicker({
skipAuthRedirect: true,
} as any,
);
const ct =
res.headers?.["content-type"] ||
res.headers?.["Content-Type"] ||
"";
const disp =
res.headers?.["content-disposition"] ||
res.headers?.["Content-Disposition"] ||
"";
const ct = readResponseHeader(res.headers, "content-type");
const disp = readResponseHeader(res.headers, "content-disposition");
const files = await extractLatestFilesFromBundle(
res.data as Blob,
parseContentDispositionFilename(disp) || "shared-file",
@@ -294,14 +289,8 @@ export function FileSelectorPicker({
skipAuthRedirect: true,
} as any,
);
const ct =
res.headers?.["content-type"] ||
res.headers?.["Content-Type"] ||
"";
const disp =
res.headers?.["content-disposition"] ||
res.headers?.["Content-Disposition"] ||
"";
const ct = readResponseHeader(res.headers, "content-type");
const disp = readResponseHeader(res.headers, "content-disposition");
const files = await extractLatestFilesFromBundle(
res.data as Blob,
parseContentDispositionFilename(disp) || stub.name,
@@ -22,6 +22,7 @@ import { alert } from "@app/components/toast";
import {
extractLatestFilesFromBundle,
parseContentDispositionFilename,
readResponseHeader,
} from "@app/services/shareBundleUtils";
import { useTranslation } from "react-i18next";
import { openFilesFromDisk } from "@app/services/openFilesFromDisk";
@@ -1003,16 +1004,14 @@ export const FileManagerProvider: React.FC<FileManagerProviderProps> = ({
skipAuthRedirect: true,
} as any,
);
const contentType =
(response.headers &&
(response.headers["content-type"] ||
response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = readResponseHeader(
response.headers,
"content-type",
);
const disposition = readResponseHeader(
response.headers,
"content-disposition",
);
const filename =
parseContentDispositionFilename(disposition) ||
file.name ||
@@ -1270,6 +1269,3 @@ export const useFileManagerContext = (): FileManagerContextValue => {
return context;
};
// Export the context for advanced use cases
export { FileManagerContext };
@@ -23,6 +23,7 @@ import {
isZipBundle,
loadShareBundleEntries,
parseContentDispositionFilename,
readResponseHeader,
} from "@app/services/shareBundleUtils";
interface FilesModalContextType {
@@ -184,16 +185,11 @@ export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({
skipAuthRedirect: true,
} as any,
);
const contentType =
(response.headers &&
(response.headers["content-type"] ||
response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = readResponseHeader(response.headers, "content-type");
const disposition = readResponseHeader(
response.headers,
"content-disposition",
);
const filename =
parseContentDispositionFilename(disposition) || "server-file";
const blob = response.data as Blob;
@@ -210,16 +206,11 @@ export const FilesModalProvider: React.FC<{ children: React.ReactNode }> = ({
skipAuthRedirect: true,
} as any,
);
const contentType =
(response.headers &&
(response.headers["content-type"] ||
response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = readResponseHeader(response.headers, "content-type");
const disposition = readResponseHeader(
response.headers,
"content-disposition",
);
const filename =
parseContentDispositionFilename(disposition) || "shared-file";
const blob = response.data as Blob;
@@ -4,6 +4,48 @@ import type { ShareBundleManifest } from "@app/services/serverStorageBundle";
const MANIFEST_FILENAME = "stirling-share.json";
type HeaderLike = {
get?: (name: string) => unknown;
[key: string]: unknown;
};
function headerValueToString(value: unknown): string {
if (typeof value === "string") return value;
if (typeof value === "number" || typeof value === "boolean") {
return String(value);
}
if (Array.isArray(value)) {
const firstString = value.find(
(entry): entry is string => typeof entry === "string",
);
return firstString ?? "";
}
return "";
}
function toHeaderKey(name: string): string {
return name.replace(
/(^|-)([a-z])/g,
(_, prefix: string, char: string) => prefix + char.toUpperCase(),
);
}
export function readResponseHeader(headers: unknown, name: string): string {
if (!headers || typeof headers !== "object") {
return "";
}
const typedHeaders = headers as HeaderLike;
if (typeof typedHeaders.get === "function") {
return headerValueToString(typedHeaders.get(name));
}
return (
headerValueToString(typedHeaders[name]) ||
headerValueToString(typedHeaders[toHeaderKey(name)])
);
}
export function parseContentDispositionFilename(
disposition?: string,
): string | null {
@@ -1,5 +1,6 @@
import apiClient from "@app/services/apiClient";
import type { SavedSignature } from "@app/types/signature";
import { readResponseHeader } from "@app/services/shareBundleUtils";
export type StorageType = "backend" | "localStorage";
@@ -166,7 +167,9 @@ class SignatureStorageService {
// Convert to data URL (base64) for both display and use
const blob = new Blob([imageResponse.data], {
type: imageResponse.headers["content-type"] || "image/png",
type:
readResponseHeader(imageResponse.headers, "content-type") ||
"image/png",
});
const dataUrl = await new Promise<string>((resolve, reject) => {
@@ -15,6 +15,7 @@ import {
isZipBundle,
loadShareBundleEntries,
parseContentDispositionFilename,
readResponseHeader,
} from "@app/services/shareBundleUtils";
interface ShareLinkLoaderProps {
@@ -78,16 +79,14 @@ export default function ShareLinkLoader({ token }: ShareLinkLoaderProps) {
);
if (signal.aborted) return;
const contentType =
(response.headers &&
(response.headers["content-type"] ||
response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = readResponseHeader(
response.headers,
"content-type",
);
const disposition = readResponseHeader(
response.headers,
"content-disposition",
);
const filename =
parseContentDispositionFilename(disposition) || "shared-file";
const blob = response.data as Blob;
@@ -8,6 +8,7 @@ import {
isZipBundle,
loadShareBundleEntries,
parseContentDispositionFilename,
readResponseHeader,
} from "@app/services/shareBundleUtils";
export interface ShareLinkMetadata {
@@ -44,15 +45,11 @@ export async function downloadShareLink(token: string): Promise<{
suppressErrorToast: true,
skipAuthRedirect: true,
});
const contentType =
(response.headers &&
(response.headers["content-type"] || response.headers["Content-Type"])) ||
"";
const disposition =
(response.headers &&
(response.headers["content-disposition"] ||
response.headers["Content-Disposition"])) ||
"";
const contentType = readResponseHeader(response.headers, "content-type");
const disposition = readResponseHeader(
response.headers,
"content-disposition",
);
const filename =
parseContentDispositionFilename(disposition) || "shared-file";
const blob = response.data as Blob;
+408 -618
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -150,7 +150,7 @@
"puppeteer": "^24.25.0",
"rollup-plugin-visualizer": "^7.0.1",
"storybook": "^9.1.20",
"tsx": "^4.21.0",
"tsx": "^4.22.4",
"typescript": "^6.0.3",
"typescript-eslint": "^8.61.1",
"vite": "^7.3.2",