fixed duplicate views bug

This commit is contained in:
David Baldwynn 2017-09-29 17:33:55 -07:00
parent b3915b7f90
commit f6bb23a3bf
19 changed files with 2281 additions and 1753 deletions

View File

@ -18,6 +18,8 @@ ENV PORT 3000
RUN apt-get update -q \
&& apt-get install -yqq \
curl \
ant \
default-jdk \
git \
gcc \
make \
@ -47,8 +49,7 @@ WORKDIR /opt/tellform
# when the local package.json file changes.
# Add npm package.json
COPY package.json /opt/tellform/package.json
RUN npm install --production
RUN mv ./node_modules ./node_modules.tmp && mv ./node_modules.tmp ./node_modules && npm install
RUN npm install
# Add bower.json
COPY bower.json /opt/tellform/bower.json
@ -62,5 +63,8 @@ COPY ./server.js /opt/tellform/server.js
COPY ./.env /opt/tellform/.env
COPY ./scripts/create_admin.js /opt/tellform/scripts/create_admin.js
# Run TellForm server
CMD npm start
# Run Development TellForm server
COPY ./dev_entrypoint.sh /dev_entrypoint.sh
RUN chmod +x /dev_entrypoint.sh
ENTRYPOINT ["/dev_entrypoint.sh"]

View File

@ -19,6 +19,8 @@ ENV BASE_URL tellform.com
RUN apt-get update -q \
&& apt-get install -yqq \
curl \
ant \
default-jdk \
git \
gcc \
make \

View File

@ -0,0 +1,174 @@
TellForm Installation Instructions
==================================
## Table of Contents
- [Local Deployment with Docker](#local-deployment-with-docker)
- [AWS AMI Deployment](#aws-ami-deployment)
## Local deployment with Docker
### Prerequisites
Make you sure have the following packages and versions on your machine:
```
"node": ">=6.11.2"
"npm": ">=3.3.6"
"bower": ">=1.8.0"
"grunt-cli": ">=1.2.0"
"grunt": ">=0.4.5"
"docker": ">=17.06.0-ce"
"docker-compose": ">=1.14.0"
```
### Install dependencies
```
$ npm install
```
### Prepare .env file:
Create `.env` file at project root folder. Fill in `MAILER_SERVICE_PROVIDER`, `MAILER_EMAIL_ID`, `MAILER_PASSWORD` and `MAILER_FROM`.
```
APP_NAME=TellForm
BASE_URL=localhost:3000
PORT=3000
DB_PORT_27017_TCP_ADDR=tellform-mongo
REDIS_DB_PORT_6379_TCP_ADDR=tellform-redis
MAILER_SERVICE_PROVIDER=<TO-FILL-IN>
MAILER_EMAIL_ID=<TO-FILL-IN>
MAILER_PASSWORD=<TO-FILL-IN>
MAILER_FROM=<TO-FILL-IN>
SIGNUP_DISABLED=false
SUBDOMAINS_DISABLED=true
DISABLE_CLUSTER_MODE=true
```
### Build docker image
```
$ docker-compose build
```
### Run docker containers with docker-compose
Create and start mongo & redis docker container:
```
$ docker-compose up
```
Your application should run on port 3000 or the port you specified in your .env file, so in your browser just go to [http://localhost:3000](http://localhost:3000)
## AWS AMI Deployment
### Prerequisites
Instructions here are tested on an Amazon Linux AMI. First, set up your fresh new AMI by setting the environment variables:
```
$ sudo vim /etc/environment
LANG=en_US.utf-8
LC_ALL=en_US.utf-8
```
Next, update and install build tools:
```
$ sudo yum update -y
$ sudo yum groupinstall "Development Tools" -y
```
### Install docker
```
$ sudo yum install -y docker
$ sudo service docker start
```
To ensure docker can be run without `sudo` each time:
```
$ sudo usermod -a -G docker ec2-user
$ logout
```
SSH back in, and test that `docker info` runs successfully.
### Install docker-compose
```
$ sudo -i
$ curl -L https://github.com/docker/compose/releases/download/1.15.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ logout
```
### Clone our repo
```
$ git clone https://github.com/datagovsg/formsg.git
```
### Prepare .env file
The `.env` file for remote deployment (or production) is slightly different from that of local deployment.
Create `.env` file at project root folder. Similarly, fill in `MAILER_SERVICE_PROVIDER`, `MAILER_EMAIL_ID`, `MAILER_PASSWORD` and `MAILER_FROM`. Note that now you have to fill in the public IP of your instance in `BASE_URL`.
```
APP_NAME=FormSG
APP_DESC=
APP_KEYWORDS=
NODE_ENV=production
BASE_URL=<PUBLIC IP OF YOUR INSTANCE>
PORT=4545
DB_PORT_27017_TCP_ADDR=<PRIVATE IP OF YOUR MONGODB HOST>
REDIS_DB_PORT_6379_TCP_ADDR=formsg-redis
username=formsg_admin
MAILER_SERVICE_PROVIDER=<TO-FILL-IN>
MAILER_EMAIL_ID=<TO-FILL-IN>
MAILER_PASSWORD=<TO-FILL-IN>
MAILER_FROM=<TO-FILL-IN>
SIGNUP_DISABLED=false
SUBDOMAINS_DISABLED=true
DISABLE_CLUSTER_MODE=true
RAVEN_DSN=
PRERENDER_TOKEN=
COVERALLS_REPO_TOKEN=
```
### Install npm, bower and grunt
```
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.32.0/install.sh | bash
$ . ~/.nvm/nvm.sh
$ nvm install 6.11.2
$ npm install -g bower
$ npm install -g grunt-cli
$ npm install grunt
```
### Install dependencies
```
$ npm install --production
```
### Build docker image
```
$ docker-compose -f docker-compose-production.yml build
```
### Run docker containers
```
$ docker run -d -p 27017:27017 -v /data/db:/data/db --name formsg-mongo mongo
$ docker-compose -f docker-compose-production.yml up
```
Note that unlike dev, mongo container is run separately from compose. Hence `docker-compose down` does not take down the mongo container each time. Your application should run on the default port 80, so in your browser just go to your public IP.
## Support
Please contact David Baldwynn (team@tellform.com) for any details.

View File

@ -40,6 +40,9 @@ var ButtonSchema = new Schema({
});
var VisitorDataSchema = new Schema({
socketId: {
type: String
},
referrer: {
type: String
},

View File

@ -12,14 +12,14 @@ module.exports = function (io, socket) {
var visitorsData = {};
var saveVisitorData = function (data, cb){
Form.findById(data.formId, function(err, form) {
if (err) {
console.log(err);
console.error(err);
throw new Error(errorHandler.getErrorMessage(err));
}
var newVisitor = {
socketId: data.socketId,
referrer: data.referrer,
lastActiveField: data.lastActiveField,
timeElapsed: data.timeElapsed,
@ -37,35 +37,37 @@ module.exports = function (io, socket) {
throw new Error(errorHandler.getErrorMessage(formSaveErr));
}
delete visitorsData[socket.id];
if(cb){
return cb();
}
});
});
};
io.on('connection', function(current_socket) {
// a user has visited our page - add them to the visitorsData object
current_socket.on('form-visitor-data', function(data) {
current_socket.id = data.formId;
visitorsData[current_socket.id] = data;
visitorsData[current_socket.id].isSaved = false;
if (data.isSubmitted) {
saveVisitorData(data, function () {
visitorsData[current_socket.id].isSaved = true;
});
}
visitorsData[current_socket.id] = data;
visitorsData[current_socket.id].socketId = current_socket.id;
visitorsData[current_socket.id].isSaved = false;
if (data.isSubmitted && !data.isSaved) {
visitorsData[current_socket.id].isSaved = true;
saveVisitorData(data, function() {
console.log("\n\n\n\n\ncurrent_socket.id: "+current_socket.id);
current_socket.disconnect(true);
});
}
});
current_socket.on('disconnect', function() {
var data = visitorsData[current_socket.id];
if(data && !data.isSubmitted && !data.isSaved) {
data.isSaved = true;
saveVisitorData(data);
saveVisitorData(data, function() {
delete visitorsData[current_socket.id];
});
} else {
delete visitorsData[current_socket.id];
}
});
});

1
config/env/all.js vendored
View File

@ -61,7 +61,6 @@ module.exports = {
maxAge: 24 * 60 * 60 * 1000 // 24 hours
// To set the cookie in a specific domain uncomment the following
// setting:
//domain: process.env.COOKIE_SESSION_URL || process.env.BASE_URL || '.tellform.com'
},
/*

10
dev_entrypoint.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
line=$(head -n 1 /etc/hosts)
echo "$line tellform.dev $(hostname)" >> /etc/hosts
# Restart sendmail
service sendmail restart
# Run Server
npm start

View File

@ -1,22 +1,22 @@
version: "3"
services:
tellform:
image: tellform/production:no_subdomains
build:
context: ./
dockerfile: dockerfile
image: tellform
ports:
- 3000:3000
- 8080:80
links:
- redis:redis-db
- mongo:db
environment:
- BASE_URL=localhost
- DB_1_PORT_27017_TCP_ADDR=mongo
- REDIS_DB_PORT_6379_TCP_ADDR=redis
redis:
image: redis:alpine
- tellform-redis:redis-db
- tellform-mongo:db
env_file:
- .env
tellform-redis:
image: redis
ports:
- 6379
mongo:
image: mongo:latest
tellform-mongo:
image: mongo
ports:
- 27017

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -221,7 +221,7 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
});
});
});
}else {
} else {
setTimeout(function() {
if (document.querySelectorAll('.activeField .focusOn')[0]) {
//FIXME: DAVID: Figure out how to set focus without scroll movement in HTML Dom
@ -232,7 +232,10 @@ angular.module('view-form').directive('submitFormDirective', ['$http', 'TimeCoun
});
}
SendVisitorData.send($scope.myform, getActiveField(), TimeCounter.getTimeElapsed());
//Only send analytics data if form has not been submitted
if(!$scope.myform.submitted){
SendVisitorData.send($scope.myform, getActiveField(), TimeCounter.getTimeElapsed());
}
};
$rootScope.nextField = $scope.nextField = function(){

View File

@ -4,10 +4,22 @@
// Create the Socket.io wrapper service
function Socket($timeout, $window) {
var service;
var service = {
socket: null
};
// Connect to Socket.io server
function connect(url) {
// Connect to TellForm Socket.io server
function connect() {
var url = '';
if($window.socketUrl && $window.socketPort){
url = window.location.protocol + '//' + $window.socketUrl + ':' + $window.socketPort;
} else if ($window.socketUrl){
url = window.location.protocol + '//' + $window.socketUrl;
} else if ($window.socketPort){
url = window.location.protocol + '//' + window.location.hostname + ':' + $window.socketPort;
} else {
url = window.location.protocol + '//' + window.location.hostname;
}
service.socket = io(url, {'transports': ['websocket', 'polling']});
}
@ -36,6 +48,8 @@
}
}
connect();
service = {
connect: connect,
emit: emit,
@ -44,19 +58,6 @@
socket: null
};
console.log($window.socketUrl);
var url = '';
if($window.socketUrl && $window.socketPort){
url = window.location.protocol + '//' + $window.socketUrl + ':' + $window.socketPort;
} else if ($window.socketUrl){
url = window.location.protocol + '//' + $window.socketUrl;
} else if ($window.socketPort){
url = window.location.protocol + '//' + window.location.hostname + ':' + $window.socketPort;
} else {
url = window.location.protocol + '//' + window.location.hostname;
}
connect(url);
return service;
}

View File

@ -12,16 +12,13 @@ angular.module('view-form').config(['$stateProvider',
Forms: 'Forms',
myForm: function (Forms, $q, $state, $stateParams) {
var deferred = $q.defer();
console.log(Forms.get({formId: $stateParams.formId}).$promise);
return Forms.get({formId: $stateParams.formId}).$promise.then(function(data) {
console.log(data);
return data;
}, function(reason) {
console.log(reason);
console.error(reason);
$state.go('unauthorizedFormAccess');
return deferred.reject({redirectTo: 'unauthorizedFormAccess'});
});
//return Forms.get({formId: $stateParams.formId}).$promise;
}
},
controller: 'SubmitFormController',

View File

@ -55,6 +55,8 @@
country: geoData.country_name
}
};
console.log('sending form-visitor-data');
Socket.emit('form-visitor-data', visitorData);
}
@ -63,6 +65,11 @@
if (!Socket.socket) {
Socket.connect();
}
Socket.on('disconnect', function(){
console.log("reconnected to socket");
Socket.connect();
});
}
var service = {

View File

@ -1,55 +0,0 @@
(function () {
'use strict';
// Create the Socket.io wrapper service
angular
.module('core')
.factory('Socket', Socket);
Socket.$inject = ['$timeout', '$window'];
function Socket($timeout, $window) {
// Connect to Socket.io server
function connect(url) {
service.socket = io(url, {'transports': ['websocket', 'polling']});
}
// Wrap the Socket.io 'emit' method
function emit(eventName, data) {
if (service.socket) {
service.socket.emit(eventName, data);
}
}
// Wrap the Socket.io 'on' method
function on(eventName, callback) {
if (service.socket) {
service.socket.on(eventName, function (data) {
$timeout(function () {
callback(data);
});
});
}
}
// Wrap the Socket.io 'removeListener' method
function removeListener(eventName) {
if (service.socket) {
service.socket.removeListener(eventName);
}
}
var service = {
connect: connect,
emit: emit,
on: on,
removeListener: removeListener,
socket: null
};
connect(window.location.protocol+'//'+window.location.hostname);
return service;
}
}());

View File

@ -123,7 +123,10 @@
<h5>{{ 'BACKGROUND_COLOR' | translate }}</h5>
</div>
<div class="field-input col-sm-6">
<input class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.backgroundColor" ng-style="{ 'background-color': myform.design.colors.backgroundColor }"/>
<input class="form-control" colorpicker="hex" type="text"
ng-model="myform.design.colors.backgroundColor"
ng-pattern="/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/"
ng-style="{ 'background-color': myform.design.colors.backgroundColor }"/>
</div>
</div>
@ -133,7 +136,7 @@
</div>
<div class="field-input col-sm-6">
<input class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.questionColor" ng-style="{ 'background-color': myform.design.colors.questionColor }"/>
<input class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.questionColor" ng-pattern="/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/"ng-style="{ 'background-color': myform.design.colors.questionColor }"/>
</div>
</div>
@ -143,7 +146,7 @@
</div>
<div class="field-input col-sm-6">
<input class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.answerColor" ng-style="{ 'background-color': myform.design.colors.answerColor }"/>
<input class="form-control" colorpicker="hex" type="text" ng-model="myform.design.colors.answerColor" ng-pattern="/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/" ng-style="{ 'background-color': myform.design.colors.answerColor }"/>
</div>
</div>
@ -155,6 +158,7 @@
<div class="field-input col-sm-6">
<input class="form-control" colorpicker="hex" type="text"
ng-model="myform.design.colors.buttonColor"
ng-pattern="/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/"
ng-style="{ 'background-color': myform.design.colors.buttonColor }"/>
</div>
</div>
@ -166,6 +170,7 @@
<div class="field-input col-sm-6">
<input class="form-control" colorpicker="hex" type="text"
ng-model="myform.design.colors.buttonTextColor"
ng-pattern="/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/"
ng-style="{ 'background-color': myform.design.colors.buttonTextColor }"/>
</div>
</div>