Skip to content

Commit 442d632

Browse files
authored
Modeling Files in Database (#9)
1 parent f767226 commit 442d632

File tree

8 files changed

+56
-16
lines changed

8 files changed

+56
-16
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ The database and UI are now connected, some improvements to make:
112112
Uploading a file to '[uploadthing](https://uploadthing.com/)' works, things that
113113
can be approved:
114114

115-
- [ ] Upload files to the right folder
115+
- [x] Add "ownership" to files and folders
116+
- [x] Upload files to the right folder
116117
- [ ] Delete file button
117-
- [ ] Allow files that are not images to be uploaded
118+
- [x] Allow files that are not images to be uploaded

src/app/api/uploadthing/core.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
1+
/* eslint-disable @typescript-eslint/only-throw-error */
2+
13
import { auth } from "@clerk/nextjs/server";
24
import { createUploadthing, type FileRouter } from "uploadthing/next";
35
import { UploadThingError } from "uploadthing/server";
6+
import { z } from "zod";
47

58
import * as mutations from "~/server/db/mutations";
9+
import * as queries from "~/server/db/queries";
610

711
const f = createUploadthing();
812

913
export const ourFileRouter = {
10-
imageUploader: f({ image: { maxFileSize: "4MB", maxFileCount: 1 } })
11-
.middleware(async () => {
14+
driveUploader: f({ blob: { maxFileSize: "4MB", maxFileCount: 1 } })
15+
.input(z.object({ folderId: z.number() }))
16+
.middleware(async ({ input }) => {
1217
const user = await auth();
13-
14-
// eslint-disable-next-line @typescript-eslint/only-throw-error
1518
if (!user.userId) throw new UploadThingError("Unauthorized");
1619

17-
return { userId: user.userId };
20+
const folder = await queries.getFolderById(input.folderId);
21+
if (!folder) throw new UploadThingError("Folder not found");
22+
23+
if (folder.ownerId !== user.userId)
24+
throw new UploadThingError("Unauthorized");
25+
26+
return { userId: user.userId, parent: folder.id };
1827
})
1928
.onUploadComplete(async ({ metadata, file }) => {
2029
console.log("Upload complete for userId:", metadata.userId);
2130
console.log("file url", file.ufsUrl);
2231

23-
const uploadedFile = { ...file, parent: 0 };
32+
const uploadedFile = { ...file, parent: metadata.parent };
2433
await mutations.createFile(uploadedFile, metadata.userId);
2534

2635
return { uploadedBy: metadata.userId };

src/app/drive-contents.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export default function DriveContents(props: {
1313
files: File[];
1414
folders: Folder[];
1515
parents: Folder[];
16+
currentFolderId: number;
1617
}) {
1718
const navigate = useRouter();
1819

@@ -64,7 +65,8 @@ export default function DriveContents(props: {
6465
</ul>
6566
</div>
6667
<UploadButton
67-
endpoint="imageUploader"
68+
endpoint="driveUploader"
69+
input={{ folderId: props.currentFolderId }}
6870
onClientUploadComplete={() => {
6971
navigate.refresh();
7072
}}

src/app/f/[folderId]/page.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,12 @@ export default async function GoogleDriveClone(props: {
2020
queries.getAllParentsForFolder(data.folderId),
2121
]);
2222

23-
return <DriveContents folders={folders} files={files} parents={parents} />;
23+
return (
24+
<DriveContents
25+
folders={folders}
26+
files={files}
27+
parents={parents}
28+
currentFolderId={data.folderId}
29+
/>
30+
);
2431
}

src/lib/mock-data.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export type Folder = {
1616

1717
// prettier-ignore
1818
export const mockFolders: Folder[] = [
19-
{ id: "root", name: "root", type: "folder", parent: null }, // the root folder
2019
{ id: "1", name: "Documents", type: "folder", parent: "root" },
2120
{ id: "2", name: "Images", type: "folder", parent: "root" },
2221
{ id: "3", name: "Work", type: "folder", parent: "root" },

src/server/db/mutations.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { db } from "~/server/db";
22
import { type File, files_table as filesSchema } from "~/server/db/schema";
33

4-
export function createFile(file: Omit<File, "id" | "parent">, _userId: string) {
5-
return db.insert(filesSchema).values({ ...file, parent: 1 });
4+
export function createFile(
5+
file: Pick<File, "name" | "size" | "url" | "parent">,
6+
userId: string,
7+
) {
8+
return db.insert(filesSchema).values({ ...file, ownerId: userId });
69
}

src/server/db/queries.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ export function getAllFolders(folderId: number) {
3333
.where(eq(folderSchema.parent, folderId));
3434
}
3535

36+
export async function getFolderById(folderId: number) {
37+
const folders = await db
38+
.select()
39+
.from(folderSchema)
40+
.where(eq(folderSchema.id, folderId));
41+
return folders[0];
42+
}
43+
3644
export function getAllFiles(folderId: number) {
3745
return db.select().from(fileSchema).where(eq(fileSchema.parent, folderId));
3846
}

src/server/db/schema.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
text,
55
singlestoreTableCreator,
66
bigint,
7+
timestamp,
78
} from "drizzle-orm/singlestore-core";
89

910
const createTable = singlestoreTableCreator((name) => `drive_tutorial_${name}`);
@@ -14,12 +15,17 @@ export const files_table = createTable(
1415
id: bigint("id", { mode: "number", unsigned: true })
1516
.primaryKey()
1617
.autoincrement(),
18+
ownerId: text("owner_id").notNull(),
1719
name: text("name").notNull(),
1820
url: text("url").notNull(),
1921
size: int("size").notNull(),
20-
parent: bigint("parent", { mode: "number", unsigned: true }),
22+
parent: bigint("parent", { mode: "number", unsigned: true }).notNull(),
23+
createdAt: timestamp("created_at").notNull().defaultNow(),
2124
},
22-
(table) => [index("parent_index").on(table.parent)],
25+
(table) => [
26+
index("owner_id_index").on(table.ownerId),
27+
index("parent_index").on(table.parent),
28+
],
2329
);
2430

2531
export const folders_table = createTable(
@@ -28,10 +34,15 @@ export const folders_table = createTable(
2834
id: bigint("id", { mode: "number", unsigned: true })
2935
.primaryKey()
3036
.autoincrement(),
37+
ownerId: text("owner_id").notNull(),
3138
name: text("name").notNull(),
3239
parent: bigint("parent", { mode: "number", unsigned: true }),
40+
createdAt: timestamp("created_at").notNull().defaultNow(),
3341
},
34-
(table) => [index("parent_index").on(table.parent)],
42+
(table) => [
43+
index("owner_id_index").on(table.ownerId),
44+
index("parent_index").on(table.parent),
45+
],
3546
);
3647

3748
export type File = typeof files_table.$inferSelect;

0 commit comments

Comments
 (0)