diff --git a/app.js b/app.js index ce38cbf..e61d1be 100644 --- a/app.js +++ b/app.js @@ -4,11 +4,16 @@ var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); +var mongoose = require('mongoose'); var appRoutes = require('./routes/app'); +var messagesRoutes = require('./routes/messages.js'); +var usersRoutes = require('./routes/users.js'); var app = express(); +mongoose.connect('localhost:27017/angular4-node') + // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'hbs'); @@ -28,6 +33,8 @@ app.use(function (req, res, next) { next(); }); +app.use('/message', messagesRoutes); +app.use('/user', usersRoutes); app.use('/', appRoutes); // catch 404 and forward to error handler diff --git a/assets/app/app.component.html b/assets/app/app.component.html index ba7c290..44cf02d 100644 --- a/assets/app/app.component.html +++ b/assets/app/app.component.html @@ -1 +1,6 @@ -

Hello World!

\ No newline at end of file +
+ +
+ +
+ \ No newline at end of file diff --git a/assets/app/app.component.ts b/assets/app/app.component.ts index becfd00..19a4920 100644 --- a/assets/app/app.component.ts +++ b/assets/app/app.component.ts @@ -1,8 +1,11 @@ import { Component } from '@angular/core'; +import { Message } from './messages/message.model'; +import { MessageService } from './messages/message.service'; @Component({ selector: 'my-app', - templateUrl: './app.component.html' + templateUrl: './app.component.html', + providers: [MessageService] }) export class AppComponent { diff --git a/assets/app/app.module.ts b/assets/app/app.module.ts index dbef480..b5caed5 100644 --- a/assets/app/app.module.ts +++ b/assets/app/app.module.ts @@ -1,13 +1,45 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms' +import { HttpModule } from '@angular/http' import { AppComponent } from "./app.component"; +import { MessageComponent } from './messages/message.component'; +import { MessageListComponent } from './messages/message-list.component'; +import { MessageInputComponent } from './messages/message-input.component'; +import { MessagesComponent } from './messages/messages.component'; +import { AuthenticationComponent } from './auth/authentication.component'; +import { HeaderComponent } from './header.component'; +import { routing } from './app.routing'; +import { SigninComponent } from './auth/signin.component'; +import { SignupComponent } from './auth/signup.component'; +import { LogoutComponent } from './auth/logout.component'; +import { AuthService } from './auth/auth.service'; +import { ErrorComponent } from './errors/error.component'; +import { ErrorService } from './errors/error.service'; @NgModule({ declarations: [ - AppComponent + AppComponent, + MessageComponent, + MessageListComponent, + MessageInputComponent, + MessagesComponent, + AuthenticationComponent, + HeaderComponent, + SigninComponent, + SignupComponent, + LogoutComponent, + ErrorComponent ], - imports: [BrowserModule], + imports: [ + BrowserModule, + FormsModule, + routing, + ReactiveFormsModule, + HttpModule + ], + providers: [AuthService, ErrorService], bootstrap: [AppComponent] }) export class AppModule { diff --git a/assets/app/app.routing.ts b/assets/app/app.routing.ts new file mode 100644 index 0000000..c15f928 --- /dev/null +++ b/assets/app/app.routing.ts @@ -0,0 +1,12 @@ +import { Routes, RouterModule } from "@angular/router"; +import { MessagesComponent } from "./messages/messages.component"; +import { AuthenticationComponent } from "./auth/authentication.component"; +import { AUTH_ROUTES } from "./auth/auth.routes"; + +const APP_ROUTES: Routes = [ + { path: '', redirectTo: '/messages', pathMatch: 'full'}, + { path: 'messages', component: MessagesComponent }, + { path: 'auth', component: AuthenticationComponent, children: AUTH_ROUTES } +]; + +export const routing = RouterModule.forRoot(APP_ROUTES); \ No newline at end of file diff --git a/assets/app/auth/auth.routes.ts b/assets/app/auth/auth.routes.ts new file mode 100644 index 0000000..f6c5652 --- /dev/null +++ b/assets/app/auth/auth.routes.ts @@ -0,0 +1,11 @@ +import { Routes } from '@angular/router'; +import { SignupComponent } from './signup.component'; +import { SigninComponent } from './signin.component'; +import { LogoutComponent } from './logout.component'; + +export const AUTH_ROUTES: Routes = [ + { path: '', redirectTo: 'signin', pathMatch: 'full' }, + { path: 'signup', component: SignupComponent }, + { path: 'signin', component: SigninComponent }, + { path: 'logout', component: LogoutComponent } +]; \ No newline at end of file diff --git a/assets/app/auth/auth.service.ts b/assets/app/auth/auth.service.ts new file mode 100644 index 0000000..9f3b3c5 --- /dev/null +++ b/assets/app/auth/auth.service.ts @@ -0,0 +1,46 @@ +import { Injectable } from "@angular/core"; +import { Http, Headers, Response } from "@angular/http"; +import 'rxjs/Rx'; +import { Observable } from "rxjs/Observable"; + +import { User } from "./user.model"; +import { ErrorService } from "../errors/error.service"; + + +@Injectable() +export class AuthService { + constructor(private http: Http, private errorService: ErrorService) {} + + signup(user: User) { + const body = JSON.stringify(user); + const headers = new Headers({'Content-Type': 'application/json'}); + return this.http.post('http://localhost:3000/user', body, {headers: headers}) + .map((response: Response) => { + console.log(response); + return response.json(); + }) + .catch((error: Response) => { + this.errorService.handleError(error.json()); + return Observable.throw(error.json()); + }); + } + + signin(user: User) { + const body = JSON.stringify(user); + const headers = new Headers({'Content-Type': 'application/json'}); + return this.http.post('http://localhost:3000/user/signin', body, {headers: headers}) + .map((response: Response) => response.json()) + .catch((error: Response) => { + this.errorService.handleError(error.json()); + return Observable.throw(error.json()); + }); + } + + logout() { + localStorage.clear(); + } + + isLoggedIn() { + return localStorage.getItem('token') !== null; + } +} \ No newline at end of file diff --git a/assets/app/auth/authentication.component.ts b/assets/app/auth/authentication.component.ts new file mode 100644 index 0000000..e0930aa --- /dev/null +++ b/assets/app/auth/authentication.component.ts @@ -0,0 +1,29 @@ +import { Component } from "@angular/core"; +import { AuthService } from "./auth.service"; + +@Component({ + selector: 'app-authentication', + template: ` +
+ +
+
+ +
+ ` +}) +export class AuthenticationComponent { + + constructor(private authService: AuthService) {} + + isLoggedIn() { + return this.authService.isLoggedIn(); + } + +} \ No newline at end of file diff --git a/assets/app/auth/logout.component.ts b/assets/app/auth/logout.component.ts new file mode 100644 index 0000000..49b97e2 --- /dev/null +++ b/assets/app/auth/logout.component.ts @@ -0,0 +1,19 @@ +import { Component } from "@angular/core"; +import { AuthService } from "./auth.service"; +import { Router } from "@angular/router"; + +@Component({ + selector: 'app-logout', + template: ` +
+ +
+ ` +}) +export class LogoutComponent { + constructor(private authService: AuthService, private router: Router) {} + onLogout() { + this.authService.logout(); + this.router.navigate(['/auth', 'signin']); + } +} \ No newline at end of file diff --git a/assets/app/auth/signin.component.html b/assets/app/auth/signin.component.html new file mode 100644 index 0000000..00b086d --- /dev/null +++ b/assets/app/auth/signin.component.html @@ -0,0 +1,13 @@ +
+
+
+ + +
+
+ + +
+ +
+
\ No newline at end of file diff --git a/assets/app/auth/signin.component.ts b/assets/app/auth/signin.component.ts new file mode 100644 index 0000000..cabceb9 --- /dev/null +++ b/assets/app/auth/signin.component.ts @@ -0,0 +1,39 @@ +import { Component } from "@angular/core"; +import { FormGroup, FormControl, Validators } from "@angular/forms"; +import { User } from "./user.model"; +import { AuthService } from "./auth.service"; +import { Router } from "@angular/router"; + +@Component({ + selector: 'app-signin', + templateUrl: './signin.component.html' +}) +export class SigninComponent { + myForm: FormGroup; + + constructor(private authService: AuthService, private router: Router) {} + + onSubmit() { + const user = new User(this.myForm.value.email, this.myForm.value.password) + this.authService.signin(user) + .subscribe( + data => { + localStorage.setItem('token', data.token); + localStorage.setItem('userId', data.userId); + this.router.navigateByUrl('/'); + }, + error => console.error(error) + ) + this.myForm.reset(); + } + + ngOnInit() { + this.myForm = new FormGroup({ + email: new FormControl(null, [ + Validators.required, + Validators.email + ]), + password: new FormControl(null, Validators.required) + }); + } +} \ No newline at end of file diff --git a/assets/app/auth/signup.component.html b/assets/app/auth/signup.component.html new file mode 100644 index 0000000..8dd4366 --- /dev/null +++ b/assets/app/auth/signup.component.html @@ -0,0 +1,21 @@ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
\ No newline at end of file diff --git a/assets/app/auth/signup.component.ts b/assets/app/auth/signup.component.ts new file mode 100644 index 0000000..eb5c736 --- /dev/null +++ b/assets/app/auth/signup.component.ts @@ -0,0 +1,41 @@ +import { Component, OnInit } from "@angular/core"; +import { FormGroup, FormControl, Validators } from "@angular/forms"; +import { AuthService } from "./auth.service"; +import { User } from "./user.model"; + +@Component({ + selector: 'app-signup', + templateUrl: './signup.component.html' +}) +export class SignupComponent implements OnInit { + myForm: FormGroup; + + constructor(private authService: AuthService) {} + + onSubmit() { + const user = new User( + this.myForm.value.email, + this.myForm.value.password, + this.myForm.value.firstName, + this.myForm.value.lastName + ); + this.authService.signup(user) + .subscribe( + data => console.log(data), + error => console.error(error) + ); + this.myForm.reset(); + } + + ngOnInit() { + this.myForm = new FormGroup({ + firstName: new FormControl(null, Validators.required), + lastName: new FormControl(null, Validators.required), + email: new FormControl(null, [ + Validators.required, + Validators.email + ]), + password: new FormControl(null, Validators.required) + }); + } +} \ No newline at end of file diff --git a/assets/app/auth/user.model.ts b/assets/app/auth/user.model.ts new file mode 100644 index 0000000..c6e2a1f --- /dev/null +++ b/assets/app/auth/user.model.ts @@ -0,0 +1,6 @@ +export class User { + constructor(public email: string, + public password: string, + public firstName?: string, + public lastName?: string ) {} +} \ No newline at end of file diff --git a/assets/app/errors/error.component.html b/assets/app/errors/error.component.html new file mode 100644 index 0000000..a58858d --- /dev/null +++ b/assets/app/errors/error.component.html @@ -0,0 +1,19 @@ +
+ \ No newline at end of file diff --git a/assets/app/errors/error.component.ts b/assets/app/errors/error.component.ts new file mode 100644 index 0000000..dac5d71 --- /dev/null +++ b/assets/app/errors/error.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from "@angular/core"; +import { Error } from "./error.model"; +import { ErrorService } from "./error.service"; + +@Component({ + selector: 'app-error', + templateUrl: './error.component.html', + styles: [ + ` + .backdrop { + background-color: rgba(0,0,0,0.6); + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100vh; + } + ` + ] +}) +export class ErrorComponent implements OnInit { + + constructor(private errorService: ErrorService) {} + + ngOnInit() { + this.errorService.errorOcurred.subscribe( + (error: Error) => { + this.error = error; + this.display = 'block'; + } + ); + } + error: Error; + display = 'none'; + + onErrorHandled() { + this.display = 'none'; + } +} \ No newline at end of file diff --git a/assets/app/errors/error.model.ts b/assets/app/errors/error.model.ts new file mode 100644 index 0000000..36aeaf4 --- /dev/null +++ b/assets/app/errors/error.model.ts @@ -0,0 +1,3 @@ +export class Error { + constructor(public title: string, public message: string) {} +} \ No newline at end of file diff --git a/assets/app/errors/error.service.ts b/assets/app/errors/error.service.ts new file mode 100644 index 0000000..e1595ca --- /dev/null +++ b/assets/app/errors/error.service.ts @@ -0,0 +1,11 @@ +import { EventEmitter } from "@angular/core"; +import { Error } from "./error.model"; + +export class ErrorService { + errorOcurred = new EventEmitter(); + + handleError(error: any) { + const errorData = new Error(error.title, error.error.message); + this.errorOcurred.emit(errorData); + } +} \ No newline at end of file diff --git a/assets/app/header.component.ts b/assets/app/header.component.ts new file mode 100644 index 0000000..2940875 --- /dev/null +++ b/assets/app/header.component.ts @@ -0,0 +1,18 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: 'app-header', + template: ` +
+ +
+ ` +}) +export class HeaderComponent { + +} \ No newline at end of file diff --git a/assets/app/messages/message-input.component.html b/assets/app/messages/message-input.component.html new file mode 100644 index 0000000..470c083 --- /dev/null +++ b/assets/app/messages/message-input.component.html @@ -0,0 +1,16 @@ +
+
+
+ + +
+ + +
+
\ No newline at end of file diff --git a/assets/app/messages/message-input.component.ts b/assets/app/messages/message-input.component.ts new file mode 100644 index 0000000..19ef0fe --- /dev/null +++ b/assets/app/messages/message-input.component.ts @@ -0,0 +1,43 @@ +import { Component, OnInit } from "@angular/core"; +import { MessageService } from "./message.service"; +import { Message } from "./message.model"; +import { NgForm } from "@angular/forms"; + +@Component({ + selector: 'app-message-input', + templateUrl: './message-input.component.html' +}) +export class MessageInputComponent implements OnInit { + message: Message; + + constructor(private messageService: MessageService) {} + + ngOnInit() { + this.messageService.messageIsEdit.subscribe( + (message: Message) => this.message = message + ); + } + + onSubmit(form: NgForm) { + if(this.message) { + this.message.content = form.value.content; + this.messageService.updateMessage(this.message).subscribe( + result => console.log(result) + ); + this.message = null; + } else { + const message = new Message(form.value.content, 'Joe'); + this.messageService.addMessage(message) + .subscribe( + data => console.log(data), + error => console.error(error) + ); + } + form.resetForm(); + } + + onClear(form: NgForm) { + this.message = null; + form.reset(); + } +} \ No newline at end of file diff --git a/assets/app/messages/message-list.component.ts b/assets/app/messages/message-list.component.ts new file mode 100644 index 0000000..08b7727 --- /dev/null +++ b/assets/app/messages/message-list.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from "@angular/core"; +import { Message } from "./message.model"; +import { MessageService } from "./message.service"; + + +@Component({ + selector: 'app-message-list', + template: ` +
+ + +
+ ` +}) +export class MessageListComponent implements OnInit { + messages: Message[] = []; + + constructor(private messageService: MessageService) {} + + ngOnInit() { + this.messageService.getMessages() + .subscribe( + (messages: Message[]) => { + this.messages = messages; + } + ); + } +} \ No newline at end of file diff --git a/assets/app/messages/message.component.html b/assets/app/messages/message.component.html new file mode 100644 index 0000000..6088f05 --- /dev/null +++ b/assets/app/messages/message.component.html @@ -0,0 +1,15 @@ + +
+
+ {{ message.content }} +
+
+
+ {{ message.username }} +
+
+ Edit + Delete +
+
+
\ No newline at end of file diff --git a/assets/app/messages/message.component.ts b/assets/app/messages/message.component.ts new file mode 100644 index 0000000..fb14830 --- /dev/null +++ b/assets/app/messages/message.component.ts @@ -0,0 +1,45 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Message } from './message.model'; +import { MessageService } from './message.service'; + +@Component({ + selector: 'app-message', + templateUrl: './message.component.html', + styles: [ + ` + .author { + display: inline-block; + font-style: italic; + font-size: 12px; + width: 80%; + } + .config { + display: inline-block; + text-align: right; + font-size: 12px; + width: 19%; + } + ` + ] +}) +export class MessageComponent { + @Input() message: Message; + @Output() editClicked = new EventEmitter(); + + constructor(private messageService: MessageService) {} + + onEdit() { + this.messageService.editMessage(this.message); + } + + onDelete() { + this.messageService.deleteMessage(this.message) + .subscribe( + result => console.log(result) + ); + } + + belongsToUser() { + return localStorage.getItem('userId') == this.message.userId; + } +} \ No newline at end of file diff --git a/assets/app/messages/message.model.ts b/assets/app/messages/message.model.ts new file mode 100644 index 0000000..5904047 --- /dev/null +++ b/assets/app/messages/message.model.ts @@ -0,0 +1,13 @@ +export class Message { + content: String; + username: string; + messageId: string; + userId: string; + + constructor(content: string, username: string, messageId?: string, userId?: string) { + this.content = content; + this.username = username; + this.messageId = messageId; + this.userId = userId; + } +} \ No newline at end of file diff --git a/assets/app/messages/message.service.ts b/assets/app/messages/message.service.ts new file mode 100644 index 0000000..967a4e4 --- /dev/null +++ b/assets/app/messages/message.service.ts @@ -0,0 +1,90 @@ +import { Message } from './message.model' +import { Injectable, EventEmitter } from '@angular/core'; +import 'rxjs/Rx'; +import { Observable } from 'rxjs'; +import { Http, Headers, Response } from '@angular/http'; +import { ErrorService } from '../errors/error.service'; + +@Injectable() +export class MessageService { + private messages: Message[] = []; + messageIsEdit = new EventEmitter(); + + constructor(private http: Http, private errorService: ErrorService) {} + + addMessage(message: Message) { + const body = JSON.stringify(message); + const headers = new Headers({'Content-Type': 'application/json'}); + const token = localStorage.getItem('token') + ? '?token=' + localStorage.getItem('token') + : ''; + return this.http.post('http://localhost:3000/message' + token, body, {headers: headers}) + .map((response: Response) => { + const result = response.json(); + const message = new Message( + result.obj.content, + result.obj.user.firstName, + result.obj._id, + result.obj.user._id); + this.messages.push(message); + return message; + }) + .catch((error: Response) => { + this.errorService.handleError(error.json()); + return Observable.throw(error.json()); + }); + } + + editMessage(message: Message) { + this.messageIsEdit.emit(message); + } + + updateMessage(message: Message) { + const body = JSON.stringify(message); + const headers = new Headers({'Content-Type': 'application/json'}); + const token = localStorage.getItem('token') + ? '?token=' + localStorage.getItem('token') + : ''; + return this.http.patch('http://localhost:3000/message/' + message.messageId + token, body, {headers: headers}) + .map((response: Response) => response.json()) + .catch((error: Response) => { + this.errorService.handleError(error.json()); + return Observable.throw(error.json()); + }); + } + + getMessages() { + return this.http.get('http://localhost:3000/message') + .map((response: Response) => { + const messages = response.json().obj; + let transformedMessages: Message[] = []; + for (let message of messages) { + transformedMessages.push(new Message( + message.content, + message.user.firstName, + message._id, + message.user._id) + ); + } + this.messages = transformedMessages; + return transformedMessages + }) + .catch((error: Response) => { + this.errorService.handleError(error.json()); + return Observable.throw(error.json()); + }); + } + + deleteMessage(message: Message) { + this.messages.splice(this.messages.indexOf(message), 1); + const token = localStorage.getItem('token') + ? '?token=' + localStorage.getItem('token') + : ''; + return this.http.delete('http://localhost:3000/message/' + message.messageId + token) + .map((response: Response) => response.json()) + .catch((error: Response) => { + this.errorService.handleError(error.json()); + return Observable.throw(error.json()); + }); + } +} \ No newline at end of file diff --git a/assets/app/messages/messages.component.ts b/assets/app/messages/messages.component.ts new file mode 100644 index 0000000..206c2b9 --- /dev/null +++ b/assets/app/messages/messages.component.ts @@ -0,0 +1,16 @@ +import { Component } from "@angular/core"; + + +@Component({ + selector: 'app-messages', + template: ` +
+ +
+ +
+ ` +}) +export class MessagesComponent { + +} \ No newline at end of file diff --git a/models/message.js b/models/message.js new file mode 100644 index 0000000..4dd79c8 --- /dev/null +++ b/models/message.js @@ -0,0 +1,18 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; + +var User = require('./user'); + +var schema = new Schema({ + content: {type: String, required: true}, + user: {type: Schema.Types.ObjectId, ref: 'User'} +}); + +schema.post('remove', function(message) { + User.findById(message.user, function(err, user) { + user.messages.pull(message._id); + user.save(); + }); +}); + +module.exports = mongoose.model('Message', schema); \ No newline at end of file diff --git a/models/user.js b/models/user.js new file mode 100644 index 0000000..11487fb --- /dev/null +++ b/models/user.js @@ -0,0 +1,15 @@ +var mongoose = require('mongoose'); +var Schema = mongoose.Schema; +var mongooseUniqueValidator = require('mongoose-unique-validator'); + +var schema = new Schema({ + firstName: {type: String, required: true}, + lastName: {type: String, required: true}, + password: {type: String, required: true}, + email: {type: String, required: true}, + messages: [{type: Schema.Types.ObjectId, ref: 'Message'}] +}); + +schema.plugin(mongooseUniqueValidator); + +module.exports = mongoose.model('User', schema); \ No newline at end of file diff --git a/package.json b/package.json index 543a131..02bdd1d 100644 --- a/package.json +++ b/package.json @@ -20,17 +20,21 @@ "@angular/platform-server": "^4.0.0", "@angular/router": "^4.0.0", "@angular/upgrade": "^4.0.0", + "bcryptjs": "^2.4.3", "body-parser": "~1.15.2", "cookie-parser": "~1.4.3", + "core-js": "^2.4.1", "debug": "~2.2.0", "express": "~4.14.0", "hbs": "~3.1.0", + "jsonwebtoken": "^8.0.1", + "mongoose": "^4.11.11", + "mongoose-unique-validator": "^1.0.6", "morgan": "~1.6.1", "reflect-metadata": "^0.1.3", - "core-js": "^2.4.1", "rxjs": "^5.2.0", - "zone.js": "^0.8.5", - "serve-favicon": "~2.3.0" + "serve-favicon": "~2.3.0", + "zone.js": "^0.8.5" }, "devDependencies": { "@types/core-js": "0.9.36", diff --git a/routes/app.js b/routes/app.js index 5c71bdb..b346d11 100644 --- a/routes/app.js +++ b/routes/app.js @@ -5,13 +5,4 @@ router.get('/', function (req, res, next) { res.render('index'); }); -router.get('/message/:msg', function (req, res, next) { - res.render('node', {message: req.params.msg}); -}); - -router.post('/message', function(req, res, next) { - var message = req.body.message; - res.redirect('/message/' + message); -}); - module.exports = router; diff --git a/routes/messages.js b/routes/messages.js new file mode 100644 index 0000000..c129cbc --- /dev/null +++ b/routes/messages.js @@ -0,0 +1,142 @@ +var express = require('express'); +var router = express.Router(); +var jwt = require('jsonwebtoken'); + +var Message = require('../models/message'); +var User = require('../models/user'); + +router.get('/', function(req, res, next) { + Message.find() + .populate('user', 'firstName') + .exec(function(err, messages) { + if(err) { + return res.status(500).json({ + title: 'An error occurred', + error: err + }); + } + + res.status(201).json({ + message: 'Success!', + obj: messages + }); + }); +}); + +router.use('/', function(req, res, next) { + jwt.verify(req.query.token, 'secret', function(err, decoded) { + if(err) { + return res.status(401).json({ + title: 'Not Authenticated', + error: err + }); + } + next(); + }); +}); + +router.post('/', function(req, res, next) { + var decoded = jwt.decode(req.query.token); + User.findById(decoded.user._id, function(err, user) { + if(err) { + return res.status(500).json({ + title: 'An error occurred', + error: err + }); + } + var message = new Message({ + content: req.body.content, + user: user._id + }); + + message.save(function(err, result) { + if(err) { + return res.status(500).json({ + title: 'An error occurred', + error: err + }); + } + user.messages.push(result); + user.save(); + res.status(201).json({ + message: 'Saved message', + obj: result + }); + }); + }); +}); + +router.patch('/:id', function(req, res, next) { + var decoded = jwt.decode(req.query.token); + Message.findById(req.params.id, function(err, message) { + if(err) { + return res.status(500).json({ + title: 'An error occurred', + error: err + }); + } + if(!message) { + return res.status(500).json({ + title: 'No message found!', + error: {message: 'Message not found!'} + }); + } + if(message.user != decoded.user._id) { + return res.status(401).json({ + title: 'Not Authenticated', + error: {message: 'Users do not match'} + }); + } + message.content = req.body.content; + message.save(function(err, result) { + if(err) { + return res.status(500).json({ + title: 'An error occurred', + error: err + }); + } + res.status(200).json({ + message: 'Updated message', + obj: result + }); + }); + }); +}); + +router.delete('/:id', function(req, res, next) { + var decoded = jwt.decode(req.query.token); + Message.findById(req.params.id, function(err, message) { + if(err) { + return res.status(500).json({ + title: 'An error occurred', + error: err + }); + } + if(!message) { + return res.status(500).json({ + title: 'No message found!', + error: {message: 'Message not found!'} + }); + } + if(message.user != decoded.user._id) { + return res.status(401).json({ + title: 'Not Authenticated', + error: {message: 'Users do not match'} + }); + } + message.remove(function(err, result) { + if(err) { + return res.status(500).json({ + title: 'An error occurred', + error: err + }); + } + res.status(200).json({ + message: 'Deleted message', + obj: result + }); + }); + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/users.js b/routes/users.js new file mode 100644 index 0000000..8e8baf2 --- /dev/null +++ b/routes/users.js @@ -0,0 +1,60 @@ +var express = require('express'); +var router = express.Router(); +var bcrypt = require('bcryptjs'); +var jwt = require('jsonwebtoken'); + +var User = require('../models/user'); + +router.post('/', function(req, res, next) { + var user = new User({ + firstName: req.body.firstName, + lastName: req.body.lastName, + password: bcrypt.hashSync(req.body.password, 10), + email: req.body.email, + }); + user.save(function(err, result){ + if(err) { + return res.status(500).json({ + title: 'An error occurred', + errors: err + }); + } + res.status(201).json({ + message: 'User created', + obj: result + }); + }); +}); + +router.post('/signin', function(req, res, next) { + User.findOne({email: req.body.email}, function(err, user){ + if (err) { + return res.status(500).json({ + title: 'An error occurred', + errors: err + }); + } + if (!user) { + return res.status(401).json({ + title: 'Login failed', + errors: { message: 'Invalid login credentials'} + }); + } + if(!bcrypt.compareSync(req.body.password, user.password)) { + return res.status(401).json({ + title: 'Login failed', + errors: { message: 'Invalid login credentials'} + }); + } + + //gut + var token = jwt.sign({user: user}, 'secret', {expiresIn: 7200}); + res.status(200).json({ + message: 'Successfully logged in', + token: token, + userId: user._id + }); + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/views/node.hbs b/views/node.hbs deleted file mode 100644 index 1051e25..0000000 --- a/views/node.hbs +++ /dev/null @@ -1,6 +0,0 @@ -

A NodeJS View

-{{ message }} -
- - -
\ No newline at end of file