fixed duplicate views bug
This commit is contained in:
parent
b3915b7f90
commit
f6bb23a3bf
12
Dockerfile
12
Dockerfile
|
@ -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"]
|
|
@ -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 \
|
||||
|
|
|
@ -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.
|
|
@ -40,6 +40,9 @@ var ButtonSchema = new Schema({
|
|||
});
|
||||
|
||||
var VisitorDataSchema = new Schema({
|
||||
socketId: {
|
||||
type: String
|
||||
},
|
||||
referrer: {
|
||||
type: String
|
||||
},
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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'
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
|
@ -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
|
@ -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(){
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}());
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue