diff --git a/README.md b/README.md index 299c42a..581148b 100644 --- a/README.md +++ b/README.md @@ -28,15 +28,15 @@ - [x] Add ownership to files - [x] Upload fiiles to the correct folder -- [ ] Delete files button +- [x] Delete files button - [x] Allow files thay arent images to be uploaded - [x] Display file type and file sizes correctly in the view -- [ ] Retrieve the file type from uploadthing and save it in db -- [ ] real homepage +- [x] Retrieve the file type from uploadthing and save it in db +- [x] real homepage ## notes 2:40.44 - [ ] Real homepage + onboarding ## followup -[ ] folder deletion - make sure to recursively delete all files and folders. \ No newline at end of file +[ ] folder deletion - make sure to recursively delete all files and folders diff --git a/src/app/(home)/drive/page.tsx b/src/app/(home)/drive/page.tsx new file mode 100644 index 0000000..928389f --- /dev/null +++ b/src/app/(home)/drive/page.tsx @@ -0,0 +1,52 @@ +import { auth } from "@clerk/nextjs/server"; +import { redirect } from "next/navigation"; +import { Button } from "~/components/ui/button"; +import { MUTATIONS, QUERIES } from "~/server/db/queries"; + +export default async function DrivePage() { + const session = await auth(); + if (!session.userId) { + return redirect("/sign-in"); + } + + const rootFolder = await QUERIES.getRootFolderForUser(session.userId); + + if (typeof rootFolder === "object" && "error" in rootFolder) { + // Handle error case, e.g., show a message to the user + console.error("Error fetching root folder:", rootFolder.error); + return ( + <> +
Error fetching root folder
+ + ); + } + + if (!rootFolder) { + // create the root folder for the user + return ( + <> +
{ + "use server"; + const session = await auth(); + if (!session.userId) { + return redirect("/sign-in"); + } + + const rootFolderId = await MUTATIONS.onboardUser(session.userId); + if (typeof rootFolderId === "object" && "error" in rootFolderId) { + // Handle error case, e.g., show a message to the user + console.error("Error creating root folder:", rootFolderId.error); + return; + } + return redirect(`/folder/${rootFolderId}`); + }} + > + +
+ + ); + } + + return redirect(`/folder/${rootFolder.id}`); +} diff --git a/src/app/(home)/layout.tsx b/src/app/(home)/layout.tsx new file mode 100644 index 0000000..e23ec91 --- /dev/null +++ b/src/app/(home)/layout.tsx @@ -0,0 +1,14 @@ +import { auth } from "@clerk/nextjs/server"; +import { redirect } from "next/navigation"; +import { Button } from "~/components/ui/button"; + +export default function HomeLayout(props: { children: React.ReactNode }) { + return ( +
+
{props.children}
+ +
+ ); +} diff --git a/src/app/(home)/page.tsx b/src/app/(home)/page.tsx index f0b3cdc..f470415 100644 --- a/src/app/(home)/page.tsx +++ b/src/app/(home)/page.tsx @@ -1,8 +1,37 @@ +import { auth } from "@clerk/nextjs/server"; +import { redirect } from "next/navigation"; +import { Button } from "~/components/ui/button"; +export default function HomePage() { + return ( + <> +

+ M8 Drive +

+

+ Secure, fast, and easy file storage for the modern web +

+
{ + "use server"; -export default function GoogleDriveClone() { - // const files = await db.select().from(fileSchema); - // const folders = await db.select().from(foldersSchema); - // return ; - return
Hello World
-} \ No newline at end of file + const session = await auth(); + + if (!session.userId) { + return redirect("/sign-in"); + } + + return redirect("/drive"); + }} + > + + + + ); +} diff --git a/src/app/(home)/sign-in/page.tsx b/src/app/(home)/sign-in/page.tsx new file mode 100644 index 0000000..b438fed --- /dev/null +++ b/src/app/(home)/sign-in/page.tsx @@ -0,0 +1,9 @@ +import { SignInButton } from "@clerk/nextjs"; + +export default function SignInPage() { + return ( + <> + + + ); +} diff --git a/src/server/db/queries.ts b/src/server/db/queries.ts index 8db74f9..b9894b3 100644 --- a/src/server/db/queries.ts +++ b/src/server/db/queries.ts @@ -1,56 +1,142 @@ -import 'server-only'; +import "server-only"; -import { eq } from "drizzle-orm"; +import { eq, and, isNull } from "drizzle-orm"; import { db } from "."; -import { DB_FileType, files_table as fileSchema, folders_table as foldersSchema } from "~/server/db/schema" +import { + DB_FileType, + files_table as fileSchema, + folders_table as foldersSchema, +} from "~/server/db/schema"; export const QUERIES = { - getAllParentsForFolder: async function (folderId: number) { - const parents = []; - let currentId: number | null = folderId; - while (currentId !== null && currentId !== 1) { - const folder = await db - .selectDistinct() - .from(foldersSchema) - .where(eq(foldersSchema.id, currentId)); - - if (!folder[0]) { - throw new Error(`Folder with ID ${currentId} not found`); - } - parents.unshift(folder[0]); - currentId = folder[0]?.parent ?? null; - } - return parents; - }, - - getFiles: async function (folderId: number) { - const filePromise = await db.select().from(fileSchema).where(eq(fileSchema.parent, folderId)).orderBy(fileSchema.createdAt); - return filePromise - }, - - getFolders: async function (folderId: number) { - const foldersPromise = await db.select().from(foldersSchema).where(eq(foldersSchema.parent, folderId)).orderBy(foldersSchema.createdAt); - return foldersPromise - }, - getFolderById: async function (folderId: number) { - const folder = await db.select().from(foldersSchema).where(eq(foldersSchema.id, folderId)); - if (!folder[0]) { - throw new Error(`Folder with ID ${folderId} not found`); - } - return folder[0]; + getAllParentsForFolder: async function (folderId: number) { + const parents = []; + let currentId: number | null = folderId; + while (currentId !== null && currentId !== 1) { + const folder = await db + .selectDistinct() + .from(foldersSchema) + .where(eq(foldersSchema.id, currentId)); + + if (!folder[0]) { + throw new Error(`Folder with ID ${currentId} not found`); + } + parents.unshift(folder[0]); + currentId = folder[0]?.parent ?? null; + } + return parents; + }, + + getFiles: async function (folderId: number) { + const filePromise = await db + .select() + .from(fileSchema) + .where(eq(fileSchema.parent, folderId)) + .orderBy(fileSchema.createdAt); + return filePromise; + }, + + getFolders: async function (folderId: number) { + const foldersPromise = await db + .select() + .from(foldersSchema) + .where(eq(foldersSchema.parent, folderId)) + .orderBy(foldersSchema.createdAt); + return foldersPromise; + }, + getFolderById: async function (folderId: number) { + const folder = await db + .select() + .from(foldersSchema) + .where(eq(foldersSchema.id, folderId)); + if (!folder[0]) { + throw new Error(`Folder with ID ${folderId} not found`); + } + return folder[0]; + }, + + getRootFolderForUser: async function (userId: string) { + // get the root folder for the user + if (!userId) { + return { error: "Unauthorized" }; } -} + const rootFolder = await db + .select() + .from(foldersSchema) + .where( + and(eq(foldersSchema.ownerId, userId), isNull(foldersSchema.parent)), + ); + return rootFolder[0]; + }, +}; export const MUTATIONS = { - createFile: async function (input : { - file: { - name: string; - size: number; - url: string; - parent: number; - ownerId: string; - }, userId: string}) { - if (!input.userId) throw new Error("Unauthorized"); - return await db.insert(fileSchema).values(input.file) - }, -} + createFile: async function (input: { + file: { + name: string; + size: number; + url: string; + parent: number; + ownerId: string; + }; + userId: string; + }) { + if (!input.userId) throw new Error("Unauthorized"); + return await db.insert(fileSchema).values(input.file); + }, + + createFolder: async function (input: { + name: string; + parentId: number | null; + userId: string; + }) { + const { name, parentId, userId } = input; + if (!userId) throw new Error("Unauthorized"); + return await db.insert(foldersSchema).values({ + name, + parent: parentId, + ownerId: userId, + }); + }, + + onboardUser: async function (userId: string) { + const isExistingUser = await db + .select() + .from(foldersSchema) + .where(eq(foldersSchema.ownerId, userId)) + .limit(1); + if (isExistingUser[0]) { + return { error: "User already exists" }; + } + const rootFolder = await db + .insert(foldersSchema) + .values({ + name: "Root", + parent: null, + ownerId: userId, + }) + .$returningId(); + + const rootFolderId = rootFolder[0]!.id; + + await db.insert(foldersSchema).values([ + { + name: "Shared", + parent: rootFolderId, + ownerId: userId, + }, + { + name: "Favourites", + parent: rootFolderId, + ownerId: userId, + }, + { + name: "Documents", + parent: rootFolderId, + ownerId: userId, + }, + ]); + + return rootFolderId; + }, +};