-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmy-drive.html
More file actions
137 lines (134 loc) · 7.5 KB
/
my-drive.html
File metadata and controls
137 lines (134 loc) · 7.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
<link rel="stylesheet" href="./css/custom.css">
<title>MyDrive</title>
</head>
<body>
<header>
<h1>zack hoang</h1>
<p>project details</p>
<nav>
<a href="./index.html">Home</a>
<a href="./projects.html">Projects</a>
<a href="./resume/vietanhhoang_resume_iteration_9.pdf" target="_blank">Resume</a>
</nav>
</header>
<main>
<h2>MyDrive</h2>
<img src="./projects/file-uploader.png">
<section>
<h2>Scope</h2>
<div class="scope">
<div>
<h3>Timeline</h3>
<p>July 2025 - Aug 2025 (4 weeks)</p>
</div>
<div>
<h3>Role(s)</h3>
<ul>
<li>Backend Developer</li>
</ul>
</div>
</div>
</section>
<article>
<h2>Table Of Content</h2>
<ol>
<li><a href="#overview">Overview</a></li>
<li><a href="#tools">Tools</a></li>
<li><a href="#features">Features</a></li>
<li><a href="#challenges">Challenges</a></li>
<li><a href="#takeaway">Takeaway</a></li>
</ol>
</article>
<section id="overview">
<h2>Overview</h2>
<p>MyDrive is a cloud storage for your images. Written using ExpressJS and its middlewares ecosystem, it
contains most of the basic features you can expect from a file uploader: Authentication, CRUDable
folders and files upload, download and delete.</p>
</section>
<section id="tools">
<h2>Tools</h2>
<ul>
<li><a href="https://expressjs.com/" target="_blank">ExpressJS</a>: JavaScript backend framework</li>
<li><a href="https://ejs.co/" target="_blank">EJS</a>: genereate HTML with JavaScript</li>
<li><a href="https://express-validator.github.io/docs/" target="_blank">Express Validator</a>: sanitize
and validate user-provided input</li>
<li><a href="https://expressjs.com/en/resources/middleware/multer.html" target="_blank">Multer</a>:
handle processing uploaded files</li>
<li><a href="https://www.postgresql.org/" target="_blank">PostgreSQL</a>: database</li>
<li><a href="https://www.prisma.io/" target="_blank">Prisma</a>: ORM</li>
<li><a href="https://www.passportjs.org/" target="_blank">PassportJS</a>: NodeJS authentication
middleware</li>
<li><a href="https://www.passportjs.org/packages/passport-local/" target="_blank">PassportJS Local
Strategy</a>: authentication strategy with username and password</li>
<li><a href="https://cloudinary.com/" target="_blank">Cloudinary API</a>: image storage</li>
</ul>
</section>
<section id="features">
<h2>Features</h2>
<ul>
<li><b>Authentication</b>: with usernames and passwords</li>
<li><b>CRUDable folders</b>: Create, Read files, Update folders' name, and Delete folders. These are
records that lies inside database that gets queried based on its ID and its author</li>
<li><b>Upload files</b>: upload images and store a record of upload information in the database, while
image is stored in Cloudinary</li>
<li><b>View file's details</b>: file details can be viewed in a seperate route. Details include: file
name, upload time, file size</li>
</ul>
</section>
<section id="challenges">
<h2>Challenges</h2>
<ul>
<li>
<b>Setting Up Initial Schema</b>: setting up the database during the early stage took me the most
amount of time and also a lot of migrations to get to the final results. I ended up writting
relations that I did not need at all until I realized I could make the app work with just querying
scalar fields, with a small cost of more codes. However, I did not find this to be much of a problem
at all given this project's scope.
</li>
<br />
<li>
<b>Folder deletion</b>: deleting one folder means to also delete all of its descendants and files
that contains within itself and its descendants. One could solve this problem recursively by
deleting folders and files with the given ID of a parent. However, I settled for a simple Breadth
First Search approach since it made the most sense to me at the time. This is how it works:
<ol>
<li>Given a folder's ID, insert that ID into an array called <code>queue</code></li>
<li>Start the search, searching for folders that has its ID parent equal to the ID that we are
trying to search</li>
<li>If the search does not return any result, we know that folder does not have anymore
descendant. We mark them as visited by insert that ID into another array called
<code>visitedFoldersID</code>, dequeue, and search the next item in the queue</li>
<li>Once the queue is empty, meaning the search is done, <code>visitedFoldersID</code> should
now be an array of ID strings, which could be iterated through to look for folders to delete
on the database. For each iteration, we also delete all files that are contained in each
folder.</li>
</ol>
</li>
<br />
<li>
<b>File Type Checking</b>: I wanted to restrict users to only upload images. This is a small
security check I implemented so that Cloudinary's storage does not grow too quick, since we can
assume images files are quite small, ranging around a few kilobytes to megabytes and making sure
users does not upload any suspicious or malicious files. Initially, file checking was only limited
to checking its MIME type and extension, provided by the Multer middleware. While it seems
reasonable on the first glance, changing the extension will also change the MIME type, which renders
this solution ineffective. The solution then, is to check the magic number on the image's buffer
using the <a href="https://www.npmjs.com/package/file-type" target="_blank">file-type</a> package.
</li>
</ul>
</section>
<section id="takeaway">
<h2>Takeaway</h2>
<p>This has been my most challenging project so far and it has given me some ground on how to operate with
Prisma. On top of being fun, it has given me some insights on how a common cloud storage works on the
surface.</p>
</section>
</main>
</body>
</html>