I am developing a MEAN project with angular 4.1.0.
On my localhost, everything works without errors. When I deploy to the server however, retrieving a user with more than 8 question-answer pairs causes a net::ERR_CONNECTION_CLOSED error on the xhr request that angular's http module fires.
The digital ocean droplet I am hosting on uses an nginx reverse proxy and uses a letsencrypt SSL certificate. I have tried restarting server, nginx service, node.js etc.
Please point me in possible directions.
The chrome dev tool network tab screenshots
-
After logging in to an account where there are only 7 question answer pairs
-
Then, after going to mlab.com and manually adding another question answer pair to same account and then refreshing the page (notice the number of questions in now 8)
-
Finally, after logging in and out of the same account (notice the xhr request to qapairs?jwt=ey... returned a failed status)
The source files:
Backend
server.js
const express = require('express')
const path = require('path')
const morgan = require('morgan') // logger
const bodyParser = require('body-parser')
require('dotenv').config()
const apiRoutes = require('./routes/api.routes')
const userRoutes = require('./routes/user.routes')
const app = express()
app.set('port', (process.env.PORT || 3001))
app.use(require('helmet')())
app.use(require('compression')({ threshold: 0 }))
app.use('/', express.static(path.join(__dirname, '/../../dist'), { maxAge: 86400000 }))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(morgan('dev'))
const { mongoose } = require('./imports')
if (process.env.NODE_ENV !== 'production') {
mongoose.connect('mongodb://localhost:27017/quiz')
} else {
mongoose.connect(`mongodb://${process.env.DB_USER}:${process.env.DB_PASS}@ds127731.mlab.com:27731/splearn`)
}
const db = mongoose.connection
db.on('error', console.error.bind(console, 'connection error:'))
db.once('open', function () {
console.log('Connected to MongoDB')
// Routes
app.use('/api', apiRoutes)
app.use('/user', userRoutes)
// all other routes are handled by Angular
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, '/../../dist/index.html'))
})
app.listen(app.get('port'), function () {
console.log('Angular 2 Full Stack listening on port ' + app.get('port'))
})
})
module.exports = app
api.routes.js
const express = require('express')
const router = express.Router()
const jwt = require('jsonwebtoken')
const { levenshtein, attemptAnswer } = require('../helpers')
const User = require('../models/user.model')
router.use('/', (req, res, next) => {
jwt.verify(req.query.jwt, process.env.JWT_SECRET, (err, decoded) => {
if (err) return res.status(401).json({title: 'Not authenticated', error: 'Not authenticated. Please log in.'})
req.body.decodedUserID = jwt.decode(req.query.jwt).user
next()
})
})
// select all
router.get('/qapairs', function (req, res) {
User.findById(req.body.decodedUserID)
.then((foundUser) => {
res.json(foundUser.qapairs)
})
.catch((err) => res.status(500).json({title: 'An error occured', error: err}))
})
/* etc etc etc */
module.exports = router
user.model.js
const { mongoose } = require('../imports')
const questionAnswerPairSchema = require('./questionAnswerPair.schema')
var uniqueValidator = require('mongoose-unique-validator')
const userSchema = mongoose.Schema({
firstName: String,
email: {type: String, required: true, unique: true},
password: { type: String, required: true },
qapairs: [questionAnswerPairSchema]
})
userSchema.plugin(uniqueValidator)
const User = mongoose.model('User', userSchema)
module.exports = User
questionAnswerPair.schema.js
const { mongoose } = require('../imports')
const { nextAssessmentDate } = require('../helpers')
const questionAnswerPairSchema = mongoose.Schema({
question: String,
correctAnswers: [String],
wrongAnswers: [String],
explanation: String,
createdAt: { type: Date, default: new Date() },
correctAttempts: { type: Number, default: 0 },
wrongAttempts: { type: Number, default: 0 },
netCorrectAttempts: { type: Number, default: 0 },
lastAssessed: Date,
toBeAssessedNext: Date
})
questionAnswerPairSchema.pre('save', function (next) {
this.toBeAssessedNext = nextAssessmentDate(this.netCorrectAttempts, this.lastAssessed || this.createdAt)
next()
})
module.exports = questionAnswerPairSchema
Frontend
qa-pairs.service.ts
The error is being caught here in the getQAPairs
function. Being passed to it is a ProgressEvent
object with a 'type' property of 'error'.
import { Injectable, EventEmitter } from '@angular/core';
import { Http, Headers } from "@angular/http";
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import QAPair, { IQAPair } from './qa-pair.model'
import { ErrorsService } from "app/errors/errors.service";
@Injectable()
export class QaPairsService {
public qapairs: QAPair[] = []
public qapairsToBeAssessed = []
public qapairsChanged: EventEmitter<any[]> = new EventEmitter()
private headers = new Headers({'Content-Type': 'application/json'})
private qapairsUrl = 'api/qapairs'
constructor(private http: Http, private errorsService: ErrorsService) {}
getQAPairs () {
const jwt = localStorage.getItem('jwt') ? `?jwt=${localStorage.getItem('jwt')}` : ''
return this.http.get(this.qapairsUrl + jwt)
.map(response => {
this.qapairs = response.json().map((qapair: IQAPair) => new QAPair(qapair))
this.qapairsChanged.emit(this.qapairs) // So that the nav bar gets wind of the qapairs, after initializing without being logged in.
return this.qapairs
})
.catch(
(error: any) => {
error = error.json()
this.errorsService.handleError(error)
return Observable.throw(error)
}
)
}
/* etc, etc, etc */
}
qa-pairs.component.ts
import { Component, OnDestroy, OnInit } from '@angular/core';
import 'rxjs/add/operator/takeUntil';
import { Subject } from 'rxjs/Subject';
import { QaPairsService } from "app/qa-pairs/qa-pairs.service";
import { filterToBeAssessed } from "app/helpers";
@Component({
selector: 'app-qa-pairs',
templateUrl: './qa-pairs.component.html',
styleUrls: ['./qa-pairs.component.scss']
})
export class QaPairsComponent implements OnDestroy, OnInit {
public ngUnsubscribe: Subject<void> = new Subject<void>();
public qapairs = []
public qapairsToBeAssessed = []
public currentQapair
public isEditMode: Boolean
public shouldShowNewQAModal: Boolean = false
constructor(private qaService: QaPairsService) { }
ngOnInit() {
this.qaService.getQAPairs()
.takeUntil(this.ngUnsubscribe)
.subscribe((qapairsArr) => {
this.qapairs = qapairsArr
this.qapairsToBeAssessed = filterToBeAssessed(qapairsArr)
})
this.qaService.qapairsChanged
.takeUntil(this.ngUnsubscribe)
.subscribe((updatedQAPairs) => {
this.qapairs = updatedQAPairs
this.qapairsToBeAssessed = filterToBeAssessed(updatedQAPairs)
})
}
/*etc, etc, etc*/
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}
via T M
No comments:
Post a Comment