1
Fork 0

Fix public url getter & update tests

This commit is contained in:
Immanuel Martini 2024-09-05 20:09:20 +02:00
parent 78c76a50ad
commit da25451f75
5 changed files with 78 additions and 60 deletions

View File

@ -1,19 +0,0 @@
import { render, screen } from "@testing-library/react"
import { ProtectedImage } from "./ProtectedMedia"
const mockPublicUrl = 'http://localhost:9876';
vi.mock('../../helpers/utils', () => ({getPublicUrl: () => mockPublicUrl}));
describe('ProtectedImage', () => {
test.each([
{label: 'relative', src: '/image.jpg', expected: `${mockPublicUrl}/image.jpg`},
{label: 'absolute', src: 'http://localhost:4040/image.jpg', expected: 'http://localhost:4040/image.jpg'}
])('loads image correctly given $label url', ({src, expected}) => {
render(<ProtectedImage alt={'alt_text'} src={src}/>);
const image = screen.getByAltText('alt_text');
expect(image).toHaveAttribute('src', expected);
})
})

View File

@ -4,26 +4,12 @@ import { useRef } from 'react'
import { useState } from 'react'
import { useEffect } from 'react'
import { BlurhashCanvas } from 'react-blurhash'
import { getPublicUrl, isNil } from '../../helpers/utils'
import { getProtectedUrl, isNil } from '../../helpers/utils'
const isNativeLazyLoadSupported = 'loading' in document.createElement('img')
const placeholder =
'data:image/gif;base64,R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
const getProtectedUrl = (url?: string) => {
if (url == undefined) return undefined
const imgUrl = new URL(url, getPublicUrl())
const tokenRegex = location.pathname.match(/^\/share\/([\d\w]+)(\/?.*)$/)
if (tokenRegex) {
const token = tokenRegex[1]
imgUrl.searchParams.set('token', token)
}
return imgUrl.href
}
export interface ProtectedImageProps
extends Omit<
DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>,

View File

@ -1,7 +1,6 @@
import { gql, useLazyQuery } from '@apollo/client'
import { useTranslation } from 'react-i18next'
import { NotificationType } from '../../__generated__/globalTypes'
import { authToken } from '../../helpers/authentication'
import { TranslationFn } from '../../localization'
import { MessageState } from '../messages/Messages'
import { MediaSidebarMedia } from './MediaSidebar/MediaSidebar'
@ -13,7 +12,7 @@ import {
sidebarDownloadQueryVariables,
sidebarDownloadQuery_media_downloads,
} from './__generated__/sidebarDownloadQuery'
import { getPublicUrl } from '../../helpers/utils'
import { getProtectedUrl } from '../../helpers/utils'
export const SIDEBAR_DOWNLOAD_QUERY = gql`
query sidebarDownloadQuery($mediaId: ID!) {
@ -57,17 +56,9 @@ const formatBytes = (t: TranslationFn) => (bytes: number) => {
}
const downloadMedia = (t: TranslationFn) => async (url: string) => {
const imgUrl = new URL(url, getPublicUrl())
const imgUrl = getProtectedUrl(url);
if (authToken() == null) {
// Get share token if not authorized
const token = location.pathname.match(/^\/share\/([\d\w]+)(\/?.*)$/)
if (token) {
imgUrl.searchParams.set('token', token[1])
}
}
const response = await fetch(imgUrl.href, {
const response = await fetch(imgUrl, {
credentials: 'include',
})

View File

@ -1,19 +1,55 @@
import { getPublicUrl } from "./utils";
import { getProtectedUrl, getPublicUrl } from "./utils";
describe('getPublicUrl', () => {
describe('Url helper', () => {
const mockOrigin = 'http://localhost:9876';
Object.defineProperty(window, 'location', {
get() {
return { origin: mockOrigin };
}
const mockBaseUrl = '/base_url/';
const mockShareToken = 'nm42';
const mockSharePath = `/share/${mockShareToken}`;
beforeEach(()=>{
import.meta.env.BASE_URL = mockBaseUrl;
Object.defineProperties(window,
{
location: {
get() {
return { origin: mockOrigin, pathname: mockSharePath };
}
},
document: {
get() {
return { cookie: 'auth-token=abc' };
}
}
}
);
});
test.each([
{label:'relative', baseUrl: '/my_public/', expected: `${mockOrigin}/my_public/`},
{label:'absolute', baseUrl: 'http://my_origin', expected: 'http://my_origin/'}
])('returns currect url for $label base', ({baseUrl, expected}) => {
{url:'', baseUrl: '', expected: `${mockOrigin}/`},
{url:'', baseUrl: '/public_path/', expected: `${mockOrigin}/public_path/`},
{url:'/image.jpg', baseUrl: '/public_path/', expected: `${mockOrigin}/public_path/image.jpg`},
{url:'/image.jpg', baseUrl: 'http://other_host/', expected: 'http://other_host/image.jpg'},
{url:'http://other_host2/image.jpg', baseUrl: 'http://other_host/', expected: 'http://other_host2/image.jpg'}
])('returns currect public url for base $baseUrl and url $url', ({url, baseUrl, expected}) => {
import.meta.env.BASE_URL = baseUrl;
const url = getPublicUrl();
expect(url.href).toBe(expected);
const publicUrl = getPublicUrl(url);
expect(publicUrl.href).toBe(expected);
})
})
test('returns undefined protected url', () => {
expect(getProtectedUrl(undefined)).toBeUndefined();
})
test('returns protected url without token', () => {
expect(getProtectedUrl('image.jpg')).toBe(`${mockOrigin}${mockBaseUrl}image.jpg`);
})
test('returns protected url with token', () => {
Object.defineProperty(window, 'document', {
get() {
return { cookie: '' };
}
});
expect(getProtectedUrl('image.jpg')).toBe(`${mockOrigin}${mockBaseUrl}image.jpg?token=${mockShareToken}`);
})
})

View File

@ -1,5 +1,6 @@
import classNames, { Argument as ClassNamesArg } from 'classnames'
import { overrideTailwindClasses } from 'tailwind-override'
import { authToken } from './authentication'
export interface DebouncedFn<F extends (...args: unknown[]) => unknown> {
(...args: Parameters<F>): void
@ -49,6 +50,29 @@ export function tailwindClassNames(...args: ClassNamesArg[]) {
// return classNames(args)
}
export function getPublicUrl() {
return new URL(import.meta.env.BASE_URL, location.origin);
export function getPublicUrl(url: string = '') {
try {
try {
return new URL(url);
} catch {
return new URL(url, import.meta.env.BASE_URL);
}
} catch {
return new URL(`${import.meta.env.BASE_URL}${url}`.replace(/\/\//g, '/'), location.origin);
}
}
export function getProtectedUrl<S extends string | undefined>(url: S) {
if (url == undefined) return undefined as S
const publicUrl = getPublicUrl(url);
if (authToken() == null) {
const tokenRegex = location.pathname.match(/^\/share\/([\d\w]+)(\/?.*)$/)
if (tokenRegex) {
publicUrl.searchParams.set('token', tokenRegex[1])
}
}
return publicUrl.href
}