Skip to content

Commit

Permalink
pdf api fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ibastawisi committed Mar 13, 2024
1 parent 1529df0 commit 869c1bb
Show file tree
Hide file tree
Showing 18 changed files with 159 additions and 186 deletions.
10 changes: 0 additions & 10 deletions src/app/(appLayout)/view/[id]/not-found.tsx

This file was deleted.

54 changes: 31 additions & 23 deletions src/app/(appLayout)/view/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { findUserDocument } from "@/repositories/document";
import ViewDocument from "@/components/ViewDocument";
import htmr from 'htmr';
import { Metadata } from "next";
import { notFound } from "next/navigation";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import SplashScreen from "@/components/SplashScreen";
import { cache } from "react";

const PUBLIC_URL = process.env.PUBLIC_URL;
const getCachedUserDocument = cache(async (id: string, revisions?: string) => await findUserDocument(id, revisions));
const getCachedSession = cache(async () => await getServerSession(authOptions));

Expand Down Expand Up @@ -56,29 +56,37 @@ export async function generateMetadata({ params, searchParams }: { params: { id:
}

export default async function Page({ params, searchParams }: { params: { id: string }, searchParams: { v?: string } }) {
const document = await getCachedUserDocument(params.id, "all");
if (!document) notFound();
const revisionId = searchParams.v ?? document.head;
const revision = document.revisions.find((revision) => revision.id === revisionId);
if (!revision) notFound();
document.updatedAt = revision.createdAt;
const session = await getCachedSession();
const isCollab = document.collab;
if (!session) {
if (document.private) return <SplashScreen title="This document is private" subtitle="Please sign in to view it" />
if (!isCollab) document.revisions = [revision];
}
const user = session?.user;
if (user) {
const isAuthor = user.id === document.author.id;
const isCoauthor = document.coauthors.some(coauthor => coauthor.id === user.id);
if (!isAuthor && !isCoauthor) {
if (document.private) return <SplashScreen title="This document is private" subtitle="You are not authorized to view this document" />
try {
const document = await getCachedUserDocument(params.id, "all");
if (!document) return <SplashScreen title="Document not found" />;
const revisionId = searchParams.v ?? document.head;
const revision = document.revisions.find((revision) => revision.id === revisionId);
if (!revision) return <SplashScreen title="Something went wrong" subtitle="Revision not found" />;
document.updatedAt = revision.createdAt;
const session = await getCachedSession();
const isCollab = document.collab;
if (!session) {
if (document.private) return <SplashScreen title="This document is private" subtitle="Please sign in to view it" />
if (!isCollab) document.revisions = [revision];
}
const user = session?.user;
if (user) {
const isAuthor = user.id === document.author.id;
const isCoauthor = document.coauthors.some(coauthor => coauthor.id === user.id);
if (!isAuthor && !isCoauthor) {
if (document.private) return <SplashScreen title="This document is private" subtitle="You are not authorized to view this document" />
if (!isCollab) document.revisions = [revision];
}
}
const response = await fetch(`${PUBLIC_URL}/api/embed/${revisionId}`);
if (!response.ok) {
const { error } = await response.json();
return <SplashScreen title={error.title} subtitle={error.subtitle} />;
}
const html = await response.text();
return <ViewDocument cloudDocument={document} user={session?.user}>{htmr(html)}</ViewDocument>;
} catch (error) {
console.error(error);
return <SplashScreen title="Something went wrong" subtitle="Please try again later" />;
}
const response = await fetch(`${process.env.PUBLIC_URL}/api/embed/${revisionId}`);
if (!response.ok) throw new Error('Failed to generate HTML');
const html = await response.text();
return <ViewDocument cloudDocument={document} user={session?.user}>{htmr(html)}</ViewDocument>;
}
2 changes: 1 addition & 1 deletion src/app/api/embed/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export async function GET(request: Request, { params }: { params: { id: string }
try {
const revision = await findRevisionById(params.id);
if (!revision) {
return NextResponse.json({ error: { title: "Not Found", subtitle: "Document not found" } }, { status: 404 })
return NextResponse.json({ error: { title: "Something went wrong", subtitle: "Revision not found" } }, { status: 404 })
}
const html = await generateHtml(revision.data);
return new Response(html, {
Expand Down
5 changes: 1 addition & 4 deletions src/app/api/pdf/[url]/route.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { NextResponse } from "next/server";
import puppeteer, { PDFOptions } from "puppeteer";

const PUBLIC_URL = process.env.PUBLIC_URL;

export async function GET(request: Request) {
try {
if (!PUBLIC_URL) return NextResponse.json({ error: { title: "Something went wrong", subtitle: "Please set up the environment variable PUBLIC_URL" }, }, { status: 500 });
const url = new URL(request.url);
const search = url.searchParams;
const handle = url.pathname.split("/").pop();
url.hostname = PUBLIC_URL;
if (url.hostname === 'localhost') url.protocol = 'http:'
url.pathname = `/embed/${handle}`;
const browser = await puppeteer.launch();
const page = await browser.newPage()
Expand Down
10 changes: 0 additions & 10 deletions src/app/embed/[id]/not-found.tsx

This file was deleted.

28 changes: 19 additions & 9 deletions src/app/embed/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { OgMetadata } from "@/app/api/og/route";
import htmr from 'htmr';
import EmbedDocument from "@/components/EmbedDocument";
import { notFound } from 'next/navigation'
import { findUserDocument } from '@/repositories/document';
import SplashScreen from '@/components/SplashScreen';
import { cache } from 'react';
import type { Metadata } from "next";
import { validate } from "uuid";

const PUBLIC_URL = process.env.PUBLIC_URL;
const getCachedUserDocument = cache(async (id: string, revisions?: string) => await findUserDocument(id, revisions));

export async function generateMetadata({ params, searchParams }: { params: { id: string }, searchParams: { v?: string } }): Promise<Metadata> {
Expand Down Expand Up @@ -42,12 +43,21 @@ export async function generateMetadata({ params, searchParams }: { params: { id:
}

export default async function Page({ params, searchParams }: { params: { id: string }, searchParams: { v?: string } }) {
const document = await getCachedUserDocument(params.id);
if (!document) notFound();
if (document.private) return <SplashScreen title="This document is private" />;
const revision = searchParams.v ?? document.head
const response = await fetch(`${process.env.PUBLIC_URL}/api/embed/${revision}`);
if (!response.ok) throw new Error('Failed to generate HTML');
const html = await response.text();
return <EmbedDocument>{htmr(html)}</EmbedDocument>
try {
const document = await getCachedUserDocument(params.id);
if (!document) return <SplashScreen title="Document not found" />;
if (document.private) return <SplashScreen title="This document is private" />;
const revision = searchParams.v ?? document.head;
if (!validate(revision)) return <SplashScreen title="Revision not found" />;
const response = await fetch(`${PUBLIC_URL}/api/embed/${revision}`);
if (!response.ok) {
const { error } = await response.json();
return <SplashScreen title={error.title} subtitle={error.subtitle} />;
}
const html = await response.text();
return <EmbedDocument>{htmr(html)}</EmbedDocument>
} catch (error) {
console.error(error);
return <SplashScreen title="Something went wrong" subtitle="Please try again later" />;
}
}
30 changes: 16 additions & 14 deletions src/app/pdf/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
import { GetCloudDocumentResponse } from "@/types";
import { NextResponse } from "next/server";
import { findUserDocument } from "@/repositories/document";

const PUBLIC_URL = process.env.PUBLIC_URL;
const PDF_WORKER_URL = process.env.PDF_WORKER_URL;

export async function GET(request: Request) {
try {
if (!PUBLIC_URL) return NextResponse.json({ error: { title: "Something went wrong", subtitle: "Please set up the environment variable PUBLIC_URL" }, }, { status: 500 });
const url = new URL(request.url);
const search = url.searchParams;
const handle = url.pathname.split("/").pop();
const revision = search.get('v');

const metadata = await fetch(`${PUBLIC_URL}/api/documents/${handle}/metadata`);
const { data, error } = await metadata.json() as GetCloudDocumentResponse;
if (error || !data || data.private) return fetch(request.url.replace('/pdf', '/embed'));
const name = data.name;
if (!revision) url.searchParams.set('v', data?.head || "");
if (PDF_WORKER_URL) url.hostname = PDF_WORKER_URL;
if (!handle) throw new Error("No handle provided");
const document = await findUserDocument(handle, revision);
if (!document || document.private) throw new Error("Document not found");
if (!revision) url.searchParams.set('v', document.head);
if (PDF_WORKER_URL) { url.hostname = PDF_WORKER_URL; url.port = ''; }
else url.pathname = `/api/pdf/${handle}`;
if (url.hostname === 'localhost') url.protocol = 'http:';
const response = await fetch(url.toString(), { cache: 'force-cache' });
response.headers.set("Content-Disposition", `inline; filename="${name}.pdf"`);
if (!response.ok) throw new Error("Couldn't generate PDF");
response.headers.set("Content-Disposition", `inline; filename="${encodeURIComponent(document.name)}.pdf"`);
return response;
} catch (error) {
console.log(error);
return NextResponse.json({ error: { title: "Something went wrong", subtitle: "Please try again later" } }, { status: 500 });
console.error(error);
const url = new URL(request.url);
if (url.hostname === 'localhost') url.protocol = 'http:';
url.pathname = url.pathname.replace('/pdf', '/embed');
const response = await fetch(url.toString());
const html = await response.text();
return new Response(html, { status: response.status, headers: { "Content-Type": "text/html" } });
}
}

7 changes: 5 additions & 2 deletions src/components/Ads/DisplayAd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,27 @@
import useOnlineStatus from '@/hooks/useOnlineStatus';
import { Card } from '@mui/material';
import { SxProps, Theme } from '@mui/material/styles';
import { usePathname } from 'next/navigation';
import { memo, useEffect } from 'react';

const PUBLISHER_ID = process.env.NEXT_PUBLIC_PUBLISHER_ID;

const DisplayAd: React.FC<{ sx?: SxProps<Theme> }> = memo(({ sx }) => {
const pathname = usePathname();
const isOnline = useOnlineStatus();

useEffect(() => {
try {
// @ts-ignore
(window.adsbygoogle = window.adsbygoogle || []).push({});
} catch (err) {
console.log(err);
}
}, []);
}, [pathname, isOnline]);

if (!PUBLISHER_ID || !isOnline) return null;

return <Card variant="outlined" sx={{ display: "flex", displayPrint: "none", flexDirection: "column", justifyContent: "space-between", my: 2, height: "auto", maxWidth: "100%", ...sx }}>
return <Card key={pathname} variant="outlined" sx={{ display: "flex", displayPrint: "none", flexDirection: "column", justifyContent: "space-between", height: "auto", maxWidth: "100%", ...sx }}>
<ins
className="adsbygoogle"
style={{ display: "block" }}
Expand Down
10 changes: 3 additions & 7 deletions src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,20 @@
import { useSelector } from '@/store';
import UserCard from "./UserCard";
import { Box, CircularProgress, Grid, Paper, Typography } from "@mui/material";
import dynamic from "next/dynamic";
import { useEffect, useState } from 'react';
import { PieChart } from '@mui/x-charts/PieChart';
import { CloudDocument, LocalDocument } from '@/types';
import { Cloud, Login, Storage } from '@mui/icons-material';

const DisplayAd = dynamic(() => import('@/components/Ads/DisplayAd'), { ssr: false });

const Dashboard: React.FC = () => {
const user = useSelector(state => state.user);

return <>
return (
<Box sx={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2 }}>
<UserCard user={user} sessionUser={user} />
<StorageChart />
</Box>
<DisplayAd />
</>
);
}

export default Dashboard;
Expand All @@ -44,7 +40,7 @@ const StorageChart: React.FC = () => {
usageDetails: {} as Record<string, number>,
}
});

const localStorageEmpty = initialized && !(storageUsage.localStorage.usage || storageUsage.indexedDB.usage);
const cloudStorageEmpty = initialized && !(storageUsage.cloud.usage);
const isLoading = !initialized || (localDocuments.length && !storageUsage.localStorage.usage) || (cloudDocuments.length && !storageUsage.cloud.usage);
Expand Down
2 changes: 0 additions & 2 deletions src/components/EditDocument.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { EditorState, LexicalEditor } from "@/editor";
import { v4 as uuidv4 } from 'uuid';
import dynamic from "next/dynamic";

const DisplayAd = dynamic(() => import('@/components/Ads/DisplayAd'), { ssr: false });
const EditDocumentInfo = dynamic(() => import('@/components/EditDocumentInfo'), { ssr: false });

const EditDocument: React.FC = () => {
Expand Down Expand Up @@ -61,7 +60,6 @@ const EditDocument: React.FC = () => {
<Helmet title={`${document.name} | Math Editor`} />
<Editor document={document} editorRef={editorRef} onChange={handleChange} />
<EditDocumentInfo documentId={document.id} editorRef={editorRef} />
<DisplayAd />
</>;
}

Expand Down
3 changes: 0 additions & 3 deletions src/components/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import { useEffect } from "react";
import useLocalStorage from "../hooks/useLocalStorage";
import Documents from "./Documents";
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material';
import dynamic from "next/dynamic";

const DisplayAd = dynamic(() => import('@/components/Ads/DisplayAd'), { ssr: false });

const Home: React.FC = () => {
const [welcomed, setWelcomed] = useLocalStorage("welcomed", false);
Expand All @@ -19,7 +17,6 @@ const Home: React.FC = () => {
return (
<>
<Documents />
<DisplayAd />
{!welcomed && (
<Dialog open onClose={handleClose}>
<DialogTitle>Welcome to Math Editor</DialogTitle>
Expand Down
4 changes: 4 additions & 0 deletions src/components/Layout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ import ProgressBar from "./ProgressBar";
import Footer from "./Footer";
import { Container } from "@mui/material";
import { Suspense } from "react";
import dynamic from "next/dynamic";

const DisplayAd = dynamic(() => import('@/components/Ads/DisplayAd'), { ssr: false });

const AppLayout = ({ children }: { children: React.ReactNode; }) => {
return (
<StoreProvider>
<TopAppBar />
<Suspense><ProgressBar /></Suspense>
<Container className='editor-container'>{children}</Container>
<Container sx={{ mb: 2 }}><DisplayAd /></Container>
<Footer />
<AlertDialog />
<Announcer />
Expand Down
Loading

0 comments on commit 869c1bb

Please sign in to comment.