Skip to content

Commit

Permalink
Improve performance and best practices
Browse files Browse the repository at this point in the history
The changes are mostly based on improving the Lighthouse score:
* Cache JavaScript assets
* Longer caching of thumbnails
* Change images to .webp format
* Implement stricter Content-Security-Policy
* Improve accessibility
* Serve robots.txt
* Improve quality of thumbnails
* Use small thumbnails everywhere
  • Loading branch information
NeKzor committed May 15, 2024
1 parent b07934f commit 0ed71c5
Show file tree
Hide file tree
Showing 19 changed files with 311 additions and 65 deletions.
1 change: 1 addition & 0 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"id": "docker exec -ti autorender-server deno task id",
"board": "docker exec -ti autorender-server deno task board",
"processing": "docker exec -ti autorender-server deno task processing",
"optimize": "docker exec -ti autorender-server deno task optimize",
"migrate": "docker exec -ti autorender-server deno task migrate",
"build": "docker compose build",
"up": "docker compose up",
Expand Down
16 changes: 11 additions & 5 deletions src/server/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ const metaNames: (keyof RouteMeta)[] = [
const getCSP = (nonce: string) => {
return [
`default-src 'self';`,
`script-src 'nonce-${nonce}' cdnjs.cloudflare.com;`,
`style-src 'nonce-${nonce}' cdnjs.cloudflare.com https://fonts.googleapis.com;`,
`script-src 'nonce-${nonce}' 'strict-dynamic' 'unsafe-inline' https:;`,
`style-src 'nonce-${nonce}';`,
`font-src 'self' https://fonts.gstatic.com;`,
`media-src 'self' blob: *.backblazeb2.com;`,
`img-src 'self' data: cdn.discordapp.com *.backblazeb2.com;`,
`object-src 'none';`,
`base-uri 'none';`,
].join(' ');
};

Expand Down Expand Up @@ -70,21 +72,25 @@ export const Head = ({ initialState }: HeadProps) => {
return <meta name={name} content={meta[name]} />;
})}
<title>{title}</title>
<meta name='description' content='Render Portal 2 demos on-demand!' />
<link rel='icon' type='image/x-icon' href='/favicon.ico' />
<link
rel='stylesheet'
href='https://fonts.googleapis.com/css?family=Roboto:300,400,500'
href='https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap'
nonce={state.nonce}
/>
<link
rel='stylesheet'
href='https://cdnjs.cloudflare.com/ajax/libs/flowbite/1.8.0/flowbite.min.css'
integrity='sha384-SU26Q8fNMYupAr9UoLFL3sKttAwvXrmP7SdUWaw146+7I1kWXTlg5gA6X1Z70FKS'
crossOrigin='anonymous'
nonce={state.nonce}
/>
<script
src='https://cdnjs.cloudflare.com/ajax/libs/flowbite/1.8.0/flowbite.min.js'
integrity='sha384-SXh3DHBSUxvOFk7+R9qN3hv+DtgPJz4vQwOArU6zxWGnvtR1sy+XmzKUkNh2qWST'
crossOrigin='anonymous'
nonce={state.nonce}
defer
/>
</>
Expand All @@ -99,9 +105,9 @@ export const Body = ({ initialState, children }: BodyProps) => {
<AppDispatchContext.Provider value={dispatch}>
<div className={tw`flex flex-col h-screen`}>
<Navbar />
<div className={tw`mt-[88px] m-4 grow`}>
<main className={tw`mt-[88px] m-4 grow`}>
{children}
</div>
</main>
<Footer />
</div>
</AppDispatchContext.Provider>
Expand Down
Binary file removed src/server/app/assets/images/404.jpg
Binary file not shown.
Binary file added src/server/app/assets/images/404.webp
Binary file not shown.
Binary file not shown.
Binary file added src/server/app/assets/images/autorender_logo.webp
Binary file not shown.
Binary file removed src/server/app/assets/images/logo.png
Binary file not shown.
Binary file not shown.
3 changes: 1 addition & 2 deletions src/server/app/assets/js/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,6 @@ const initRerenderModal = () => {
disableSkipCoopVideos: rerenderModalSkipCoopCheckbox?.checked ?? false,
}),
})
.catch(console.error)
.finally(() => location.replace(location.href));
});
};
Expand Down Expand Up @@ -366,7 +365,7 @@ if (location.pathname.startsWith('/videos/') && location.pathname.length === 19)
}
});

fetch(`/api/v1${location.pathname}/views`, { method: 'POST' }).catch(console.error);
fetch(`/api/v1${location.pathname}/views`, { method: 'POST' });

initShareModal();
}
Expand Down
30 changes: 26 additions & 4 deletions src/server/app/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,26 @@ const Navbar = () => {
>
<div className={tw`max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4`}>
<div id='nav-items-left' className={tw`flex items-center`}>
<a href='/'>
<img height='30px' width='30px' src='/assets/images/logo.png' />
<a href='/' aria-label='Go to home'>
<img
src='/assets/images/autorender_logo.webp'
alt='Autorender logo'
height='30px'
width='30px'
/>
</a>
<a href='/' className={tw`self-center pl-1 pt-1 text-2xl font-semibold whitespace-nowrap dark:text-white`}>
<a
href='/'
aria-label='Go to home'
className={tw`self-center pl-1 pt-1 text-2xl font-semibold whitespace-nowrap dark:text-white`}
>
Autorender
</a>
</div>
<div id='nav-search-items' className={tw`flex md:order-2`}>
<button
id='nav-back-button'
aria-label='Cancel search'
type='button'
aria-expanded='false'
className={tw`hidden text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 mr-4`}
Expand Down Expand Up @@ -77,6 +87,7 @@ const Navbar = () => {
/>
<button
id='nav-search-input-clear-button'
aria-label='Clear search'
type='button'
className={tw`${searchValue ? ' ' : 'hidden'}absolute inset-y-0 right-0 flex items-center px-3`}
>
Expand Down Expand Up @@ -123,6 +134,7 @@ const Navbar = () => {
</button>
<button
id='theme-toggle-button'
aria-label='Toggle theme'
type='button'
className={tw`text-gray-500 inline-flex items-center justify-center dark:text-gray-400 hover:bg-gray-100 w-10 h-10 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 mr-4`}
data-tooltip-target='theme-toggle-button-tooltip'
Expand Down Expand Up @@ -161,6 +173,7 @@ const Navbar = () => {
<>
<button
id='user-menu-button'
aria-label='Open user menu'
type='button'
className={tw`flex mr-3 text-sm bg-gray-800 rounded-full md:mr-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600`}
aria-expanded='false'
Expand All @@ -187,6 +200,7 @@ const Navbar = () => {
<li>
<a
href={`/profile/${state?.user.username}`}
aria-label='Go to profile'
className={tw`block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white`}
>
Profile
Expand All @@ -197,6 +211,7 @@ const Navbar = () => {
<li>
<a
href='/tokens'
aria-label='Go to tokens'
className={tw`block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white`}
>
Tokens
Expand All @@ -206,6 +221,7 @@ const Navbar = () => {
<li>
<a
href='/logout'
aria-label='Logout'
className={tw`block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white`}
>
Sign out
Expand All @@ -217,9 +233,15 @@ const Navbar = () => {
)
: (
<>
<a id='login-button' href={state?.discordAuthorizeLink} tabIndex={-1}>
<a
id='login-button'
aria-label='Login with Discord'
href={state?.discordAuthorizeLink}
tabIndex={-1}
>
<button
className={tw`text-gray-500 inline-flex items-center justify-center dark:text-gray-400 hover:bg-gray-100 w-10 h-10 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 mr-4`}
aria-label='Login'
type='button'
data-ripple-light='true'
data-tooltip-target='login-button-tooltip'
Expand Down
14 changes: 9 additions & 5 deletions src/server/app/components/VideoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,23 @@ export const VideoCard = ({ video }: { video: VideoCardData }) => {
<a href={`/videos/${video.share_id}`}>
<div
className={tw`relative flex items-center justify-center h-48 mb-4${
video.thumbnail_url_large ? '' : ' bg-gray-300 dark:bg-gray-700 rounded-[12px]'
video.thumbnail_url_small ? '' : ' bg-gray-300 dark:bg-gray-700 rounded-[12px]'
}`}
>
{video.thumbnail_url_large
{video.thumbnail_url_small
? (
<>
<img
className={tw`transition-transform duration-300 transform object-cover w-full h-full rounded-[12px]`}
src={video.thumbnail_url_large}
src={video.thumbnail_url_small}
alt='Thumbnail of video'
/>
{video.video_length !== null && <VideoLength videoLength={video.video_length} />}
{video.video_preview_url && (
<img
className={tw`absolute top-0 left-0 opacity-0 transition-opacity duration-300 transform hover:opacity-100 object-cover w-full h-full rounded-[12px]`}
src={video.video_preview_url}
alt='Preview of video'
/>
)}
</>
Expand All @@ -76,14 +78,16 @@ export const VideoCard = ({ video }: { video: VideoCardData }) => {
? (
<img
className={tw`w-10 h-10 text-gray-200 dark:text-gray-700 rounded-full`}
src={video.requested_by_discord_avatar_url!}
src={video.requested_by_discord_avatar_url}
alt='Avatar of user'
/>
)
: video.board_changelog_id
? (
<img
className={tw`w-10 h-10 text-gray-200 dark:text-gray-700 rounded-full`}
src='/assets/images/portal2boards_avatar.jpg'
src='/assets/images/autorender_avatar.webp'
alt='Avatar of autorender'
/>
)
: (
Expand Down
99 changes: 61 additions & 38 deletions src/server/app/components/VideoRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,51 @@ type VideoRowData =
| 'requested_by_name'
| 'requested_by_id'
| 'video_preview_url'
| 'thumbnail_url_large'
| 'thumbnail_url_small'
| 'video_length'
| 'board_changelog_id'
>
& {
requested_by_discord_avatar_url: string | null;
};

const RequestedBy = ({ video }: { video: VideoRowData }) => {
return (
<>
{video.requested_by_discord_avatar_url
? (
<img
className={tw`w-10 h-10 text-gray-200 dark:text-gray-700 rounded-full`}
src={video.requested_by_discord_avatar_url}
alt='Avatar of user'
/>
)
: video.board_changelog_id
? (
<img
className={tw`w-10 h-10 text-gray-200 dark:text-gray-700 rounded-full`}
src='/assets/images/autorender_avatar.webp'
alt='Avatar of autorender'
/>
)
: (
<svg
className={tw`w-10 h-10 text-gray-200 dark:text-gray-700`}
aria-hidden='true'
xmlns='http://www.w3.org/2000/svg'
fill='currentColor'
viewBox='0 0 20 20'
>
<path d='M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z' />
</svg>
)}
<div className={tw`pl-2`}>
{video.requested_by_name ?? 'Autorender'}
</div>
</>
);
};

export const VideoRow = ({ video }: { video: VideoRowData }) => {
return (
<div
Expand All @@ -37,24 +74,26 @@ export const VideoRow = ({ video }: { video: VideoRowData }) => {
<div className={tw`rounded-xl shadow-sm overflow-hidden`}>
<div className={tw`sm:flex`}>
<div className={tw`sm:shrink-0`}>
<a href={`/videos/${video.share_id}`}>
<a href={`/videos/${video.share_id}`} aria-label='Go to video'>
<div
className={tw`relative flex items-center justify-center h-48 min-w-[390px]${
video.thumbnail_url_large ? '' : ' bg-gray-300 dark:bg-gray-700 rounded-[12px]'
video.thumbnail_url_small ? '' : ' bg-gray-300 dark:bg-gray-700 rounded-[12px]'
}`}
>
{video.thumbnail_url_large
{video.thumbnail_url_small
? (
<>
<img
className={tw`transition-transform duration-300 transform object-cover w-full h-full rounded-[12px]`}
src={video.thumbnail_url_large}
src={video.thumbnail_url_small}
alt='Thumbnail of video'
/>
{video.video_length !== null && <VideoLength videoLength={video.video_length} />}
{video.video_preview_url && (
<img
className={tw`absolute top-0 left-0 opacity-0 transition-opacity duration-300 transform hover:opacity-100 object-cover w-full h-full rounded-[12px]`}
src={video.video_preview_url}
alt='Preview of video'
/>
)}
</>
Expand Down Expand Up @@ -92,39 +131,22 @@ export const VideoRow = ({ video }: { video: VideoRowData }) => {
</a>
</div>
<div className={tw`flex pt-2`}>
<a
className={tw`flex items-center`}
href={video.requested_by_name ? `/profile/${video.requested_by_name}` : undefined}
>
{video.requested_by_discord_avatar_url
? (
<img
className={tw`w-10 h-10 text-gray-200 dark:text-gray-700 rounded-full`}
src={video.requested_by_discord_avatar_url!}
/>
)
: video.board_changelog_id
? (
<img
className={tw`w-10 h-10 text-gray-200 dark:text-gray-700 rounded-full`}
src='/assets/images/portal2boards_avatar.jpg'
/>
)
: (
<svg
className={tw`w-10 h-10 text-gray-200 dark:text-gray-700`}
aria-hidden='true'
xmlns='http://www.w3.org/2000/svg'
fill='currentColor'
viewBox='0 0 20 20'
>
<path d='M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z' />
</svg>
)}
<div className={tw`pl-2`}>
{video.requested_by_name ?? 'Autorender'}
</div>
</a>
{video.requested_by_name
? (
<a
className={tw`flex items-center`}
href={`/profile/${video.requested_by_name}`}
>
<RequestedBy video={video} />
</a>
)
: (
<div
className={tw`flex items-center`}
>
<RequestedBy video={video} />
</div>
)}
</div>
{video.comment && (
<a href={`/videos/${video.share_id}`}>
Expand All @@ -138,6 +160,7 @@ export const VideoRow = ({ video }: { video: VideoRowData }) => {
<div className={tw`flex items-center justify-center`}>
<button
id={`video-menu-button-${video.share_id}`}
aria-label='Open video menu'
data-dropdown-toggle={`video-menu-dropdown-${video.share_id}`}
type='button'
className={tw`mx-2 hover:text-white focus:outline-none font-medium rounded-full text-sm p-2.5 text-center inline-flex items-center dark:border-gray-500 dark:text-gray-500 dark:hover:text-white dark:focus:ring-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700`}
Expand Down
10 changes: 8 additions & 2 deletions src/server/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import { getStyleTag, virtualSheet } from 'twind/sheets';
import { Body, Head } from './App.tsx';
import { AppState } from './AppState.ts';

// NOTE: Always bump this version before deployment when module.js changed.
const jsModuleVersion = '1.0.0';

const isHotReloadEnabled = Deno.env.get('HOT_RELOAD')!.toLowerCase() === 'true';

const sheet = virtualSheet();

setup({
Expand Down Expand Up @@ -59,9 +64,10 @@ export const index = (
const themeScriptTag =
`<script nonce="${nonce}">localStorage.getItem('color-theme')==='light'&&document.documentElement.classList.remove('dark')</script>`;

const moduleScriptTag = `<script nonce="${nonce}" src="/assets/js/module.js" type="module"></script>`;
const moduleScriptTag =
`<script nonce="${nonce}" src="/assets/js/module.js?v=${jsModuleVersion}" type="module"></script>`;

const hotReloadScriptTag = Deno.env.get('HOT_RELOAD')!.toLowerCase() === 'true'
const hotReloadScriptTag = isHotReloadEnabled
? `<script nonce="${nonce}" src="/assets/js/hot_reload.js" type="module"></script>`
: '';

Expand Down
Loading

0 comments on commit 0ed71c5

Please sign in to comment.