-
Notifications
You must be signed in to change notification settings - Fork 0
/
projectTwo.html
440 lines (414 loc) · 23.9 KB
/
projectTwo.html
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
<!DOCTYPE html>
<html lang="en" style="overflow: visible">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<title>GameShare</title>
<link rel="stylesheet" href="everything.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Special+Elite&display=swap"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Rubik+Glitch&display=swap"/>
</head>
<body data-barba="wrapper">
<main data-barba="container" data-barba-namespace="home">
<div class="transition"></div>
<nav class="project-nav">
<a href="index.html">
<div class="logo-main">
<div class="gas co">
<span class="number">27</span>
<h2 class="symbol">Jf</h2>
<p class="name">58.933</p>
</div>
</div>
</a>
<div class="burger-menu">
<div class="burger-line"></div>
<div class="burger-line"></div>
<div class="burger-line"></div>
</div>
<ul class="dropdown">
<li class="dropdown-li" style="background-color: wheat;"><a href="projectOne.html" >Project 1</a></li>
<li class="dropdown-li" style="background-color: gold;"><a href="projectTwo.html">Project 2</a></li>
<li class="dropdown-li" style="background-color: tomato;"><a href="projectThree.html">Project 3</a></li>
<li class="dropdown-li" style="background-color: wheat;"><a href="projectFour.html">Project 4</a></li>
</ul>
</nav>
<div class="wrapper">
<section class="project-header">
<h1 class="project-title">
PROJECT 2: <span style="color: green">GameShare™</span>
</h1>
<h3 class="project-goal">Objective: Build a full-stack CRUD app.</h3>
<h3 class="project-tools">Tools: PostgreSQL, Express, EJS, bcrypt</h3>
<div class="overview-image">
<img src="images/gameshare.png" alt="" />
</div>
<img class="header-bg" src="images/brain2.jpeg" alt="" />
</section>
<div class="parallaxParent darken ralof">
<div
style="
background-image: url(images/project2/ralof.png);
"
></div>
</div>
<section class="parallax-section" style="border: 10px darkgreen; border-style: ridge none;">
<div class="parallax-content">
<div></div>
<h1>Loading <span style="color: green">GameShare™</span>... </h1>
<p>
For this project, we had to make a CRUD app, which stands for
<strong>CREATE-READ-UPDATE-DESTROY</strong>, referring to the
actions you can take with a database. In short, this meant making
a website that gave users the ability to do all four actions to
the data in a connected database. Being a <em>gamer</em>, I
decided to make something related to games. After some thinking,
<strong style="color: green">GameShare™</strong> was brought
into existence: a website where people could share what games they
played and find other people who share their interests. Kinda like
a gaming rip-off of Tinder<sup>®</sup>.
</p>
<p>
With my idea set in stone, I now had to make a database for all
the information I needed to store. PostgreSQL was the tool that we
were given for this purpose. It’s a relational database management
system, which just means we just stuff all our data into tables
with columns of attributes and rows and rows of data all following
the same structure.
</p>
<p>For my app, I decided that I needed 3 tables:</p>
<ul>
<li><strong>users:</strong> for user information,</li>
<li><strong>games:</strong> for game information,</li>
<li>
and <strong>usergamelist:</strong> for each user's list of games
</li>
</ul>
<pre class="theme-atom-one-dark"><code class="hljs-code language-sql">
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL,
email TEXT NOT NULL,
password_hashed TEXT NOT NULL,
profile_image TEXT,
about_me TEXT
);
CREATE TABLE games (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
description TEXT,
image_url TEXT,
player_count INTEGER
);
CREATE TABLE usergamelist (
id SERIAL PRIMARY KEY,
user_id INTEGER,
game_id INTEGER,
remark TEXT,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
FOREIGN KEY (game_id) REFERENCES games (id) ON DELETE CASCADE
);
</code></pre>
<span class="subtitle"
>FOREIGN KEY indicates that the key is derived from a key in
another table. If said key is deleted, then anything referencing
it will get deleted too.</span
>
</div>
</section>
<div class="parallaxParent">
<div style="background-image: url(images/project2/users3.png)"></div>
</div>
<section class="parallax-section" style="border: 10px darkgreen; border-style: ridge none;">
<div class="parallax-content">
<h1>Working with Databases</h1>
<p>
With the database ready, I now had to seed it with dummy data.
Using JavaScript, I connected to the database and made a request
to add new data to it. This is an example of the C(reating) part
of CRUD.
</p>
<pre
class="theme-atom-one-dark"
><code class="hljs-code language-javascript">
const db = require('./index.js')
const bcrypt = require('bcrypt')
const saltRounds = 10
const username = 'CobaltDingus'
const dummyEmail = '[email protected]'
const plainTextPassword = 'gaming'
const sql = `
INSERT INTO users (username, email, password_hashed) VALUES ($1, $2, $3);
`
function createSingleUser() {
const name = username
const email = dummyEmail
const password = plainTextPassword
bcrypt.genSalt(saltRounds, (err, salt) => {
bcrypt.hash(password, salt, function(err, hash) {
db.query(sql, [name, email, hash], (err, result) => {
if (err) console.log(err);
})
});
});
}
createSingleUser()
</code></pre>
<span class="subtitle"
>Surely putting my real email and password for
<span class="hacked"
>a quick demo will be fine, right?</span
></span
>
<div>
<p>|</p>
<br />
</div>
<div class="hackerman">
<p>
-> Hey, it's your local Hackerman here to explain
<strong style="color: red">bcrypt</strong>. This is a module
containing functions to secure user passwords and make my job
harder >:(
</p>
<p>
-> The way it works is by first generating
<em style="color: aquamarine">salt</em>, a string of random
numbers that will be attached to your password. The
<em style="color: aquamarine">salted password</em> is then run
through a hashing algorithm to turn it into an abomination of
random characters that I will have to spend years trying to
reverse engineer.
</p>
<p>
-> This <em style="color: blue">hashed password</em> is what is
sent to the database along with the user's name and email. The
trick then is that a password passed through all the same
<strong style="color: red">bcrypt</strong> functions will always
return the same <em style="color: blue">hashed password</em>.
This is how your plain text password is checked upon logging
into the website.
</p>
<p>
-> And that's it! Hackerman out!
<span style="text-decoration: line-through"
>Just ignore the fact that I stole all your credit card info
while you were reading this.</span
>
</p>
</div>
<div>
<br />
<p>|</p>
</div>
<p>
...alright, I'm back. My service provider said they had to cut me
off because I didn't pay my bill or something? Anyway, back to
coding. In the pile of code above, there were 3 steps to
communicating with PostgreSQL:
</p>
<pre class="theme-atom-one-dark"><code class="hljs-code language-javascript">
const db = require('./index.js')
const sql = `INSERT INTO users (username, email, password_hashed) VALUES ($1, $2, $3);`
db.query(sql, [name, email, hash], (err, result) => {
if (err) console.log(err);
})
</code></pre>
<p>
1. Connecting to the database. The index file contained all the
necessary code to import the database as an object to execute
functions.
</p>
<p>
2. Coming up with instructions. SQL statements are, by design,
meant to be somewhat understandable to non-coders. This one
basically says "I want to insert some data in the users table,
under these three columns, using three specific values for each
respectively (marked as placeholders for now)."
</p>
<p>
3. Making the actual query. Using the <code>.query</code> method
on the <code>db</code> variable, the <code>sql</code> statement is
sent the database, along with all the other info that's meant to
fill in the placeholders (i.e. the user's name, email, and hashed
password that was acquired earlier).
</p>
<div>-</div>
<p>
I then repeated this process with a loop to seed dummy data for 10
users to simulate people caring about and using my
website. With all the backend set up, I was ready to start with the
actual website.
</p>
</div>
</section>
<div class="parallaxParent">
<div style="background-image: url(images/project2/gshare_live.png)"></div>
</div>
<section class="parallax-section" style="border: 10px darkgreen; border-style: ridge none;">
<div class="parallax-content">
<h1>Coding <span style="color: green">GameShare™</span></h1>
<p>
For the website’s features, I started with the essential before considering anything else. These would be the features that carried out the necessary CRUD actions as per our objective.
To do anything with my database however, I needed 2 new tools we had picked up: <strong>Express and EJS (and Express-EJS-Layouts to integrate them)</strong>.
</p>
<pre class="theme-atom-one-dark"><code class="hljs-code language-javascript">
router.get('/games/:id', (req, res) => {
const gameId = req.params.id
const sqlGameDetails = `SELECT * FROM games WHERE id = $1;`
db.query(sqlGameDetails, [gameId], (err, result) => {
if (err) console.log(err);
const game = result.rows[0]
const sqlGameUsers = `
SELECT *
FROM usergamelist
JOIN users
ON (usergamelist.user_id = users.id)
WHERE game_id = $1;
`
db.query(sqlGameUsers, [gameId], (err, result) => {
if (err) console.log(err);
const gameUsers = result.rows
res.render('show', { game: game, gameUsers: gameUsers })
})
})
})
</code></pre>
<p>
<strong>Express</strong> handled all the server side stuff like routing.
Requests from the client would go through here if they visited any URL with a pattern like <code>gameshare.com/games/:id</code>.
The ":id" in this case could be any anything. Whatever it is, it would be taken and put into an SQL statement to see if a game of that ID existed already in the database and if so, retrieve whatever data is being asked for.
As you might see, I actually have two statements: one to get the game details and one to get a list of the users who play said game.
Only after all this data is retrieved does the router render a web page for the user and for that, we now turn to <strong>EJS</strong>.
</p>
<p>
<strong>EJS</strong> essentially allows you to run JavaScript inside a HTML file. That alone is nice but with <strong>Express-EJS-Layouts</strong>, you can make templates for web pages filled with dynamically generated content.
</p>
<pre class="theme-atom-one-dark"><code class="hljs-code language-html">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GameShare</title>
<link rel="stylesheet" href="/style.css">
<body>
<header id="top">
<%- include('_header') %>
</header>
<div class="wrapper">
<main>
<%- body %>
</main>
</div>
</body>
</html>
</code></pre>
<p>This is the base layout for every page in <span style="color: green">GameShare™</span> meaning that it applies for every page no matter what content the page actually has.
You can see this base layout also includes a header (which is a custom layout for just the header of the page) and a content body which depends on what page layout I want to render. This for example is all I need to write for each game page:
</p>
<pre class="theme-atom-one-dark"><code class="hljs-code language-html">
<h1 class="header"><%= game.title %></h1>
<img class="game-image" src="<%= game.image_url %>" alt="Image of game">
<div class="paragraph">
<p>Description: <%= game.description %></p>
</div>
<section>
<div class="user-games">
<h2 class="tagline">Users</h2>
<table>
<tr>
<th></th>
<th>Username</th>
<th>Remarks</th>
</tr>
<% for (let gameUser of gameUsers) {%>
<tr>
<td><img src="<%= gameUser.profile_image %>" alt="Profile picture"></td>
<td><a class="player" href="/users/<%= gameUser.user_id %>"><%= gameUser.username %></a></td>
<td><span class="remark"><%= gameUser.remark %></span></td>
</tr>
<% } %>
</table>
</div>
</section>
</code></pre>
<span class="subtitle">Now isn't this nice and clean?</span>
<p>
All the <code><% %></code>, or the 'crab hands' as we called them in class, signify embedded JavaScript (which is what EJS stands for).
The code creates and fills new HTML elements with data from JavaScript variables. If you need a reminder of where the data came from, it was through the <strong>Express</strong> router from earlier:
</p>
<pre class="theme-atom-one-dark"><code class="hljs-code language-javascript">
db.query(sqlGameUsers, [gameId], (err, result) => {
if (err) console.log(err);
const gameUsers = result.rows
res.render('show', { game: game, gameUsers: gameUsers })
})
</code></pre>
<span class="subtitle">'show' is the name of the games template and 'game' and 'gameUsers' are the data retrieved from the database via the query(s).</span>
<p>
With <strong>Express</strong> and <strong>EJS</strong> together, I could do all my CRUD functions:
<ul>
<li>C(reating) was fulfilled through forms that allowed users to sign up and create an account. Additionally, users can also create entries in the database by adding new games to their list of games.</li>
<li>R(eading) was achieved any time data was displayed on a page.</li>
<li>U(pdating) was fulfilled by allowing users to update their remarks for games as well as their general profile information.</li>
<li>D(estroying) was fulfilled by allowing users to remove entries from their game lists.</li>
</ul>
</p>
<p>
And that covers most of the important parts of the code for <span style="color: green">GameShare™</span>... wait a sec <span class="hacked">I'm dropping out again...</span>
</p>
<div>
<p>|</p>
<br />
</div>
<div class="hackerman">
<p>
-> Hey, it's your local Hackerman again. JF forgot to mention that he created some <em style="color: yellow;">'middlewares'</em>. This is basically extra code that runs before you get to a route.
In particular, JF has some middlewares to determine if a user is logged in and whether they are on their own profile to check whether they can edit things.
This means I can't just go to a URL like <em style="color: plum;">"users/SomeOtherPersonsID/edit-profile"</em> and vandalize their page because, even if I'm logged in, my user ID won't be the same as the one I'm trying to edit. Blast!
</p>
</div>
<div>
<br />
<p>|</p>
</div>
</div>
</section>
<div class="parallaxParent">
<div style="background-image: url(images/project2/gameshare.gif)"></div>
</div>
<section class="parallax-section" style="border: 10px darkgreen; border-style: ridge none;">
<div class="parallax-content">
<h1>Enjoying <span style="color: green">GameShare™</span>? Be sure to leave a rating!</h1>
<p>
In designing <span style="color: green">GameShare™</span>, I tried to be practical when it came to deciding my features. There were several things I wanted to add: a way to display all games in a list, a way to search for games or users, and biggest of all, a way for users to add new games that weren’t in the database. None of these were necessary to meet the objective so I left them aside to work on if I had spare time. I did also plan a chat feature to actually connect with other users but I did not manage to get it working in time for my project submission. Bits and pieces of it are still in the code so if you’re interested in sifting around for it, be my guest.
</p>
<p>
As I am probably going to make abundantly clear throughout this website, visual design is not my strong suit. I did my best to try and emulate the styles and colors of gaming websites I used (e.g. Nexus Mods) and while I do think the site could be more polished in some areas (like the UIs), I am fairly happy with what I made. If I went back and did it again, I would definitely not even bother with the chat feature as it ultimately took up a lot of time for something that wasn’t needed at all.
</p>
That’s it for <span style="color: green">GameShare™</span>! Feel free to move on to my other projects:
<a class="bottom-link" href="projectOne" style="color: red;">Project 1: Tic-Tac-Toe</a>,
<a class="bottom-link" href="projectThree" style="color: #d93900;">Project 3: Petrol Down Under</a>, or
<a class="bottom-link" href="projectFour" style="color: #0047ab;">Project 4</a>
</p>
</div>
</section>
</div> <!-- wrapper div -->
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@barba/core"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ScrollTrigger.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/ScrollMagic.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/animation.gsap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/debug.addIndicators.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="barba.js"></script>
<script class="main-script" src="everything.js"></script>
</body>
</html>