Files
paperlessmanager/paperless-frontend/src/utils/auth-resource.tsx
T
2026-05-10 22:30:27 +02:00

108 lines
2.8 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Spin } from 'antd';
import type { CSSProperties } from 'react';
import { getAccessToken } from '../auth/oidc';
export function useAuthUrl(url: string | null | undefined): string | null {
const [blobUrl, setBlobUrl] = useState<string | null>(null);
useEffect(() => {
if (!url) {
setBlobUrl(null);
return;
}
let cancelled = false;
let objectUrl: string | undefined;
(async () => {
try {
const token = await getAccessToken();
if (cancelled) return;
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
if (cancelled || !res.ok) return;
const blob = await res.blob();
if (cancelled) return;
objectUrl = URL.createObjectURL(blob);
setBlobUrl(objectUrl);
} catch {
// image/resource just won't show
}
})();
return () => {
cancelled = true;
setBlobUrl(null);
if (objectUrl) URL.revokeObjectURL(objectUrl);
};
}, [url]);
return blobUrl;
}
interface AuthImageProps {
src: string;
width?: number | string;
height?: number | string;
style?: CSSProperties;
alt?: string;
}
export function AuthImage({ src, width, height, style, alt = '' }: AuthImageProps) {
const blobUrl = useAuthUrl(src);
if (!blobUrl) {
return (
<div
style={{
width,
height: height ?? width,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#f5f5f5',
borderRadius: style?.borderRadius ?? 0,
}}
>
<Spin size="small" />
</div>
);
}
return <img src={blobUrl} width={width} height={height} style={style} alt={alt} />;
}
interface AuthIframeProps {
src: string;
style?: CSSProperties;
title?: string;
}
export function AuthIframe({ src, style, title }: AuthIframeProps) {
const hashIdx = src.indexOf('#');
const cleanUrl = hashIdx >= 0 ? src.slice(0, hashIdx) : src;
const hash = hashIdx >= 0 ? src.slice(hashIdx) : '';
const blobUrl = useAuthUrl(cleanUrl);
if (!blobUrl) {
return (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', ...style }}>
<Spin />
</div>
);
}
return <iframe src={`${blobUrl}${hash}`} style={{ border: 'none', ...style }} title={title} />;
}
export async function openAuthUrl(url: string): Promise<void> {
try {
const token = await getAccessToken();
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
if (!res.ok) return;
const blob = await res.blob();
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, '_blank');
setTimeout(() => URL.revokeObjectURL(blobUrl), 60_000);
} catch {
// silently fail
}
}