├── .eslintignore ├── .babelrc ├── PULL_REQUEST_TEMPLATE.md ├── src ├── controllers │ ├── root.controller.js │ ├── timecode.controller.js │ ├── register.controller.js │ ├── time-entry.controller.js │ └── weeklyCounts.controller.js ├── config │ └── database.js ├── routes │ ├── routes.root.js │ ├── timecode.route.js │ ├── routes.register.js │ ├── routes.time-entry.js │ ├── routes.weeklyCounts.js │ └── routes.index.js ├── models │ ├── timecode.model.js │ ├── time-entry.model.js │ └── user.model.js └── server.js ├── .eslintrc.js ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── README.md ├── package.json ├── LICENSE ├── CONTRIBUTING.md ├── .gitignore └── CODE_OF_CONDUCT.md /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | - Fixed item 1 4 | - Added item 2 5 | - Removed item 3 6 | 7 | # Screenshots 8 | -------------------------------------------------------------------------------- /src/controllers/root.controller.js: -------------------------------------------------------------------------------- 1 | const rootController = async (req, res) => { 2 | res.send('Hello World!'); 3 | }; 4 | 5 | export default rootController; 6 | -------------------------------------------------------------------------------- /src/config/database.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | url: 'mongodb://localhost/test', // looks like mongodb://:@mongo.onmodulus.net:27017/Mikha4ot 3 | }; 4 | -------------------------------------------------------------------------------- /src/routes/routes.root.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import rootController from '../controllers/root.controller'; 3 | 4 | const root = express.Router(); 5 | 6 | root 7 | .get('/', rootController); 8 | 9 | export default root; 10 | -------------------------------------------------------------------------------- /src/routes/timecode.route.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import timeCodeController from '../controllers/timecode.controller'; 3 | 4 | const router = express.Router(); 5 | 6 | router.post('/', timeCodeController); 7 | 8 | export default router; 9 | -------------------------------------------------------------------------------- /src/models/timecode.model.js: -------------------------------------------------------------------------------- 1 | import { Schema, model } from 'mongoose'; 2 | 3 | const TimeCodeSchema = new Schema({ 4 | id: String, 5 | description: String, 6 | }); 7 | 8 | const TimeCode = model('Timecode', TimeCodeSchema); 9 | 10 | export default TimeCode; 11 | -------------------------------------------------------------------------------- /src/routes/routes.register.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import registerController from '../controllers/register.controller'; 3 | 4 | const register = express.Router(); 5 | 6 | register 7 | .post('/', registerController); 8 | 9 | export default register; 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2020: true, 5 | }, 6 | extends: [ 7 | 'airbnb-base', 8 | ], 9 | parserOptions: { 10 | ecmaVersion: 11, 11 | sourceType: 'module', 12 | }, 13 | rules: { 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /src/routes/routes.time-entry.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import timeEntryController from '../controllers/time-entry.controller'; 3 | 4 | const timeEntry = express.Router(); 5 | 6 | timeEntry 7 | .post('/', timeEntryController); 8 | 9 | export default timeEntry; 10 | -------------------------------------------------------------------------------- /src/routes/routes.weeklyCounts.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import weeklyCountsController from '../controllers/weeklyCounts.controller'; 3 | 4 | const weeklyCounts = express.Router(); 5 | 6 | weeklyCounts 7 | .get('/', weeklyCountsController); 8 | 9 | export default weeklyCounts; 10 | -------------------------------------------------------------------------------- /src/models/time-entry.model.js: -------------------------------------------------------------------------------- 1 | import { Schema, model } from 'mongoose'; 2 | 3 | const TimeEntrySchema = new Schema({ 4 | username: String, 5 | timeCode: String, 6 | timeIn: Date, 7 | timeOut: Date, 8 | }); 9 | 10 | const TimeEntry = model('TimeEntry', TimeEntrySchema); 11 | 12 | export default TimeEntry; 13 | -------------------------------------------------------------------------------- /src/models/user.model.js: -------------------------------------------------------------------------------- 1 | import { Schema, model } from 'mongoose'; 2 | import passportLocalMongoose from 'passport-local-mongoose'; 3 | 4 | const UserSchema = new Schema({ 5 | username: String, 6 | password: String, 7 | }); 8 | 9 | UserSchema.plugin(passportLocalMongoose); 10 | 11 | const User = model('User', UserSchema); 12 | 13 | export default User; 14 | -------------------------------------------------------------------------------- /src/controllers/timecode.controller.js: -------------------------------------------------------------------------------- 1 | import TimeCode from '../models/timecode.model'; 2 | 3 | const timeCodeController = async (req, res) => { 4 | try { 5 | const timeCode = new TimeCode({ id: req.body.id, description: req.body.description }); 6 | timeCode.save(); 7 | res.status(200); 8 | res.send('Ok'); 9 | } catch (e) { 10 | res.status(400); 11 | res.send(e.message); 12 | } 13 | }; 14 | 15 | export default timeCodeController; 16 | -------------------------------------------------------------------------------- /src/routes/routes.index.js: -------------------------------------------------------------------------------- 1 | import root from './routes.root'; 2 | import register from './routes.register'; 3 | import timeEntry from './routes.time-entry'; 4 | import timecode from './timecode.route'; 5 | import weeklyCounts from './routes.weeklyCounts'; 6 | 7 | module.exports = (app) => { 8 | app.use('/register/', register); 9 | app.use('/api/', root); 10 | app.use('/api/timeEntry', timeEntry); 11 | app.use('/api/timecodes/', timecode); 12 | app.use('/api/weeklyCounts', weeklyCounts); 13 | }; 14 | -------------------------------------------------------------------------------- /src/controllers/register.controller.js: -------------------------------------------------------------------------------- 1 | import User from '../models/user.model'; 2 | 3 | const registerController = async (req, res) => { 4 | // eslint-disable-next-line consistent-return 5 | User.register(new User({ username: req.body.email }), req.body.password, 6 | (err) => { 7 | if (err) { 8 | res.status(400); 9 | res.send(err.message); 10 | } else { 11 | res.status(200); 12 | res.send('Ok'); 13 | } 14 | }); 15 | }; 16 | 17 | export default registerController; 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import bodyParser from 'body-parser'; 3 | import mongoose from 'mongoose'; 4 | import passport from 'passport'; 5 | import LocalStrategy from 'passport-local'; 6 | 7 | import addRoutes from './routes/routes.index'; 8 | import dbConfig from './config/database'; 9 | import User from './models/user.model'; 10 | 11 | mongoose.connect(dbConfig.url); 12 | 13 | const app = express(); 14 | app.use(bodyParser()); 15 | app.use(passport.initialize()); 16 | app.use(passport.session()); 17 | 18 | passport.use(new LocalStrategy(User.authenticate())); 19 | passport.serializeUser(User.serializeUser()); 20 | passport.deserializeUser(User.deserializeUser()); 21 | 22 | addRoutes(app); 23 | 24 | app.listen(8000); 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web Clock Time Tracker Backend 2 | 3 | 4 | > A webapp that makes time tracking simple for associates. 5 | 6 | Find out precisely what time to clock out at the end of each day to get the hours you need. Easily track time spent on any time codes, and view the weekly summary to easily see what information you need to submit at the end of the week. 7 | 8 | This is the backend for [Web Clock Time Tracker](https://github.com/CompSciLauren/web-clock-time-tracker). 9 | 10 | ## Contributing 11 | 12 | Contributions are always welcome! Please see our [Contribution Guidelines](CONTRIBUTING.md). 13 | 14 | ## Available Scripts 15 | 16 | In the project directory, you can run: 17 | 18 | ### `npm start` 19 | 20 | Runs the app in the development mode.
21 | It will run on [http://localhost:8000](http://localhost:8000). 22 | 23 | You will see any lint errors in the console. 24 | -------------------------------------------------------------------------------- /src/controllers/time-entry.controller.js: -------------------------------------------------------------------------------- 1 | import TimeEntry from '../models/time-entry.model'; 2 | 3 | const timeEntryController = async (req, res) => { 4 | const { email } = req.body; 5 | const latest = await TimeEntry.findOne().sort('-timeIn').exec(); 6 | if (latest.timeOut) { 7 | try { 8 | await TimeEntry({ 9 | username: email, 10 | timeCode: req.body.timeCode, 11 | timeIn: req.body.timeIn, 12 | }).save(); 13 | res.status(200); 14 | res.send('ok'); 15 | } catch (err) { 16 | res.status(400); 17 | res.send(err.message); 18 | } 19 | } else { 20 | try { 21 | // eslint-disable-next-line no-underscore-dangle 22 | await TimeEntry.findByIdAndUpdate(latest._id, { timeOut: req.body.timeOut }); 23 | res.status(200); 24 | res.send('ok'); 25 | } catch (err) { 26 | res.status(400); 27 | res.send(err.message); 28 | } 29 | } 30 | }; 31 | 32 | export default timeEntryController; 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-mongoose-template", 3 | "version": "1.0.0", 4 | "description": "Boilerplate for a Node, Express, Mongoose api project", 5 | "main": "src/server.js", 6 | "scripts": { 7 | "start": "nodemon --exec babel-node src/server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "lint": "./node_modules/.bin/eslint src/" 10 | }, 11 | "author": "Eric McCullough ", 12 | "license": "MIT", 13 | "dependencies": { 14 | "body-parser": "^1.19.0", 15 | "express": "^4.17.1", 16 | "mongodb": "^3.5.9", 17 | "mongoose": "^5.9.22", 18 | "passport": "^0.4.1", 19 | "passport-local": "^1.0.0", 20 | "passport-local-mongoose": "^6.0.1" 21 | }, 22 | "devDependencies": { 23 | "@babel/core": "^7.10.4", 24 | "@babel/node": "^7.10.4", 25 | "@babel/preset-env": "^7.10.4", 26 | "eslint": "^7.4.0", 27 | "eslint-config-airbnb-base": "^14.2.0", 28 | "eslint-plugin-import": "^2.22.0", 29 | "nodemon": "^2.0.4" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Eric McCullough 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Please note that this project is released with a [Contributor Code of Conduct](CODE-OF-CONDUCT.md). By participating in this project you agree to abide by its terms. 4 | 5 | Contributions are _always_ welcome! Please ensure you follow the guidelines explained below. Thank you! 6 | 7 | ## Pull Requests 8 | 9 | - Keep pull request title short and simple, but descriptive. 10 | - Add a description explaining your changes if needed. 11 | - Check your spelling and grammar. 12 | 13 | ## Contributions 14 | 15 | We love all forms of friendly contributions! Feel free to make any changes that you think would be beneficial. You can [create a new issue](https://github.com/CompSciLauren/web-clock-time-tracker/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=) to discuss any proposals/feedback/thoughts/etc. 16 | 17 | ## How to Contribute 18 | 19 | To fix a bug or add an enhancement, follow these steps: 20 | 21 | - Fork the repo 22 | - Create a new branch (`git checkout -b improve-feature`) 23 | - Make the appropriate changes in the files 24 | - Commit your changes (`git commit -am 'Improve feature'`) 25 | - Push to the branch (`git push origin improve-feature`) 26 | - Create a Pull Request 27 | -------------------------------------------------------------------------------- /src/controllers/weeklyCounts.controller.js: -------------------------------------------------------------------------------- 1 | import TimeEntry from '../models/time-entry.model'; 2 | 3 | const weeklyCountsController = async (req, res) => { 4 | const now = new Date(); 5 | const currentDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()); 6 | const nextDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()); 7 | nextDay.setDate(nextDay.getDate() + 1); 8 | 9 | const data = {}; 10 | for (let i = now.getDay(); i >= 0; i -= 1) { 11 | // eslint-disable-next-line no-await-in-loop 12 | const results = await TimeEntry.find({ 13 | timeIn: { $gte: currentDay, $lt: nextDay }, 14 | username: req.query.username, 15 | }); 16 | results.forEach((el) => { 17 | if (el.timeIn && el.timeOut) { 18 | if (data[el.timeCode]) { 19 | data[el.timeCode][i] += (Math.abs(el.timeOut - el.timeIn) / 36e5); 20 | } else { 21 | data[el.timeCode] = new Array(7).fill(0); 22 | data[el.timeCode][i] = (Math.abs(el.timeOut - el.timeIn) / 36e5); 23 | } 24 | } 25 | }); 26 | currentDay.setDate(currentDay.getDate() - 1); 27 | nextDay.setDate(nextDay.getDate() - 1); 28 | } 29 | res.status(200); 30 | res.send(data); 31 | }; 32 | 33 | export default weeklyCountsController; 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project owner at compscilauren@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project owner is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | --------------------------------------------------------------------------------