diff --git a/6-backend-dev/4-exercise-tracker/.gitattributes b/6-backend-dev/4-exercise-tracker/.gitattributes new file mode 100644 index 0000000..4891ee0 --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/.gitattributes @@ -0,0 +1,3 @@ +# Tell Linguist to exclude HTML files to help Replit language detection. +*.html linguist-vendored +*.css linguist-vendored \ No newline at end of file diff --git a/6-backend-dev/4-exercise-tracker/.gitignore b/6-backend-dev/4-exercise-tracker/.gitignore new file mode 100644 index 0000000..97aca2e --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules \ No newline at end of file diff --git a/6-backend-dev/4-exercise-tracker/.prettierignore b/6-backend-dev/4-exercise-tracker/.prettierignore new file mode 100644 index 0000000..9736424 --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/.prettierignore @@ -0,0 +1 @@ +.replit \ No newline at end of file diff --git a/6-backend-dev/4-exercise-tracker/.replit b/6-backend-dev/4-exercise-tracker/.replit new file mode 100644 index 0000000..f2f3406 --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/.replit @@ -0,0 +1,10 @@ +run = "npm start" +entrypoint = "index.js" + +[packager] +language = "nodejs" + + [packager.features] + packageSearch = true + guessImports = true + enabledForHosting = false \ No newline at end of file diff --git a/6-backend-dev/4-exercise-tracker/README.md b/6-backend-dev/4-exercise-tracker/README.md new file mode 100644 index 0000000..8ef1a56 --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/README.md @@ -0,0 +1,3 @@ +# Exercise Tracker + +This is the boilerplate for the Exercise Tracker project. Instructions for building your project can be found at https://www.freecodecamp.org/learn/apis-and-microservices/apis-and-microservices-projects/exercise-tracker diff --git a/6-backend-dev/4-exercise-tracker/index.js b/6-backend-dev/4-exercise-tracker/index.js new file mode 100644 index 0000000..859395f --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/index.js @@ -0,0 +1,83 @@ +const express = require('express') +const app = express() +const cors = require('cors') +require('dotenv').config() + +const users = []; + +function User(username, id) { + this.username = username; + this._id = id; + this.log = []; +} + +users.push(new User('testUser', '0')) + +app.use(cors()) +app.use(express.static('public')) +app.use(express.json()) // for parsing application/json +app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded +app.get('/', (req, res) => { + res.sendFile(__dirname + '/views/index.html') +}); + +app.route('/api/users') + .get((req, res) => { + res.json(users) + }) + .post((req, res) => { + const id = users.length + const username = req.body.username + users.push(new User(username, id)) + res.json({username: username, _id: id}) + }) + +app.post('/api/users/:_id/exercises', (req, res) => { + const description = req.body.description; + const duration = +req.body.duration; + const date = req.body.date; + const id = req.params._id; + const user = users[id] + if (!duration || !description){ + return res.json({error: 'missing parameters'}) + } else { + let dateString; + if (date) { + dateString = new Date(date).toDateString() + } else { + dateString = new Date().toDateString() + } + + user.log.push({description: description, duration: duration, date: dateString}); + user.count = user.log.length; + res.json({ + username: user.username, + description: description, + duration: duration, + date: dateString, + _id: user._id + }) + } +}) + +app.route('/api/users/:_id/logs') + .get((req, res) => { + const id = req.params._id; + const limit = Number(req.query.limit) || 0; + const response = users[id]; + let newlog = response.log + if (req.query.from && req.query.to) { + const from = new Date(req.query.from).getTime(); + const to = new Date(req.query.to).getTime(); + newlog = newlog.filter(item => from <= new Date(item.date).getTime() <= to)} + + if (limit > 0){ + newlog = newlog.slice(0, limit) + } + response.log = newlog + res.json(response) + }) + +const listener = app.listen(process.env.PORT || 3000, () => { + console.log('Your app is listening on port ' + listener.address().port) +}) diff --git a/6-backend-dev/4-exercise-tracker/package-lock.json b/6-backend-dev/4-exercise-tracker/package-lock.json new file mode 100644 index 0000000..89c50c2 --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/package-lock.json @@ -0,0 +1,749 @@ +{ + "name": "fcc-exercise-tracker", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "fcc-exercise-tracker", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "cors": "^2.8.5", + "dotenv": "^8.2.0", + "express": "^4.16.4" + } + }, + "node_modules/accepts": { + "version": "1.3.7", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.19.0", + "license": "MIT", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytes": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "8.2.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.17.1", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors": { + "version": "1.7.2", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.44.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.27", + "license": "MIT", + "dependencies": { + "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "license": "MIT", + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "license": "MIT", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.17.1", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.14.1", + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/statuses": { + "version": "1.5.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + }, + "dependencies": { + "accepts": { + "version": "1.3.7", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1" + }, + "body-parser": { + "version": "1.19.0", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0" + }, + "content-disposition": { + "version": "0.5.3", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4" + }, + "cookie": { + "version": "0.4.0" + }, + "cookie-signature": { + "version": "1.0.6" + }, + "cors": { + "version": "2.8.5", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "debug": { + "version": "2.6.9", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2" + }, + "destroy": { + "version": "1.0.4" + }, + "dotenv": { + "version": "8.2.0" + }, + "ee-first": { + "version": "1.1.1" + }, + "encodeurl": { + "version": "1.0.2" + }, + "escape-html": { + "version": "1.0.3" + }, + "etag": { + "version": "1.8.1" + }, + "express": { + "version": "4.17.1", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2" + }, + "fresh": { + "version": "0.5.2" + }, + "http-errors": { + "version": "1.7.2", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3" + }, + "ipaddr.js": { + "version": "1.9.1" + }, + "media-typer": { + "version": "0.3.0" + }, + "merge-descriptors": { + "version": "1.0.1" + }, + "methods": { + "version": "1.1.2" + }, + "mime": { + "version": "1.6.0" + }, + "mime-db": { + "version": "1.44.0" + }, + "mime-types": { + "version": "2.1.27", + "requires": { + "mime-db": "1.44.0" + } + }, + "ms": { + "version": "2.0.0" + }, + "negotiator": { + "version": "0.6.2" + }, + "object-assign": { + "version": "4.1.1" + }, + "on-finished": { + "version": "2.3.0", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3" + }, + "path-to-regexp": { + "version": "0.1.7" + }, + "proxy-addr": { + "version": "2.0.6", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0" + }, + "range-parser": { + "version": "1.2.1" + }, + "raw-body": { + "version": "2.4.0", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2" + }, + "safer-buffer": { + "version": "2.1.2" + }, + "send": { + "version": "0.17.1", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1" + } + } + }, + "serve-static": { + "version": "1.14.1", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1" + }, + "statuses": { + "version": "1.5.0" + }, + "toidentifier": { + "version": "1.0.0" + }, + "type-is": { + "version": "1.6.18", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0" + }, + "utils-merge": { + "version": "1.0.1" + }, + "vary": { + "version": "1.1.2" + } + } +} diff --git a/6-backend-dev/4-exercise-tracker/package.json b/6-backend-dev/4-exercise-tracker/package.json new file mode 100644 index 0000000..eb2d24b --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/package.json @@ -0,0 +1,22 @@ +{ + "name": "fcc-exercise-tracker", + "version": "0.1.0", + "description": "A REST API project, part of Free Code Camp's curriculum", + "main": "index.js", + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "dotenv": "^8.2.0", + "express": "^4.16.4", + "cors": "^2.8.5" + }, + "repository": { + "url": "https://github.com/freeCodeCamp/boilerplate-project-exercisetracker" + }, + "license": "MIT", + "keywords": [ + "node", + "express" + ] +} diff --git a/6-backend-dev/4-exercise-tracker/public/style.css b/6-backend-dev/4-exercise-tracker/public/style.css new file mode 100644 index 0000000..c5eb36b --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/public/style.css @@ -0,0 +1,59 @@ +* { + box-sizing: border-box; +} + +body { + font-family:sans-serif; + margin: 25px; + color: #222; + background-color: #f5f5f5; +} + +h1 { + font-weight: bold; + +} + +p { + max-width: 900px; +} + +form { + margin-bottom: 25px; + padding: 15px; + background-color: #87D37C; + display: inline-block; + width: 100%; + max-width: 340px; + border-radius: 5px; +} + +input { + display: block; + margin-bottom: 10px; + padding: 5px; + width: 100%; + border: 1px solid lightgrey; + border-radius: 3px; + font-size: 16px; +} + +input[type=submit] { + font-size: 16px; + border-radius: 3px; + background-color: #E4F1FE; + border: 1px solid grey; + box-shadow: 2px 2px #999; + cursor: pointer; +} + +input[type=submit]:hover { + background-color: #FFFEC4; +} + +code { + font-family: monospace; + font-size: 1.2em; + background-color: #FFFEC4; + padding: 2px; +} diff --git a/6-backend-dev/4-exercise-tracker/sample.env b/6-backend-dev/4-exercise-tracker/sample.env new file mode 100644 index 0000000..c0c68b1 --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/sample.env @@ -0,0 +1 @@ +PORT=3000 \ No newline at end of file diff --git a/6-backend-dev/4-exercise-tracker/views/index.html b/6-backend-dev/4-exercise-tracker/views/index.html new file mode 100644 index 0000000..00a7a42 --- /dev/null +++ b/6-backend-dev/4-exercise-tracker/views/index.html @@ -0,0 +1,46 @@ + + + + Exercise Tracker | freeCodeCamp + + + + + + +
+

Exercise tracker

+
+

Create a New User

+

POST /api/users

+ + +
+
+

Add exercises

+

POST /api/users/:_id/exercises

+ + + + + +
+

+ GET user's exercise log: + GET /api/users/:_id/logs?[from][&to][&limit] +

+

[ ] = optional

+

from, to = dates (yyyy-mm-dd); limit = number

+
+ + +