diff --git a/src/pages/browse/[userId].tsx b/src/pages/browse/[userId].tsx deleted file mode 100644 index fa2e2f8..0000000 --- a/src/pages/browse/[userId].tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { - GetServerSideProps, - GetStaticPaths, - GetStaticProps, - InferGetServerSidePropsType, - InferGetStaticPropsType, -} from 'next'; -import { createServerSideHelpers } from '@trpc/react-query/server'; -import { api } from '~/utils/api'; -import { formatDistanceToNow } from 'date-fns'; -import styled from '@emotion/styled'; -import Link from 'next/link'; - -const Container = styled('div')({ - maxWidth: '1280px', - margin: '0 auto', - padding: '1rem', -}); - -const Title = styled('h1')({ - fontSize: '1.5rem', - fontWeight: 'bold', - marginBottom: '1rem', -}); - -const Grid = styled('div')({ - display: 'grid', - gap: '1rem', - gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', - gridTemplate: 'masonry', -}); - -const FileCard = styled('div')({ - border: '1px solid #e5e7eb', - borderRadius: '0.5rem', - padding: '1rem', - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - alignItems: 'center', - height: '400px', - width: '150px', -}); - -const BrowsePage = ({ - userId, -}: InferGetStaticPropsType) => { - const { data: filesData } = api.s3.listUserFiles.useQuery( - { userId }, - { enabled: userId !== undefined } - ); - - return ( - - My Files - - {filesData?.files.map((file) => ( - - - file -

- {file.lastModified && - formatDistanceToNow(new Date(file.lastModified))}{' '} - ago -

-
- - ))} - {filesData?.files.length === 0 && ( -
No files found
- )} -
-
- ); -}; - -export const getStaticProps = (async ({ params }) => { - const userId = params!.userId as string; - - return { - props: { - userId, - }, - }; -}) satisfies GetStaticProps; - -export const getStaticPaths = (async () => { - return { - paths: [], - fallback: 'blocking', - }; -}) satisfies GetStaticPaths; - -export default BrowsePage; diff --git a/src/pages/browse/index.tsx b/src/pages/browse/index.tsx new file mode 100644 index 0000000..0682512 --- /dev/null +++ b/src/pages/browse/index.tsx @@ -0,0 +1,146 @@ +import type { + GetServerSideProps, + InferGetServerSidePropsType +} from 'next'; +import { useState, useEffect, useCallback } from 'react'; +import { api } from '~/utils/api'; +import { formatDistanceToNow } from 'date-fns'; +import styled from '@emotion/styled'; +import Link from 'next/link'; + +const Container = styled('div')({ + maxWidth: '1280px', + margin: '0 auto', + padding: '1rem', +}); + +const Title = styled('h1')({ + fontSize: '1.5rem', + fontWeight: 'bold', + marginBottom: '1rem', +}); + +const Grid = styled('div')({ + display: 'grid', + gap: '1rem', + gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', + gridTemplate: 'masonry', +}); + +const FileCard = styled('div')({ + border: '1px solid #e5e7eb', + borderRadius: '0.5rem', + padding: '1rem', + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + alignItems: 'center', + height: '400px', + width: '150px', +}); + +const BrowsePage = ({ + login, +}: InferGetServerSidePropsType) => { + const [token, setToken] = useState(null); + const utils = api.useContext(); + + const { data: currentUser } = api.account.getCurrentUser.useQuery(undefined, { + enabled: !!token, + }); + + const signInMutation = api.account.signIn.useMutation({ + onSuccess: (data) => { + setToken(data.token); + localStorage.setItem('authToken', data.token); + void utils.s3.listUserFiles.invalidate(); + }, + }); + + const { data: filesData } = api.s3.listUserFiles.useQuery( + undefined, + { enabled: !!token } + ); + + const handleLogin = useCallback(async () => { + const email = window.prompt('Email:'); + if (!email) return; + + const password = window.prompt('Password:'); + if (!password) return; + + void signInMutation.mutate({ email, password }); + }, [signInMutation]); + + // Check for stored token on mount + useEffect(() => { + const storedToken = localStorage.getItem('authToken'); + if (storedToken && !login) { + setToken(storedToken); + } else if (!signInMutation.isSuccess && !signInMutation.isPending && !signInMutation.error) { + void handleLogin(); + } + }, [currentUser, login, handleLogin, signInMutation]); + + if (signInMutation.error) { + return ( + + Access Denied +
+ Login failed. Please refresh to try again. +
+
+ ); + } + + if (!token || !currentUser) { + return ( + + Waiting for authentication... +
+ Please refresh this page if nothing happens. +
+
+ ); + } + + return ( + + My Files + + {filesData?.files.map((file) => ( + + + file +

+ {file.lastModified && + formatDistanceToNow(new Date(file.lastModified))}{' '} + ago +

+
+ + ))} + {filesData?.files.length === 0 && ( +
No files found
+ )} +
+
+ ); +}; + +export const getServerSideProps = (async ({ query }) => { + const login = query?.login ?? null; + + return { + props: { + login, + }, + }; +}) satisfies GetServerSideProps; + +export default BrowsePage; diff --git a/src/server/api/routers/s3.ts b/src/server/api/routers/s3.ts index 3eb8f73..d7428f2 100644 --- a/src/server/api/routers/s3.ts +++ b/src/server/api/routers/s3.ts @@ -6,10 +6,9 @@ import { env } from '~/env.js'; import { createTRPCRouter, protectedProcedure, - publicProcedure, } from '~/server/api/trpc'; export const s3Router = createTRPCRouter({ - listUserFiles: publicProcedure + listUserFiles: protectedProcedure .meta({ openapi: { method: 'GET', @@ -19,7 +18,7 @@ export const s3Router = createTRPCRouter({ description: 'Returns list of files uploaded by the current user', }, }) - .input(z.object({ userId: z.string() })) + .input(z.void()) .output( z.object({ files: z.array( @@ -33,10 +32,13 @@ export const s3Router = createTRPCRouter({ ), }) ) - .query(async ({ ctx, input: { userId } }) => { + .query(async ({ ctx }) => { + // Use the authenticated user's name from the session + const userName = ctx.session.user.name; + const response = await ctx.s3.listObjectsV2({ Bucket: env.BUCKET_NAME, - Prefix: `${userId}/`, // Only list objects that are in the user's folder + Prefix: `${userName}/`, // Only list objects that are in the user's folder }); return { diff --git a/src/utils/api.ts b/src/utils/api.ts index 0f03d30..7e40ebc 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -40,6 +40,11 @@ export const api = createTRPCNext({ */ transformer: superjson, url: `${getBaseUrl()}/api/trpc`, + headers() { + // Add auth token to all requests + const token = localStorage.getItem("authToken"); + return token ? { Authorization: `Bearer ${token}` } : {}; + }, }), ], };