notherbase-fs 1.2.0 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,27 +1,12 @@
1
- const { user, connectionSuccess } = require("../models");
1
+ const { connectionSuccess } = require("../models");
2
2
 
3
3
  const authCheck = async function authCheck(req, res, next){
4
4
  if (connectionSuccess) {
5
- try {
6
- if (req.session.currentUser) {
7
- const foundAccount = await user.findById(req.session.currentUser);
8
-
9
- if (foundAccount) {
10
- req.session.currentUserFull = foundAccount;
11
- next();
12
- }
13
- else {
14
- req.session.currentUserFull = null;
15
- res.redirect("/the-front");
16
- }
17
- }
18
- else{
19
- res.redirect("/the-front");
20
- }
5
+ if (req.session.currentUser) {
6
+ next();
21
7
  }
22
- catch(err) {
23
- console.log("database error");
24
- console.log(err);
8
+ else {
9
+ res.redirect("/the-front");
25
10
  }
26
11
  }
27
12
  else {
@@ -7,7 +7,7 @@ const { inventory, item, connectionSuccess } = require("../models");
7
7
  router.get("/", async function(req, res) {
8
8
  if (connectionSuccess) {
9
9
  try {
10
- if (req.session.currentUserFull) {
10
+ if (req.session.currentUser) {
11
11
  let foundInventory = await inventory.findOne({user: req.session.currentUser}).populate("items.item");
12
12
 
13
13
  res.status(200).send({ foundInventory: foundInventory });
@@ -1,4 +1,4 @@
1
- const { inventory, connectionSuccess } = require("../models");
1
+ const { user, inventory, connectionSuccess } = require("../models");
2
2
 
3
3
  let router = require("express").Router();
4
4
  let dir = "";
@@ -9,6 +9,7 @@ let front = function front(detail) {
9
9
  styles: [],
10
10
  externalStyles: [],
11
11
  localScripts: [],
12
+ requiredItems: [],
12
13
  needsKey: "",
13
14
  dropOff: "",
14
15
  ...detail.options
@@ -29,11 +30,18 @@ let front = function front(detail) {
29
30
  return script;
30
31
  });
31
32
 
32
- detail.options.main = "index";
33
- if (detail.name !== "") detail.options.main = detail.name;
34
- detail.options.main = `${dir}/views/${detail.options.main}`;
35
-
36
33
  router.get(`/${detail.name}`, async function(req, res) {
34
+ detail.options.main = "index";
35
+ if (detail.name !== "") detail.options.main = detail.name;
36
+ detail.options.main = `${dir}/views/${detail.options.main}`;
37
+
38
+ let foundItemIDs = [];
39
+ for (let m = 0; m < detail.options.requiredItems.length; m++) {
40
+ let foundItem = await item.findOne({name: detail.options.requiredItems[m]});
41
+
42
+ foundItemIDs.push(foundItem._id);
43
+ }
44
+
37
45
  let context = {
38
46
  siteTitle: "NotherBase | The Front",
39
47
  user: null,
@@ -41,18 +49,17 @@ let front = function front(detail) {
41
49
  externalStyles: detail.options.externalStyles,
42
50
  main: detail.options.main,
43
51
  localScripts: detail.options.localScripts,
52
+ itemIDs: foundItemIDs,
44
53
  inventory: null,
45
54
  query: req.query
46
55
  }
47
56
 
48
57
  if (connectionSuccess) {
49
- context.user = req.session.currentUserFull;
50
-
51
58
  try {
52
- const foundInventory = await inventory.findOne({ user: req.session.currentUser }).populate("items.item");
53
- context.inventory = foundInventory;
59
+ context.user = await user.findById(req.session.currentUser);
60
+ context.inventory = await inventory.findOne({ user: req.session.currentUser }).populate("items.item");
54
61
 
55
- if (detail.options.needsKey !== "" && foundInventory) {
62
+ if (detail.options.needsKey !== "" && context.inventory) {
56
63
  let hasKey = false;
57
64
 
58
65
  for (let i = 0; i < foundInventory.items.length; i++) {
@@ -1,15 +1,173 @@
1
1
  const express = require("express");
2
2
  const router = express.Router();
3
3
  const bcrypt = require("bcrypt");
4
+ const nodemailer = require("nodemailer");
4
5
 
5
6
  // Import my Data
6
- const { user, inventory } = require("../models");
7
+ const { user, inventory, sendMail } = require("../models");
7
8
 
8
9
  const authCheck = require("./authCheck");
9
10
 
11
+ let getAttributes = async function getAttributes(userID) {
12
+ try {
13
+ let foundUser = await user.findById(userID, 'attributes');
14
+
15
+ if (!foundUser.attributes || foundUser.attributes == {}) {
16
+ foundUser.attributes = {
17
+ translation: 0,
18
+ strength: 0,
19
+ agility: 0,
20
+ defense: 0
21
+ }
22
+
23
+ await foundUser.save();
24
+ }
25
+
26
+ return foundUser;
27
+ }
28
+ catch (err) {
29
+ console.log(err);
30
+ return null;
31
+ }
32
+ }
33
+
34
+ router.get("/basic", async function(req, res) {
35
+ try {
36
+ if (req.session.currentUser) {
37
+ let foundUser = await user.findById(req.session.currentUser, 'username email');
38
+
39
+ res.status(200).send(foundUser);
40
+ }
41
+ else {
42
+ res.status(401).send("Please login first!");
43
+ }
44
+ }
45
+ catch(err) {
46
+ console.log(err);
47
+ res.status(500).end();
48
+ }
49
+ });
50
+
51
+ router.get("/logout", authCheck, async function(req, res) {
52
+ try {
53
+ await req.session.destroy();
54
+
55
+ res.redirect(`/`);
56
+ }
57
+ catch {
58
+ console.log(err);
59
+ }
60
+ });
61
+
62
+ router.get("/all", async function(req, res) {
63
+ try {
64
+ let foundUsers = await user.find({}, 'username coin home authLevels location attributes');
65
+
66
+ res.status(200).send({ foundUsers: foundUsers });
67
+ }
68
+ catch(err) {
69
+ res.status(500).end();
70
+ console.log(err);
71
+ }
72
+ });
73
+
74
+ router.get("/password-reset", async function(req, res) {
75
+ try {
76
+ let foundUser = await user.findOne({ email: req.query.email });
77
+
78
+ if (foundUser) {
79
+ foundUser.reset.token = Math.floor(Math.random() * 9999);
80
+ foundUser.reset.exp = Date.now() + (1000 * 60 * 30);
81
+
82
+ await foundUser.save();
83
+
84
+ sendMail.passwordReset(req.query.email, foundUser.reset.token);
85
+
86
+ res.status(200).send("Reset link sent!");
87
+ }
88
+ else {
89
+ res.status(401).send("Failed: user not found!");
90
+ }
91
+ }
92
+ catch(err) {
93
+ console.log(err);
94
+
95
+ res.status(500).send("Update Failed: Database error!");
96
+ }
97
+ });
98
+
99
+ router.post("/password-reset", async function(req, res) {
100
+ try {
101
+ const foundUser = await user.findOne({ "reset.token": req.body.token });
102
+
103
+ if (foundUser) {
104
+ if (foundUser.reset.exp > Date.now()) {
105
+ if (req.body.password !== req.body.confirmation) res.status(400).send("Passwords must match!");
106
+ else {
107
+ foundUser.reset = {};
108
+
109
+ const salt = await bcrypt.genSalt(10);
110
+ const hash = await bcrypt.hash(req.body.password, salt);
111
+
112
+ foundUser.password = hash;
113
+ await foundUser.save();
114
+
115
+ res.status(200).send("Password changed successfully!");
116
+ }
117
+ }
118
+ else res.status(498).send("Reset token expired!");
119
+ }
120
+ else {
121
+ res.status(404).send("Reset token not valid!");
122
+ }
123
+ }
124
+ catch(err) {
125
+ console.log(err);
126
+
127
+ res.status(500).send("Internal Server Error!");
128
+ }
129
+ });
130
+
131
+ router.get("/attributes", async function(req, res) {
132
+ try {
133
+ if (req.session.currentUser) {
134
+ let foundUser = await getAttributes(req.session.currentUser);
135
+
136
+ res.status(200).send(foundUser.attributes);
137
+ }
138
+ else {
139
+ res.status(401).send("Please login first!");
140
+ }
141
+ }
142
+ catch(err) {
143
+ res.status(500).end();
144
+ console.log(err);
145
+ }
146
+ });
147
+
148
+ router.get("/attributes/check", authCheck, async function(req, res) {
149
+ try {
150
+ if (req.session.currentUser) {
151
+ let foundUser = await getAttributes(req.session.currentUser);
152
+
153
+ if (foundUser.attributes[req.query.check] >= parseInt(req.query.against)) {
154
+ res.status(200).send("Pass");
155
+ }
156
+ else res.status(200).send("Fail");
157
+ }
158
+ else {
159
+ res.status(401).send("Please login first!");
160
+ }
161
+ }
162
+ catch(err) {
163
+ res.status(500).end();
164
+ console.log(err);
165
+ }
166
+ });
167
+
10
168
  router.post("/register", async function(req, res) {
11
169
  try {
12
- const foundAccount = await user.findOne({ username: req.body.username });
170
+ let foundAccount = await user.findOne({ username: req.body.username });
13
171
 
14
172
  if (!foundAccount) {
15
173
  const salt = await bcrypt.genSalt(10);
@@ -18,10 +176,17 @@ router.post("/register", async function(req, res) {
18
176
  let qAuth = await user.create({
19
177
  username: req.body.username,
20
178
  password: hash,
21
- email: "temp@example.com",
179
+ email: req.body.email,
22
180
  coin: 0,
23
181
  home: "/",
24
- authLevels: [ "Basic" ]
182
+ authLevels: [ "Basic" ],
183
+ location: "/the-front",
184
+ attributes: {
185
+ translation: 0,
186
+ strength: 0,
187
+ agility: 0,
188
+ defense: 0
189
+ }
25
190
  });
26
191
 
27
192
  await inventory.create({
@@ -44,12 +209,11 @@ router.post("/register", async function(req, res) {
44
209
 
45
210
  router.post("/login", async function(req, res) {
46
211
  try {
47
- const foundAccount = await user.findOne({ username: req.body.username });
212
+ const foundAccount = await user.findOne({ email: req.body.email });
48
213
 
49
214
  if (foundAccount) {
50
215
  if (await bcrypt.compare(req.body.password, foundAccount.password)) {
51
216
  req.session.currentUser = foundAccount._id;
52
- req.session.currentUserFull = foundAccount;
53
217
 
54
218
  res.status(200).send("Login successful!");
55
219
  }
@@ -58,7 +222,7 @@ router.post("/login", async function(req, res) {
58
222
  }
59
223
  }
60
224
  else {
61
- res.status(401).send("Login Failed: username not found!");
225
+ res.status(401).send("Login Failed: Email not found!");
62
226
  }
63
227
  }
64
228
  catch(err) {
@@ -68,41 +232,171 @@ router.post("/login", async function(req, res) {
68
232
  }
69
233
  });
70
234
 
71
- router.get("/logout", authCheck, async function(req, res) {
235
+ router.post("/email", async function(req, res) {
72
236
  try {
73
- await req.session.destroy();
237
+ if (req.session.currentUser) {
238
+ let foundAccount = await user.findOne({ email: req.body.email });
74
239
 
75
- res.redirect(`/`);
240
+ if (!foundAccount) {
241
+ let foundUser = await user.findById(req.session.currentUser);
242
+
243
+ if (foundUser) {
244
+ foundUser.email = req.body.email;
245
+ await foundUser.save();
246
+
247
+ res.status(200).send("Update successful!");
248
+ }
249
+ else {
250
+ res.status(401).send("Update Failed: user not found!");
251
+ }
252
+ }
253
+ else {
254
+ res.status(401).send("Update Failed: email already in use!");
255
+ }
256
+ }
257
+ else {
258
+ res.status(401).send("Please login first!");
259
+ }
76
260
  }
77
- catch {
261
+ catch(err) {
78
262
  console.log(err);
263
+
264
+ res.status(500).send("Update Failed: Database error!");
79
265
  }
80
266
  });
81
267
 
82
- router.get("/all", authCheck, async function(req, res) {
268
+ router.post("/username", async function(req, res) {
83
269
  try {
84
- let foundUsers = await user.find({}, 'username coin home authLevels location');
270
+ if (req.session.currentUser) {
271
+ let foundAccount = await user.findOne({ username: req.body.username });
85
272
 
86
- res.status(200).send({ foundUsers: foundUsers });
273
+ if (!foundAccount) {
274
+ let foundUser = await user.findById(req.session.currentUser);
275
+
276
+ if (foundUser) {
277
+ foundUser.username = req.body.username;
278
+ await foundUser.save();
279
+
280
+ res.status(200).send("Update successful!");
281
+ }
282
+ else {
283
+ res.status(401).send("Update Failed: user not found!");
284
+ }
285
+ }
286
+ else {
287
+ res.status(401).send("Update Failed: username taken!");
288
+ }
289
+ }
290
+ else {
291
+ res.status(401).send("Please login first!");
292
+ }
87
293
  }
88
294
  catch(err) {
89
- res.status(500).end();
90
295
  console.log(err);
296
+
297
+ res.status(500).send("Update Failed: Database error!");
91
298
  }
92
299
  });
93
300
 
94
- router.delete("/", authCheck, async function(req, res) {
301
+ router.post("/password", async function(req, res) {
302
+ try {
303
+ if (req.session.currentUser) {
304
+ let foundUser = await user.findById(req.session.currentUser);
305
+
306
+ if (foundUser) {
307
+ const salt = await bcrypt.genSalt(10);
308
+ const hash = await bcrypt.hash(req.body.password, salt);
309
+ foundUser.password = hash;
310
+ await foundUser.save();
311
+
312
+ res.status(200).send("Update successful!");
313
+ }
314
+ else {
315
+ res.status(401).send("Update Failed: user not found!");
316
+ }
317
+ }
318
+ else {
319
+ res.status(401).send("Please login first!");
320
+ }
321
+ }
322
+ catch(err) {
323
+ console.log(err);
324
+
325
+ res.status(500).send("Update Failed: Database error!");
326
+ }
327
+ });
328
+
329
+ router.post("/attributes", async function(req, res) {
95
330
  try {
96
- const found = await user.findByIdAndDelete(req.session.currentUser);
331
+ if (req.session.currentUser) {
332
+ let foundUser = await getAttributes(req.session.currentUser);
97
333
 
98
- if (!found) console.log("Could not find account. No deletion!");
334
+ if (foundUser) {
335
+ foundUser.attributes[req.body.change] = parseInt(req.body.to);
336
+ await foundUser.save();
99
337
 
100
- await req.session.destroy();
338
+ res.status(200).send("AttUp successful!");
339
+ }
340
+ else {
341
+ res.status(401).send("AttUp Failed: user not found!");
342
+ }
343
+ }
344
+ else {
345
+ res.status(401).send("Please login first!");
346
+ }
347
+ }
348
+ catch(err) {
349
+ console.log(err);
350
+
351
+ res.status(500).send("AttUp Failed: Database error!");
352
+ }
353
+ });
101
354
 
102
- res.redirect("/");
355
+ router.post("/attributes/increment", async function(req, res) {
356
+ try {
357
+ if (req.session.currentUser) {
358
+ let foundUser = await getAttributes(req.session.currentUser);
359
+
360
+ if (foundUser) {
361
+ if (foundUser.attributes[req.body.change] < req.body.max) {
362
+ foundUser.attributes[req.body.change]++;
363
+ await foundUser.save();
364
+ res.status(200).send({ newLevel: foundUser.attributes[req.body.change] });
365
+ }
366
+ else {
367
+ res.status(304).send({ newLevel: foundUser.attributes[req.body.change] });
368
+ }
369
+ }
370
+ else {
371
+ res.status(401).send("AttUp Failed: user not found!");
372
+ }
373
+ }
374
+ else {
375
+ res.status(401).send("Please login first!");
376
+ }
377
+ }
378
+ catch(err) {
379
+ console.log(err);
380
+
381
+ res.status(500).send("AttUp Failed: Database error!");
382
+ }
383
+ });
384
+
385
+ router.delete("/", authCheck, async function(req, res) {
386
+ try {
387
+ if (req.session.currentUser) {
388
+ await user.findByIdAndDelete(req.session.currentUser);
389
+ await req.session.destroy();
390
+
391
+ res.redirect("/");
392
+ }
393
+ else {
394
+ res.status(401).send("Please login first!");
395
+ }
103
396
  }
104
397
  catch {
105
398
  console.log(err);
399
+ res.status(500).send("Delete Failed: Database error!");
106
400
  }
107
401
  });
108
402
 
package/models/index.js CHANGED
@@ -5,5 +5,6 @@ module.exports = {
5
5
  user: require("./user"),
6
6
  contact: require("./contact"),
7
7
  inventory: require("./inventory"),
8
- game: require("./game")
8
+ game: require("./game"),
9
+ sendMail: require("./send-mail")
9
10
  }
@@ -0,0 +1,26 @@
1
+ const nodemailer = require("nodemailer");
2
+
3
+ const passwordReset = async (toEmail, resetToken) => {
4
+ var transporter = nodemailer.createTransport({
5
+ service: 'gmail',
6
+ auth: {
7
+ user: process.env.NOREPLY,
8
+ pass: process.env.NOREPLYPW
9
+ }
10
+ });
11
+
12
+ var mailOptions = {
13
+ from: process.env.NOREPLY,
14
+ to: toEmail,
15
+ subject: 'Password Reset for NotherBase',
16
+ html: `<h1>Your One-Time Password Reset Code: ${resetToken}<h1>`
17
+ };
18
+
19
+ transporter.sendMail(mailOptions, function(error, info){
20
+ if (error) console.log(error);
21
+ });
22
+ };
23
+
24
+ module.exports = {
25
+ passwordReset: passwordReset
26
+ };
package/models/user.js CHANGED
@@ -9,7 +9,17 @@ const user = mongoose.model('users',
9
9
  coin: Number,
10
10
  home: String,
11
11
  authLevels: [ String ],
12
- location: String
12
+ location: String,
13
+ attributes: {
14
+ translation: Number,
15
+ strength: Number,
16
+ agility: Number,
17
+ defense: Number
18
+ },
19
+ reset: {
20
+ token: Number,
21
+ exp: Number
22
+ }
13
23
  })
14
24
  );
15
25
 
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "notherbase-fs",
3
- "version": "1.2.0",
3
+ "version": "1.2.3",
4
4
  "description": "Functions to help make developing for NotherBase easier.",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "nodemon test.js"
7
+ "test": "nodemon test.js",
8
+ "gmail-auth": "node gmail-auth.js",
9
+ "gmail-token": "node gmail-token.js"
8
10
  },
9
11
  "repository": {
10
12
  "type": "git",
@@ -23,8 +25,10 @@
23
25
  "ejs": "^3.1.6",
24
26
  "express": "^4.17.1",
25
27
  "express-session": "^1.17.2",
28
+ "googleapis": "^100.0.0",
26
29
  "method-override": "^3.0.0",
27
30
  "mongoose": "^6.1.7",
31
+ "nodemailer": "^6.7.5",
28
32
  "serve-favicon": "^2.5.0",
29
33
  "socket.io": "^4.4.1"
30
34
  }
@@ -0,0 +1,41 @@
1
+ .content#account {
2
+ padding: 10px;
3
+ }
4
+
5
+ .content#account hr {
6
+ margin: 10px 0;
7
+ }
8
+
9
+ .content#account #info {
10
+ text-align: center;
11
+ width: 100%;
12
+ margin-top: 40px;
13
+ }
14
+
15
+ .content#account .setting {
16
+ background: var(--darkBgColor);
17
+ padding: 25px;
18
+ border-radius: 5px;
19
+ display: flex;
20
+ justify-content: center;
21
+ text-align: center;
22
+ flex-wrap: wrap;
23
+ }
24
+
25
+ .content#account p {
26
+ width: 100%;
27
+ }
28
+
29
+ .content#account button {
30
+ width: 40%;
31
+ }
32
+
33
+ .content#account .edit {
34
+ background: var(--darkWoodColor);
35
+ padding: 25px;
36
+ border-radius: 5px;
37
+ display: flex;
38
+ justify-content: center;
39
+ text-align: center;
40
+ flex-wrap: wrap;
41
+ }
@@ -3,11 +3,9 @@
3
3
  color: var(--textColor);
4
4
  width: 100%;
5
5
  height: 100%;
6
- background-color: var(--bgColor);
6
+ /* background-color: var(--bgColor); */
7
7
  padding: 5px;
8
8
  z-index: 500;
9
- display: flex;
10
- flex-wrap: wrap;
11
9
  overflow: auto;
12
10
  }
13
11
 
@@ -56,6 +54,7 @@
56
54
  top: 0;
57
55
  margin: 0;
58
56
  padding: 0;
57
+ border-radius: 0;
59
58
  }
60
59
 
61
60
  .item-card hr {
@@ -1,6 +1,8 @@
1
1
  :root {
2
2
  --bgColor: rgb(37, 37, 37);
3
+ --bgColorTransparent: rgba(37, 37, 37, 0.404);
3
4
  --darkBgColor: rgb(27, 27, 27);
5
+ --veryDarkBgColorTransparent: rgba(15, 15, 15, 0.404);
4
6
  --veryDarkBgColor: rgb(15, 15, 15);
5
7
  --lightBgColor: rgb(57, 57, 57);
6
8
  --shadowColor: rgb(26, 26, 26);
@@ -22,6 +24,11 @@
22
24
  display: none !important;
23
25
  }
24
26
 
27
+ .camo {
28
+ transition: all .15s;
29
+ opacity: 0;
30
+ }
31
+
25
32
  body {
26
33
  background-color: var(--darkBgColor);
27
34
  min-height: 100vh;
@@ -86,7 +93,7 @@ p {
86
93
  width: 80%;
87
94
  }
88
95
 
89
- main input, main textarea {
96
+ input, textarea {
90
97
  padding: 10px;
91
98
  width: calc(100% - 10px);
92
99
  background-color: var(--bgColor);
@@ -96,7 +103,7 @@ main input, main textarea {
96
103
  font-size: 15px;
97
104
  }
98
105
 
99
- main input[type=submit], main button, main a {
106
+ input[type=submit], button, a {
100
107
  background-color: var(--bgColor);
101
108
  padding: 10px;
102
109
  border-radius: 5px;
@@ -108,7 +115,7 @@ main input[type=submit], main button, main a {
108
115
  text-align: center;
109
116
  }
110
117
 
111
- main input[type=submit]:hover, main button:hover, main a:hover {
118
+ input[type=submit]:hover, button:hover, a:hover {
112
119
  border: 1px solid var(--textColorBright);
113
120
  color: var(--textColorBright);
114
121
  cursor: pointer;
@@ -1,20 +1,37 @@
1
1
  .menu {
2
2
  width: 75%;
3
- height: 75%;
4
- margin: 5% 12.5%;
3
+ height: 50%;
5
4
  border: 1px solid var(--textColor);
6
5
  color: var(--textColor);
7
6
  position: fixed;
8
- left: 0;
9
- top: 0;
10
- background-color: var(--bgColor);
7
+ left: 12.5%;
8
+ top: 5%;
9
+ background-color: var(--bgColorTransparent);
10
+ backdrop-filter: blur(3px);
11
11
  border-radius: 5px;
12
12
  z-index: 500;
13
13
  }
14
14
 
15
+ .menu hr {
16
+ margin: 0;
17
+ }
18
+
19
+ .ui .fade {
20
+ background-color: var(--veryDarkBgColorTransparent);
21
+ backdrop-filter: blur(3px);
22
+ width: 100%;
23
+ height: 100%;
24
+ position: fixed;
25
+ left: 0;
26
+ top: 0;
27
+ z-index: 400;
28
+ }
29
+
15
30
  .ui button {
16
31
  background-color: var(--bgColor);
17
32
  border: 1px solid var(--textColor);
33
+ margin: 0;
34
+
18
35
  }
19
36
 
20
37
  .ui button:hover {
@@ -29,6 +46,7 @@
29
46
  right: 5px;
30
47
  width: 64px;
31
48
  height: 64px;
49
+ border-radius: 5px;
32
50
  }
33
51
 
34
52
  .menu button#close {
@@ -37,6 +55,7 @@
37
55
  width: 32px;
38
56
  height: 32px;
39
57
  position: absolute;
58
+ border-radius: 0;
40
59
  }
41
60
 
42
61
  .menu .tabs {
@@ -77,5 +96,4 @@
77
96
  border-top: none;
78
97
  border-radius: 0 0 5px 5px;
79
98
  text-align: center;
80
- z-index: 499;
81
99
  }
@@ -0,0 +1,3 @@
1
+ .content#more {
2
+ padding: 10px;
3
+ }
@@ -0,0 +1,12 @@
1
+ .content#player {
2
+ padding: 10px;
3
+ }
4
+
5
+ .content#player h3 {
6
+ width: 100%;
7
+ text-transform: capitalize;
8
+ }
9
+
10
+ .content#player h3 button {
11
+ width: 40px;
12
+ }
@@ -4,15 +4,35 @@
4
4
 
5
5
  <hr>
6
6
 
7
+ <h3>Register Account</h3>
8
+ <input type="email" placeholder="email" id="register-email">
9
+ <input type="text" placeholder="username" id="register-user">
10
+ <input type="password" placeholder="password" id="register-pass">
11
+ <button id="register-button">register</button>
12
+ <p id="register-info"></p>
13
+
14
+ <hr>
15
+
7
16
  <div class="auth-form" id="login-form">
8
17
  <h1>NotherBase</h1>
9
18
  <h3>Login to Your Account</h3>
10
- <input type="text" name="username" placeholder="username" id="login-user">
19
+ <input type="email" name="email" placeholder="user@email.com" id="login-email">
11
20
  <input type="password" name="password" placeholder="password" id="login-pass">
12
21
  <button id="login-button">Login</button>
22
+ <button id="reset-password">Reset Password</button>
13
23
  <p id="login-info"></p>
14
24
  </div>
15
25
 
26
+ <div class="auth-form" id="reset-form">
27
+ <h1>NotherBase</h1>
28
+ <h3>Login to Your Account</h3>
29
+ <input type="number" name="token" placeholder="Your Reset Code" id="token">
30
+ <input type="password" name="password" placeholder="Type Your New Password Here" minlength="8" required id="password">
31
+ <input type="password" name="confirmation" placeholder="Type Your New Password Here Again" minlength="8" required id="confirmation">
32
+ <button id="change-password">Change Password</button>
33
+ <p id="change-info"></p>
34
+ </div>
35
+
16
36
  <hr>
17
37
 
18
38
  <div class="locked to nother-base">
@@ -24,8 +44,13 @@
24
44
  </a>
25
45
 
26
46
  <script>
47
+ let $resetPassword = $("#reset-password");
27
48
  let $loginButton = $("#login-button");
28
49
  let $loginInfo = $("#login-info");
50
+ let $registerButton = $("#register-button");
51
+ let $registerInfo = $("#register-info");
52
+ let $changePasswordButton = $("button#change-password");
53
+ let $changeInfo = $("#change-info");
29
54
  let $toNonoButton = $(".locked.to.nother-base");
30
55
  let $toNonoLink = $(".invisible.to.nother-base");
31
56
 
@@ -37,25 +62,86 @@
37
62
  $loginButton.on("click", async function () {
38
63
  try {
39
64
  await $.post("/user/login", {
40
- username: $("#login-user").val(),
65
+ email: $("#login-email").val(),
41
66
  password: $("#login-pass").val()
42
67
  }, (data) => {
43
68
  $loginInfo.text("To your right you hear the sound of a bang against a chain-link fence. You've logged in.");
44
69
  $toNonoButton.addClass("invisible");
45
70
  $toNonoLink.removeClass("invisible");
46
71
 
47
- if (playerInventory) {
48
- playerInventory.refresh();
49
- }
72
+ playerInventory.refresh();
73
+ playerAttributes.refresh();
74
+ accountServices.refresh();
75
+ });
76
+ } catch (error) {
77
+ if (error.status === 401) {
78
+ $loginInfo.text("Login Error: Email or password incorrect!");
79
+ }
80
+ else if (error.status === 500) {
81
+ $loginInfo.text("Server Error: Try again later!");
82
+ }
83
+ }
84
+ });
85
+
86
+ $registerButton.on("click", async function () {
87
+ try {
88
+ await $.post("/user/register", {
89
+ username: $("#register-user").val(),
90
+ password: $("#register-pass").val(),
91
+ email: $("#register-email").val()
92
+ }, (data) => {
93
+ $registerInfo.text("Account registered.");
50
94
  });
51
95
  } catch (error) {
52
96
  //console.log(error);
97
+ if (error.status === 400) {
98
+ $registerInfo.text("Login Error: Username already taken!");
99
+ }
100
+ else if (error.status === 500) {
101
+ $registerInfo.text("Server Error: Try again later!");
102
+ }
103
+ }
104
+ });
105
+
106
+ $resetPassword.on("click", async function () {
107
+ try {
108
+ await $.get("/user/password-reset", { email: $("#login-email").val() });
109
+ $loginInfo.text("Reset link sent to your email.");
110
+ }
111
+ catch(err) {
53
112
  if (error.status === 401) {
54
- $loginInfo.text("Login Error: Username or password incorrect!");
113
+ $loginInfo.text("Reset Error: Email not found!");
55
114
  }
56
115
  else if (error.status === 500) {
57
116
  $loginInfo.text("Server Error: Try again later!");
58
117
  }
59
118
  }
60
119
  });
120
+
121
+ $changePasswordButton.on("click", async function () {
122
+ try {
123
+ let token = $("#reset-form #token").val();
124
+
125
+ await $.post(`/user/password-reset/${token}`, {
126
+ token: token,
127
+ password: $("#reset-form #password").val(),
128
+ confirmation: $("#reset-form #confirmation").val()
129
+ }, (data) => {
130
+ $changeInfo.text("Password changed.");
131
+ });
132
+ } catch (error) {
133
+ if (error.status === 498) {
134
+ $changeInfo.text("Change Error: Your reset code expired! Please request a new one.");
135
+ }
136
+ else if (error.status === 400) {
137
+ $changeInfo.text("Change Error: Passwords must match!");
138
+ }
139
+ else if (error.status === 404) {
140
+ $changeInfo.text("Change Error: Reset code not valid!");
141
+ }
142
+ else if (error.status === 500) {
143
+ $changeInfo.text("Server Error: Try again later!");
144
+ }
145
+ }
146
+ });
61
147
  </script>
@@ -0,0 +1,161 @@
1
+ <div id="account" class="invisible content">
2
+ <p id="please-login">Please login to view your account settings.</p>
3
+
4
+ <div class="invisible settings">
5
+ <h3>Email:</h3>
6
+ <div class="setting" id="email">
7
+ <p></p>
8
+ <button onclick="accountServices.editEmail()">Change Email</button>
9
+ </div>
10
+ <div class="edit invisible" id="email">
11
+ <input type="email" name="email">
12
+ <button onclick="accountServices.updateEmail()">Update</button>
13
+ <button onclick="accountServices.cancelEmail()">Cancel</button>
14
+ </div>
15
+
16
+ <hr>
17
+
18
+ <h3>Username:</h3>
19
+ <div class="setting" id="username">
20
+ <p></p>
21
+ <button onclick="accountServices.editUsername()">Change Username</button>
22
+ </div>
23
+ <div class="edit invisible" id="username">
24
+ <input type="text" name="username">
25
+ <button onclick="accountServices.updateUsername()">Update</button>
26
+ <button onclick="accountServices.cancelUsername()">Cancel</button>
27
+ </div>
28
+
29
+ <hr>
30
+
31
+ <h3>Password:</h3>
32
+ <div class="setting" id="password">
33
+ <p>*********</p>
34
+ <button onclick="accountServices.editPassword()">Change Password</button>
35
+ </div>
36
+ <div class="edit invisible" id="password">
37
+ <input type="password" name="password">
38
+ <button onclick="accountServices.updatePassword()">Update</button>
39
+ <button onclick="accountServices.cancelPassword()">Cancel</button>
40
+ </div>
41
+
42
+ <p id="info"></p>
43
+ </div>
44
+ </div>
45
+
46
+ <script>
47
+ class AccountServices {
48
+ constructor() {
49
+ this.$emailSetting = $(".content#account .setting#email");
50
+ this.$email = this.$emailSetting.find("p");
51
+ this.$emailEdit = $(".content#account .edit#email");
52
+ this.$emailInput = this.$emailEdit.find("input");
53
+
54
+ this.$usernameSetting = $(".content#account .setting#username");
55
+ this.$username = this.$usernameSetting.find("p");
56
+ this.$usernameEdit = $(".content#account .edit#username");
57
+ this.$usernameInput = this.$usernameEdit.find("input");
58
+
59
+ this.$passwordSetting = $(".content#account .setting#password");
60
+ this.$passwordEdit = $(".content#account .edit#password");
61
+ this.$passwordInput = this.$passwordEdit.find("input");
62
+
63
+ this.$info = $(".content#account #info");
64
+
65
+ <% if (user) { %>
66
+ this.refresh();
67
+ <% } %>
68
+ }
69
+
70
+ async refresh() {
71
+ try {
72
+ $.get("/user/basic", (data) => {
73
+ this.$email.text(data.email);
74
+ this.$emailInput.val(data.email);
75
+ this.$username.text(data.username);
76
+ this.$usernameInput.val(data.username);
77
+
78
+ $(".content#account .settings").removeClass("invisible");
79
+ $(".content#account #please-login").addClass("invisible");
80
+ });
81
+ }
82
+ catch(err) {
83
+ this.$info.text(err);
84
+ }
85
+ }
86
+
87
+ editEmail() {
88
+ this.$emailSetting.addClass("invisible");
89
+ this.$emailEdit.removeClass("invisible");
90
+ }
91
+
92
+ cancelEmail() {
93
+ this.$emailSetting.removeClass("invisible");
94
+ this.$emailEdit.addClass("invisible");
95
+ this.$emailInput.val(this.$email.text());
96
+ }
97
+
98
+ async updateEmail() {
99
+ try {
100
+ await $.post("/user/email", { email: this.$emailInput.val() }, (data) => {
101
+ this.$email.text(this.$emailInput.val());
102
+ this.cancelEmail();
103
+ this.$info.text("Email Updated!");
104
+ });
105
+ }
106
+ catch(err) {
107
+ this.$info.text(err);
108
+ }
109
+ }
110
+
111
+ editUsername() {
112
+ this.$usernameSetting.addClass("invisible");
113
+ this.$usernameEdit.removeClass("invisible");
114
+ }
115
+
116
+ cancelUsername() {
117
+ this.$usernameSetting.removeClass("invisible");
118
+ this.$usernameEdit.addClass("invisible");
119
+ this.$usernameInput.val(this.$username.text());
120
+ }
121
+
122
+ async updateUsername() {
123
+ try {
124
+ await $.post("/user/username", { username: this.$usernameInput.val() }, (data) => {
125
+ this.$username.text(this.$usernameInput.val());
126
+ this.cancelUsername();
127
+ this.$info.text("Username Updated!");
128
+ });
129
+ }
130
+ catch(err) {
131
+ if (err.status === 401) this.$info.text("Username already taken!");
132
+ else this.$info.text("Server Error");
133
+ }
134
+ }
135
+
136
+ editPassword() {
137
+ this.$passwordSetting.addClass("invisible");
138
+ this.$passwordEdit.removeClass("invisible");
139
+ }
140
+
141
+ cancelPassword() {
142
+ this.$passwordSetting.removeClass("invisible");
143
+ this.$passwordEdit.addClass("invisible");
144
+ this.$passwordInput.val("");
145
+ }
146
+
147
+ async updatePassword() {
148
+ try {
149
+ await $.post("/user/password", { password: this.$passwordInput.val() }, (data) => {
150
+ this.cancelPassword();
151
+ this.$info.text("Password Updated!");
152
+ });
153
+ }
154
+ catch(err) {
155
+ this.$info.text(err);
156
+ }
157
+ }
158
+ }
159
+
160
+ let accountServices = new AccountServices();
161
+ </script>
package/views/head.ejs CHANGED
@@ -14,8 +14,11 @@
14
14
  <link href="https://fonts.googleapis.com/css2?family=Redacted+Script:wght@300&display=swap" rel="stylesheet">
15
15
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
16
16
  <link rel="stylesheet" href="/styles/main.css">
17
- <link rel="stylesheet" href="/styles/inventory.css">
18
17
  <link rel="stylesheet" href="/styles/menu.css">
18
+ <link rel="stylesheet" href="/styles/inventory.css">
19
+ <link rel="stylesheet" href="/styles/player.css">
20
+ <link rel="stylesheet" href="/styles/account.css">
21
+ <link rel="stylesheet" href="/styles/more.css">
19
22
  </head>
20
23
 
21
24
  <body>
@@ -63,11 +63,16 @@
63
63
  }
64
64
 
65
65
  async getData() {
66
- await $.get("/inventory", (data) => {
67
- if (data.foundInventory) this.items = data.foundInventory.items;
68
-
69
- this.clearError();
70
- });
66
+ try {
67
+ await $.get("/inventory", (data) => {
68
+ if (data.foundInventory) this.items = data.foundInventory.items;
69
+
70
+ this.clearError();
71
+ });
72
+ }
73
+ catch(err) {
74
+
75
+ }
71
76
  }
72
77
 
73
78
  render() {
@@ -97,8 +102,13 @@
97
102
  }
98
103
 
99
104
  async refresh() {
100
- await this.getData();
101
- this.render();
105
+ try {
106
+ await this.getData();
107
+ this.render();
108
+ }
109
+ catch(err) {
110
+
111
+ }
102
112
  }
103
113
 
104
114
  clearError() {
package/views/menu.ejs CHANGED
@@ -1,3 +1,7 @@
1
+ <div class="fade invisible">
2
+
3
+ </div>
4
+
1
5
  <div class="menu invisible">
2
6
  <div class="tabs">
3
7
  <button id="inventory">
@@ -6,11 +10,11 @@
6
10
  <button id="player">
7
11
  Player
8
12
  </button>
9
- <button id="contact-form">
10
- Contact
13
+ <button id="account">
14
+ Account
11
15
  </button>
12
- <button id="debug">
13
- Debug
16
+ <button id="more">
17
+ More
14
18
  </button>
15
19
  </div>
16
20
 
@@ -21,8 +25,8 @@
21
25
  <div id="content-window">
22
26
  <%- include("./inventory.ejs"); %>
23
27
  <%- include("./player.ejs"); %>
24
- <%- include("./contact.ejs"); %>
25
- <%- include("./debug.ejs"); %>
28
+ <%- include("./account.ejs"); %>
29
+ <%- include("./more.ejs"); %>
26
30
  </div>
27
31
 
28
32
  <a href="/user/logout" id="logout">Logout</a>
@@ -32,13 +36,32 @@
32
36
 
33
37
  <script>
34
38
  let $menu = $(".menu");
39
+ let $fade = $(".ui .fade");
40
+ let menuClosing = false;
41
+
42
+ let closeMenu = function closeMenu() {
43
+ menuClosing = true;
44
+ $fade.addClass("camo");
45
+
46
+ setTimeout(() => {
47
+ $menu.addClass("invisible");
48
+ $fade.addClass("invisible");
49
+ menuClosing = false;
50
+ }, 100);
51
+ }
35
52
 
36
53
  $("button#menu").on("click", function () {
37
54
  $menu.toggleClass("invisible");
55
+ $fade.removeClass("camo");
56
+ $fade.removeClass("invisible");
57
+ });
58
+
59
+ $fade.on("click", function () {
60
+ if (!menuClosing) closeMenu();
38
61
  });
39
62
 
40
63
  $(".menu button#close").on("click", function () {
41
- $menu.toggleClass("invisible");
64
+ closeMenu();
42
65
  });
43
66
 
44
67
  $(".menu .tabs button").on("click", function (e) {
@@ -1,6 +1,6 @@
1
- <div id="contact-form" class="invisible content">
1
+ <div class="content invisible" id="more">
2
2
  <h4>Message Nother</h4>
3
- <input type="text" name="content" id="content">
3
+ <textarea id="content"></textarea>
4
4
  <button id="send">Send Message</button>
5
5
  </div>
6
6
 
package/views/player.ejs CHANGED
@@ -1 +1,47 @@
1
- <div class="content invisible" id="player"></div>
1
+ <div class="content invisible" id="player">
2
+ <p id="please-login">Please login to view your player stats.</p>
3
+ </div>
4
+
5
+ <script>
6
+ class PlayerAttributes {
7
+ constructor() {
8
+ this.$content = $(".content#player");
9
+
10
+ <% if (user) { %>
11
+ this.refresh();
12
+ <% } %>
13
+ }
14
+
15
+ async setAtt(change, to) {
16
+ await $.post("/user/attributes", { change: change, to: to }, async (data) => {
17
+ await this.refresh();
18
+ });
19
+ }
20
+
21
+ async refresh() {
22
+ try {
23
+ await $.get("/user/attributes", (data) => {
24
+ this.$content.empty();
25
+ for (const [key, value] of Object.entries(data)) {
26
+ <% if (user) {
27
+ if (user.authLevels.includes("Creator")) { %>
28
+ this.$content.append(`<h3 id="${key}">
29
+ ${key}: ${value}
30
+ <input type="number"></input>
31
+ <button onclick="playerAttributes.setAtt('${key}', $('.content#player h3#${key} input').val())">Set</button>
32
+ </h3>`);
33
+ <% }
34
+ } else { %>
35
+ this.$content.append(`<h3 id="${key}">${key}: ${value}</h3>`);
36
+ <% } %>
37
+ }
38
+ });
39
+ }
40
+ catch(err) {
41
+ console.log(err);
42
+ }
43
+ }
44
+ }
45
+
46
+ let playerAttributes = new PlayerAttributes();
47
+ </script>
package/views/debug.ejs DELETED
@@ -1,15 +0,0 @@
1
- <div class="content invisible" id="debug">
2
- <h3>user</h3>
3
- <% if (user) { %>
4
- <p><%= user._id %></p>
5
- <% } else { %>
6
- <p>Please login to view user.</p>
7
- <% } %>
8
-
9
- <h3>inv</h3>
10
- <% if (user) { %>
11
- <p><%= inventory.items.length %> items</p>
12
- <% } else { %>
13
- <p>Please login to view inventory.</p>
14
- <% } %>
15
- </div>