download url

This commit is contained in:
2025-11-22 00:28:11 +01:00
parent dd7dae5f9d
commit c50372af06
6 changed files with 35 additions and 51 deletions
+8 -10
View File
@@ -40,7 +40,6 @@ interface UseDocumentPreviewArgs {
}; };
selectedFolder?: FolderId | null; selectedFolder?: FolderId | null;
api: ApiClient; api: ApiClient;
resolveApiPath?: (path: string) => string;
notifyApiError: (error: unknown, message: string) => void; notifyApiError: (error: unknown, message: string) => void;
navigate: NavigateHandler; navigate: NavigateHandler;
locationPathname: string; locationPathname: string;
@@ -67,7 +66,6 @@ const useDocumentPreview = ({
documentsManager, documentsManager,
selectedFolder, selectedFolder,
api, api,
resolveApiPath,
notifyApiError, notifyApiError,
navigate, navigate,
locationPathname, locationPathname,
@@ -109,7 +107,7 @@ const useDocumentPreview = ({
async (documentId: DocumentId, { force = false }: { force?: boolean } = {}): Promise<DocumentLink | null> => { async (documentId: DocumentId, { force = false }: { force?: boolean } = {}): Promise<DocumentLink | null> => {
if (!documentId) return null; if (!documentId) return null;
const existing = documentLinks.get(documentId) || null; const existing = documentLinks.get(documentId);
const now = Date.now(); const now = Date.now();
const expiresAt = Number.isFinite(existing?.expiresAt) ? Number(existing?.expiresAt) : null; const expiresAt = Number.isFinite(existing?.expiresAt) ? Number(existing?.expiresAt) : null;
if (!force && existing && (!expiresAt || expiresAt > now)) { if (!force && existing && (!expiresAt || expiresAt > now)) {
@@ -123,17 +121,17 @@ const useDocumentPreview = ({
const request: Promise<DocumentLink | null> = (async () => { const request: Promise<DocumentLink | null> = (async () => {
try { try {
const docResponse = await api.get<{ document?: Record<string, any> }>(`/documents/${documentId}`); const docResponse = await api.get<{ document?: Record<string, any> }>(`/documents/${documentId}`);
const downloadPath = docResponse.data?.document?.current_version?.download_path; const download = docResponse.data?.document?.current_version?.download || null;
if (!downloadPath || !resolveApiPath) { const downloadUrl = download?.url;
throw new Error('Document missing download path'); if (!downloadUrl) {
throw new Error('Document missing download url');
} }
const href = resolveApiPath(downloadPath);
const entry: DocumentLink = { const entry: DocumentLink = {
url: href, url: downloadUrl,
contentType: docResponse.data?.document?.current_version?.version?.content_type || null, contentType: docResponse.data?.document?.current_version?.version?.content_type || null,
filename: docResponse.data?.document?.filename, filename: docResponse.data?.document?.filename,
expiresAt: Date.now() + 5 * 60 * 1000, expiresAt: download?.expires_at,
}; };
setDocumentLinks((prev) => { setDocumentLinks((prev) => {
const next = new Map(prev); const next = new Map(prev);
@@ -152,7 +150,7 @@ const useDocumentPreview = ({
previewInflightRef.current.set(documentId, request); previewInflightRef.current.set(documentId, request);
return request; return request;
}, },
[documentLinks, api, resolveApiPath, notifyApiError], [documentLinks, api, notifyApiError],
); );
const ensurePreviewData = useCallback( const ensurePreviewData = useCallback(
+18 -26
View File
@@ -8,7 +8,7 @@ export interface AssetObject {
ordinal?: number; ordinal?: number;
url?: string | null; url?: string | null;
metadata?: Record<string, unknown> | null; metadata?: Record<string, unknown> | null;
expires_at?: number | null; expires_at?: number;
[key: string]: unknown; [key: string]: unknown;
} }
@@ -16,10 +16,8 @@ export interface AssetLike {
id?: Identifier; id?: Identifier;
asset_type?: string; asset_type?: string;
cardinality?: number | null; cardinality?: number | null;
url?: string | null; download?: { url: string; expires_at: number } | null;
expires_at?: number | null;
metadata?: Record<string, unknown> | null; metadata?: Record<string, unknown> | null;
expiresAt?: number | null;
assets?: Record<string, AssetLike> | AssetLike[] | null; assets?: Record<string, AssetLike> | AssetLike[] | null;
objects?: AssetObject[] | null; objects?: AssetObject[] | null;
[key: string]: unknown; [key: string]: unknown;
@@ -37,19 +35,11 @@ export interface DocumentLike {
[key: string]: unknown; [key: string]: unknown;
} }
export const resolveAssetExpiresAt = ( export const resolveAssetExpiresAt = (asset?: { download?: { expires_at: number } | null } | null): number | null =>
asset?: { expiresAt?: number | null; expires_at?: number | null } | null, asset?.download?.expires_at ?? null;
): number | null => {
const camel = Number(asset?.expiresAt); export const resolveAssetUrl = (asset?: { download?: { url: string } | null } | null): string | null =>
if (Number.isFinite(camel)) { asset?.download?.url ?? null;
return camel;
}
const snake = Number(asset?.expires_at);
if (Number.isFinite(snake)) {
return snake;
}
return null;
};
export type EnsureAssetUrl = ( export type EnsureAssetUrl = (
documentId: Identifier, documentId: Identifier,
@@ -78,7 +68,7 @@ export const getAssetFromVersion = (currentVersion: Nullable<DocumentVersionLike
if (!currentVersion) { if (!currentVersion) {
return null; return null;
} }
return getAssetFromGroup(currentVersion.assets ?? null, assetType); return getAssetFromGroup(currentVersion.assets, assetType);
}; };
const normalizeAssetObjects = (objects?: AssetObject[] | null): AssetObject[] => { const normalizeAssetObjects = (objects?: AssetObject[] | null): AssetObject[] => {
@@ -140,10 +130,11 @@ export class AssetView {
} }
if (ordinal === 1 && this.asset) { if (ordinal === 1 && this.asset) {
if (this.asset.url || this.asset.metadata) { const primaryUrl = resolveAssetUrl(this.asset);
if (primaryUrl || this.asset.metadata) {
return { return {
ordinal: 1, ordinal: 1,
url: this.asset.url || null, url: primaryUrl || null,
metadata: this.asset.metadata || null, metadata: this.asset.metadata || null,
expires_at: resolveAssetExpiresAt(this.asset), expires_at: resolveAssetExpiresAt(this.asset),
}; };
@@ -242,7 +233,7 @@ class AssetManager {
{ force = false }: { force?: boolean } = {}, { force = false }: { force?: boolean } = {},
): Promise<Nullable<AssetLike>> { ): Promise<Nullable<AssetLike>> {
if (!documentId || !asset?.id) { if (!documentId || !asset?.id) {
return Promise.resolve(asset ?? null); return Promise.resolve(asset);
} }
const baseAsset = this.assetCache.get(asset.id) || asset; const baseAsset = this.assetCache.get(asset.id) || asset;
@@ -258,7 +249,8 @@ class AssetManager {
return true; return true;
} }
} }
if (baseAsset.url && (!assetExpiresAt || assetExpiresAt > now)) { const assetUrl = resolveAssetUrl(baseAsset);
if (assetUrl && (!assetExpiresAt || assetExpiresAt > now)) {
return true; return true;
} }
return false; return false;
@@ -288,13 +280,13 @@ class AssetManager {
.then(({ data }) => { .then(({ data }) => {
const cachedEntry = this.assetCache.get(asset.id) || baseAsset; const cachedEntry = this.assetCache.get(asset.id) || baseAsset;
const combined = { ...cachedEntry, ...asset, ...data }; const combined = { ...cachedEntry, ...asset, ...data };
const expiresAt = const expires_at =
resolveAssetExpiresAt(data) resolveAssetExpiresAt(data)
?? resolveAssetExpiresAt(combined) ?? resolveAssetExpiresAt(combined);
?? Date.now() + this.assetPresignTtlMs;
const entry = { const entry = {
...combined, ...combined,
expiresAt, url: resolveAssetUrl(combined),
expires_at,
}; };
this.rememberAsset(entry); this.rememberAsset(entry);
+4 -4
View File
@@ -13,14 +13,14 @@ export type DocumentLike = OcrDocumentLike;
const asyncFalse = async () => false; const asyncFalse = async () => false;
const resolveDocumentDownloadHref = (document?: DocumentLike | null, resolveApiPath?: ResolveApiPath | null): string | null => { const resolveDocumentDownloadHref = (document?: DocumentLike | null, resolveApiPath?: ResolveApiPath | null): string | null => {
if (!document || !resolveApiPath) { if (!document) {
return null; return null;
} }
const downloadPath = (document.current_version as { download_path?: string | null } | null)?.download_path; const downloadUrl = (document.current_version as { download?: { url: string } | null } | null)?.download?.url;
if (!downloadPath) { if (!downloadUrl) {
return null; return null;
} }
return resolveApiPath(downloadPath); return resolveApiPath ? resolveApiPath(downloadUrl) : downloadUrl;
}; };
const hasDocumentOcrAsset = (document?: DocumentLike | null, getDocumentAsset?: GetDocumentAsset | null): boolean => { const hasDocumentOcrAsset = (document?: DocumentLike | null, getDocumentAsset?: GetDocumentAsset | null): boolean => {
@@ -450,7 +450,6 @@ const useDocumentsWorkspace = ({
documentsManager, documentsManager,
selectedFolder, selectedFolder,
api, api,
resolveApiPath,
notifyApiError, notifyApiError,
navigate, navigate,
locationPathname: location.pathname, locationPathname: location.pathname,
+4 -5
View File
@@ -38,7 +38,9 @@ interface DocumentLike {
correspondents?: Array<{ id?: string | number; name?: string }>; correspondents?: Array<{ id?: string | number; name?: string }>;
current_version?: { current_version?: {
version_number?: number; version_number?: number;
download?: { url?: string | null; expires_at?: number } | null;
version?: { content_type?: string | null } | null; version?: { content_type?: string | null } | null;
filename?: string | null;
} | null; } | null;
documentLink?: { documentLink?: {
url: string; url: string;
@@ -261,11 +263,8 @@ const DocumentViewerPanel: React.FC<DocumentViewerPanelProps> = ({
if (!document) { if (!document) {
return null; return null;
} }
const downloadPath = document.current_version?.download_path; const downloadUrl = document.current_version?.download?.url;
if (!downloadPath) { const href = resolveApiPath ? resolveApiPath(downloadUrl) : downloadUrl;
return null;
}
const href = resolveApiPath ? resolveApiPath(downloadPath) : downloadPath;
if (!href) { if (!href) {
return null; return null;
} }
+1 -5
View File
@@ -6,12 +6,8 @@ import type {
GetAsset as AssetManagerGetAsset, GetAsset as AssetManagerGetAsset,
} from '../asset_manager'; } from '../asset_manager';
interface DocumentVersion extends DocumentVersionLike {
download_path?: string | null;
}
export interface DocumentLike extends AssetManagerDocumentLike { export interface DocumentLike extends AssetManagerDocumentLike {
current_version?: DocumentVersion | null; current_version?: DocumentVersionLike | null;
} }
export type AssetLike = AssetManagerAssetLike; export type AssetLike = AssetManagerAssetLike;