diff --git a/server/Auth.js b/server/Auth.js new file mode 100644 index 0000000..54a15b9 --- /dev/null +++ b/server/Auth.js @@ -0,0 +1,37 @@ +import { randomBytes } from 'crypto' +import jwt from 'jsonwebtoken' +import db from './Database.js' + +export default { + async createAccount(name) { + console.log("Creating user"); + if (db.userExists(name)) { + console.log("User exists"); + return { + error: "The name alredy occupied" + } + } + + const pass = randomBytes(16).toString('hex') + const token = jwt.sign({ + name: name, + record: 0, + rank: 0 + }, process.env.secret) + + await db.addUser({ + name, + pass, + record: 0, + rank: 0, + }) + + return { + name, + pass, + record: 0, + rank: 0, + token + } + } +} diff --git a/server/Database.js b/server/Database.js new file mode 100644 index 0000000..e31c597 --- /dev/null +++ b/server/Database.js @@ -0,0 +1,33 @@ +import { join, dirname } from 'path' +import { Low, JSONFile } from 'lowdb' +import { fileURLToPath } from 'url' + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +class Database { + constructor() { + const file = join(__dirname, 'storage', 'db.json') + const adapter = new JSONFile(file) + this.db = new Low(adapter) + } + + async read() { + await this.db.read(); + this.db.data = this.db.data || { + users: [], + records: [] + } + } + + userExists(name) { + return this.db.data.users.find(user => user.name == name) !== undefined + } + + async addUser(user) { + this.db.data.users.push(user) + await this.db.write() + return user + } +} + +export default new Database diff --git a/server/package-lock.json b/server/package-lock.json index 8e44029..4e7b124 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -15,7 +15,8 @@ "@babel/preset-env": "^7.15.6", "dotenv": "^10.0.0", "express": "^4.17.1", - "express-jwt": "^6.1.0" + "jsonwebtoken": "^8.5.1", + "lowdb": "^3.0.0" }, "devDependencies": { "nodemon": "^2.0.13" @@ -1766,11 +1767,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "node_modules/async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, "node_modules/babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -2521,25 +2517,6 @@ "node": ">= 0.10.0" } }, - "node_modules/express-jwt": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-6.1.0.tgz", - "integrity": "sha512-mmSR52Ps1FMeGwchroHzEJONWhsobd5KHVVKBffYiUEpZh9iK9wI9ZWkmzY5ROwWQtJLNGHV/VUMLh2M208s4Q==", - "dependencies": { - "async": "^1.5.0", - "express-unless": "^1.0.0", - "jsonwebtoken": "^8.1.0", - "lodash.set": "^4.0.0" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/express-unless": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-1.0.0.tgz", - "integrity": "sha512-zXSSClWBPfcSYjg0hcQNompkFN/MxQQ53eyrzm9BYgik2ut2I7PxAf2foVqBRMYCwWaZx/aWodi+uk76npdSAw==" - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3424,10 +3401,19 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, - "node_modules/lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + "node_modules/lowdb": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-3.0.0.tgz", + "integrity": "sha512-9KZRulmIcU8fZuWiaM0d5e2/nPnrFyXkeXVpqT+MJS+vgbgOf1EbtvgQmba8HwUFgDl1oeZR6XqEJnkJmQdKmg==", + "dependencies": { + "steno": "^2.1.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } }, "node_modules/lowercase-keys": { "version": "1.0.1", @@ -4266,6 +4252,17 @@ "node": ">= 0.6" } }, + "node_modules/steno": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/steno/-/steno-2.1.0.tgz", + "integrity": "sha512-mauOsiaqTNGFkWqIfwcm3y/fq+qKKaIWf1vf3ocOuTdco9XoHCO2AGF1gFYXuZFSWuP38Q8LBHBGJv2KnJSXyA==", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -5853,11 +5850,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, "babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -6458,22 +6450,6 @@ } } }, - "express-jwt": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-6.1.0.tgz", - "integrity": "sha512-mmSR52Ps1FMeGwchroHzEJONWhsobd5KHVVKBffYiUEpZh9iK9wI9ZWkmzY5ROwWQtJLNGHV/VUMLh2M208s4Q==", - "requires": { - "async": "^1.5.0", - "express-unless": "^1.0.0", - "jsonwebtoken": "^8.1.0", - "lodash.set": "^4.0.0" - } - }, - "express-unless": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-1.0.0.tgz", - "integrity": "sha512-zXSSClWBPfcSYjg0hcQNompkFN/MxQQ53eyrzm9BYgik2ut2I7PxAf2foVqBRMYCwWaZx/aWodi+uk76npdSAw==" - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -7110,10 +7086,13 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + "lowdb": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-3.0.0.tgz", + "integrity": "sha512-9KZRulmIcU8fZuWiaM0d5e2/nPnrFyXkeXVpqT+MJS+vgbgOf1EbtvgQmba8HwUFgDl1oeZR6XqEJnkJmQdKmg==", + "requires": { + "steno": "^2.1.0" + } }, "lowercase-keys": { "version": "1.0.1", @@ -7753,6 +7732,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "steno": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/steno/-/steno-2.1.0.tgz", + "integrity": "sha512-mauOsiaqTNGFkWqIfwcm3y/fq+qKKaIWf1vf3ocOuTdco9XoHCO2AGF1gFYXuZFSWuP38Q8LBHBGJv2KnJSXyA==" + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", diff --git a/server/package.json b/server/package.json index 7fd5957..61ce0cf 100644 --- a/server/package.json +++ b/server/package.json @@ -4,19 +4,21 @@ "description": "", "main": "index.js", "scripts": { - "start": "nodemon --exec babel-node server.js" + "start": "nodemon --ignore ./storage/ --exec babel-node server.js" }, + "type": "module", "keywords": [], "author": "Grzegorz Kupczyk", "license": "ISC", "dependencies": { - "dotenv": "^10.0.0", - "express": "^4.17.1", - "express-jwt": "^6.1.0", "@babel/cli": "^7.15.7", "@babel/core": "^7.15.5", "@babel/node": "^7.15.4", - "@babel/preset-env": "^7.15.6" + "@babel/preset-env": "^7.15.6", + "dotenv": "^10.0.0", + "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", + "lowdb": "^3.0.0" }, "devDependencies": { "nodemon": "^2.0.13" diff --git a/server/server.js b/server/server.js index 24739bc..b05399e 100644 --- a/server/server.js +++ b/server/server.js @@ -1,11 +1,61 @@ -import express from 'express' import 'dotenv/config' -const app = express() +import express from 'express' +import jwt from 'jsonwebtoken' +import auth from './Auth.js' +import db from './Database.js' + +const protect = (req, res, next) => { + const authHeader = req.headers.authorization; + + if (!authHeader) { + res.sendStatus(401) + } + + const token = authHeader.split(' ')[1]; + + jwt.verify(token, process.env.secret, (err, user) => { + if (err) + return res.sendStatus(403) + + req.user = user + next() + }) +} + +const app = express() +app.use(express.json()) + +app.get("/", (_, res) => { + res.json({ message: "Hello! API here" }) -app.get("/", (req, res) => { - res.send("hello") - console.log(process.env.secret); }); +app.get("/secured", protect, (req, res) => { + res.json({ message: "You can see it!", payload: req.user }) +}) + +app.post("/signup", async (req, res) => { + if (!req.body.name) + return res.status(400).json({ + error: "missing argument name" + }) + + const info = await auth.createAccount(req.body.name) + + if (info.error) + return res.status(400).json({ + error: info.error + }) + + res.json(info) +}) + +app.use(function (err, req, res, next) { + if (err.name === 'UnauthorizedError') { + res.status(401).send('invalid token...'); + } +}); + +await db.read() app.listen(3000)