fix(client): 🐛 do not allow private resumes to be viewable or downloadable through the link

This commit is contained in:
Amruth Pillai
2023-07-12 15:59:22 +02:00
parent 5ef4bfcb6b
commit 1c2d796c50
121 changed files with 3193 additions and 2068 deletions

View File

@ -42,14 +42,10 @@ const Build: NextPage<Props> = ({ username, slug }) => {
`resume/${username}/${slug}`,
() => fetchResumeByIdentifier({ username, slug }),
{
cacheTime: 0,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
onSuccess: (resume) => {
dispatch(setResume(resume));
},
}
},
);
useEffect(() => {
@ -62,7 +58,7 @@ const Build: NextPage<Props> = ({ username, slug }) => {
<div className={styles.container}>
<Head>
<title>
{resume.name} | {t<string>('common.title')}
{resume.name} | {t('common.title')}
</title>
</Head>

View File

@ -57,6 +57,16 @@ const Preview: NextPage<Props> = ({ username, slug, resume: initialData }) => {
useEffect(() => {
if (initialData && !isEmpty(initialData)) {
const errorObj = JSON.parse(JSON.stringify(initialData));
const statusCode: number | null = get(errorObj, 'statusCode', null);
if (statusCode === 404) {
toast.error('The resume you were looking for does not exist, or maybe it never did?');
router.push('/');
return;
}
dispatch(setResume(initialData));
}
}, [dispatch, initialData]);
@ -73,10 +83,6 @@ const Preview: NextPage<Props> = ({ username, slug, resume: initialData }) => {
useQuery<Resume>(`resume/${username}/${slug}`, () => fetchResumeByIdentifier({ username, slug }), {
initialData,
retry: false,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
onSuccess: (data) => {
dispatch(setResume(data));
},

View File

@ -1,4 +1,10 @@
import '@/styles/globals.scss';
import '@fontsource/material-icons';
import '@fontsource/ibm-plex-sans/300.css';
import '@fontsource/ibm-plex-sans/400.css';
import '@fontsource/ibm-plex-sans/500.css';
import '@fontsource/ibm-plex-sans/600.css';
import '@fontsource/ibm-plex-sans/700.css';
import env from '@beam-australia/react-env';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';

View File

@ -23,7 +23,7 @@ const Document: NextPage = () => (
"@context": "https://schema.org",
"@type": "Organization",
"url": "https://rxresu.me",
"logo": "https://rxresu.me/images/logos/logo.svg"
"logo": "https://rxresu.me/logo/dark.svg"
}`,
}}
/>

View File

@ -40,7 +40,7 @@ const Dashboard: NextPage = () => {
<div className={styles.container}>
<Head>
<title>
{t<string>('dashboard.title')} | {t<string>('common.title')}
{t('dashboard.title')} | {t('common.title')}
</title>
</Head>
@ -56,15 +56,15 @@ const Dashboard: NextPage = () => {
<ResumeCard
icon={Add}
modal="dashboard.create-resume"
title={t<string>('dashboard.create-resume.title')}
subtitle={t<string>('dashboard.create-resume.subtitle')}
title={t('dashboard.create-resume.title')}
subtitle={t('dashboard.create-resume.subtitle')}
/>
<ResumeCard
icon={ImportExport}
modal="dashboard.import-external"
title={t<string>('dashboard.import-external.title')}
subtitle={t<string>('dashboard.import-external.subtitle')}
title={t('dashboard.import-external.title')}
subtitle={t('dashboard.import-external.subtitle')}
/>
{data.map((resume) => (

68
client/pages/home.tsx Normal file
View File

@ -0,0 +1,68 @@
import type { GetStaticProps, NextPage } from 'next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import Tilt from 'react-parallax-tilt';
import HeroBackground from '@/components/home/Background';
import Footer from '@/components/home/Footer';
import Header from '@/components/home/Header';
import HeroPattern from '@/components/home/Pattern';
import LogoSection from '@/components/home/sections/Logo';
import StatsSection from '@/components/home/sections/Stats';
import { defaultTiltProps } from '@/constants/tilt';
export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => ({
props: {
...(await serverSideTranslations(locale, ['common', 'modals', 'landing'])),
},
});
const Home: NextPage = () => (
<div>
<Header />
<main className="relative isolate mb-[450px] overflow-hidden bg-zinc-50 dark:bg-zinc-950">
<section className="relative">
<HeroPattern />
<HeroBackground />
<div className="mx-auto max-w-7xl px-6 pb-24 pt-10 sm:pb-32 lg:flex lg:px-8 lg:py-52">
<div className="mx-auto max-w-2xl shrink-0 lg:mx-0 lg:max-w-xl lg:pt-12">
<div className="mt-10 space-y-2">
<h6 className="text-base font-bold tracking-wide">Finally,</h6>
<h1 className="text-4xl font-bold !leading-[1.15] tracking-tight sm:text-6xl">
A free and open-source resume builder
</h1>
</div>
<p className="prose prose-base prose-zinc mt-6 text-lg leading-8 dark:prose-invert">
Reactive Resume is a free and open-source resume builder that simplifies the tasks of creating, updating,
and sharing your resume.
</p>
</div>
<div className="mx-auto mt-16 flex max-w-2xl sm:mt-24 lg:ml-10 lg:mr-0 lg:mt-0 lg:max-w-none lg:flex-none xl:ml-32">
<div className="max-w-3xl flex-none sm:max-w-5xl lg:max-w-none">
<Tilt {...defaultTiltProps}>
<img
width={2432}
height={1442}
src="/images/screenshots/builder.png"
alt="Reactive Resume Screenshot - Builder Screen"
className="w-[76rem] rounded-lg bg-zinc-50/5 shadow-2xl ring-1 ring-zinc-950/10 dark:bg-zinc-950/5 dark:ring-zinc-50/10"
/>
</Tilt>
</div>
</div>
</div>
</section>
<LogoSection />
<StatsSection />
</main>
<Footer />
</div>
);
export default Home;

View File

@ -7,7 +7,7 @@ import Link from 'next/link';
import { Trans, useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import Testimony from '@/components/landing/Testimony';
import Testimony from '@/components/home/Testimony';
import Footer from '@/components/shared/Footer';
import LanguageSwitcher from '@/components/shared/LanguageSwitcher';
import Logo from '@/components/shared/Logo';
@ -49,28 +49,28 @@ const Home: NextPage = () => {
</div>
<div className={styles.main}>
<h1>{t<string>('common.title')}</h1>
<h1>{t('common.title')}</h1>
<h2>{t<string>('common.subtitle')}</h2>
<h2>{t('common.subtitle')}</h2>
<NoSsr>
<div className={styles.buttonWrapper}>
{isLoggedIn ? (
<>
<Link href="/dashboard" passHref>
<Button>{t<string>('landing.actions.app')}</Button>
<Button>{t('landing.actions.app')}</Button>
</Link>
<Button variant="outlined" onClick={handleLogout}>
{t<string>('landing.actions.logout')}
{t('landing.actions.logout')}
</Button>
</>
) : (
<>
<Button onClick={handleLogin}>{t<string>('landing.actions.login')}</Button>
<Button onClick={handleLogin}>{t('landing.actions.login')}</Button>
<Button variant="outlined" onClick={handleRegister} disabled={FLAG_DISABLE_SIGNUPS}>
{t<string>('landing.actions.register')}
{t('landing.actions.register')}
</Button>
</>
)}
@ -80,21 +80,21 @@ const Home: NextPage = () => {
</div>
<section className={styles.section}>
<h6>{t<string>('landing.summary.heading')}</h6>
<h6>{t('landing.summary.heading')}</h6>
<p>{t<string>('landing.summary.body')}</p>
<p>{t('landing.summary.body')}</p>
</section>
<section className={styles.section}>
<h6>{t<string>('landing.features.heading')}</h6>
<h6>{t('landing.features.heading')}</h6>
<ul className="list-inside list-disc leading-loose">
<li>{t<string>('landing.features.list.free')}</li>
<li>{t<string>('landing.features.list.ads')}</li>
<li>{t<string>('landing.features.list.tracking')}</li>
<li>{t<string>('landing.features.list.languages')}</li>
<li>{t<string>('landing.features.list.import')}</li>
<li>{t<string>('landing.features.list.export')}</li>
<li>{t('landing.features.list.free')}</li>
<li>{t('landing.features.list.ads')}</li>
<li>{t('landing.features.list.tracking')}</li>
<li>{t('landing.features.list.languages')}</li>
<li>{t('landing.features.list.import')}</li>
<li>{t('landing.features.list.export')}</li>
<li>
<Trans t={t} i18nKey="landing.features.list.more">
And a lot of exciting features,
@ -107,7 +107,7 @@ const Home: NextPage = () => {
</section>
<section className={styles.section}>
<h6>{t<string>('landing.screenshots.heading')}</h6>
<h6>{t('landing.screenshots.heading')}</h6>
<div className={styles.screenshots}>
{screenshots.map(({ src, alt }) => (
@ -125,7 +125,7 @@ const Home: NextPage = () => {
</section>
<section className={styles.section}>
<h6>{t<string>('landing.testimonials.heading')}</h6>
<h6>{t('landing.testimonials.heading')}</h6>
<p className="my-3">
<Trans t={t} i18nKey="landing.testimonials.body">
@ -150,42 +150,42 @@ const Home: NextPage = () => {
</section>
<section className={styles.section}>
<h6>{t<string>('landing.links.heading')}</h6>
<h6>{t('landing.links.heading')}</h6>
<div>
<Link href="/meta/privacy" passHref>
<Button variant="text" startIcon={<LinkIcon />}>
{t<string>('landing.links.links.privacy')}
{t('landing.links.links.privacy')}
</Button>
</Link>
<Link href="/meta/service" passHref>
<Button variant="text" startIcon={<LinkIcon />}>
{t<string>('landing.links.links.service')}
{t('landing.links.links.service')}
</Button>
</Link>
<a href={GITHUB_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<LinkIcon />}>
{t<string>('landing.links.links.github')}
{t('landing.links.links.github')}
</Button>
</a>
<a href={DOCS_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<LinkIcon />}>
{t<string>('landing.links.links.docs')}
{t('landing.links.links.docs')}
</Button>
</a>
<a href={REDDIT_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<LinkIcon />}>
{t<string>('landing.links.links.reddit')}
{t('landing.links.links.reddit')}
</Button>
</a>
<a href={DONATION_URL} target="_blank" rel="noreferrer">
<Button variant="text" startIcon={<LinkIcon />}>
{t<string>('landing.links.links.donate')}
{t('landing.links.links.donate')}
</Button>
</a>
</div>

View File

@ -41,9 +41,6 @@ const Preview: NextPage<Props> = ({ shortId }) => {
const dispatch = useAppDispatch();
const { data: resume } = useQuery<Resume>(`resume/${shortId}`, () => fetchResumeByShortId({ shortId }), {
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
onSuccess: (data) => {
dispatch(setResume(data));
},