Skip to content

Commit 1613398

Browse files
authored
Merge pull request #412 from webkom/develop
Merge develop -> master
2 parents d0b77e2 + 2570b15 commit 1613398

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+5131
-715
lines changed

.drone.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ name: default
55

66
steps:
77
- name: setup
8-
image: node:13
8+
image: node:14
99
when:
1010
event:
1111
- push
@@ -16,7 +16,7 @@ steps:
1616
- clone
1717

1818
- name: lint
19-
image: node:13
19+
image: node:14
2020
when:
2121
event:
2222
- push
@@ -27,7 +27,7 @@ steps:
2727
- yarn lint
2828

2929
- name: test
30-
image: node:13
30+
image: node:14
3131
when:
3232
event:
3333
- push
@@ -38,7 +38,7 @@ steps:
3838
- MONGO_URL=mongodb://mongodb:27017/vote-test REDIS_URL=redis yarn mocha
3939

4040
- name: coverage
41-
image: node:13
41+
image: node:14
4242
when:
4343
event:
4444
- pull_request
@@ -54,7 +54,7 @@ steps:
5454
COVERALLS_SERVICE_NUMBER: ${DRONE_BUILD_NUMBER}
5555

5656
- name: build
57-
image: node:13
57+
image: node:14
5858
when:
5959
event:
6060
- push
@@ -114,7 +114,7 @@ steps:
114114

115115
services:
116116
- name: mongodb
117-
image: mongo:3.6
117+
image: mongo:4.4
118118

119119
- name: redis
120-
image: redis:latest
120+
image: redis:6.0

.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"eslint:recommended",
44
"prettier"
55
],
6+
"parser": "babel-eslint",
67
"parserOptions": {
78
"ecmaVersion": 8,
89
"sourceType": "module"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ public/*.js
4040
public/main.css
4141
screenshots
4242
*.mp4
43+
client_secret.json

.prettierignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
build/Release
2+
node_modules
3+
*.map
4+
dist
5+
public
6+
app/stv/stv.js

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:13
1+
FROM node:14-slim
22
MAINTAINER Abakus Webkom <[email protected]>
33

44
# Create app directory

README.md

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,41 @@
11
# vote [![DroneCI](https://ci.webkom.dev/api/badges/webkom/vote/status.svg?branch=master)](https://ci.webkom.dev/webkom/vote) [![Coverage Status](https://coveralls.io/repos/github/webkom/vote/badge.svg?branch=master)](https://coveralls.io/github/webkom/vote?branch=master) [![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/webkom/vote)](https://libraries.io/github/webkom/vote#dependencies) ![GitHub](https://img.shields.io/github/license/webkom/vote)
22

3-
> vote optimizes the election
3+
> Digital voting system for Abakus' generral assembly
44
5-
Digital voting system for Abakus' general assembly, built using the MEAN-stack (mongoDB, Express, AngularJS, Node.js).
6-
Relevant (Norwegian) blog post: http://webkom.abakus.no/vote/
5+
Irrelevant [blog post](http://webkom.abakus.no/vote/)
76

8-
![vote](http://i.imgur.com/DU1CXQx.png)
7+
![vote](https://i.imgur.com/DIMAJfj.png)
98

109
## Setup
1110

12-
vote assumes you have a MongoDB-server running on `mongodb://localhost:27017/vote` and a redis-server running as `localhost:6379`. To
13-
change the URL, export `MONGO_URL` and `REDIS_URL` as an environment variable.
11+
vote assumes you have a MongoDB-server running on `mongodb://localhost:27017/vote` and a redis-server running as `localhost:6379`. To change the URL, export `MONGO_URL` and `REDIS_URL` as an environment variable.
1412

1513
```bash
16-
$ git clone [email protected]:webkom/vote.git
17-
$ cd vote
18-
1914
# Start MongoDB and Redis, both required for development and production
2015
$ docker-compose up -d
21-
2216
# Install all dependencies
23-
$ yarn
24-
25-
# Create a user via the CLI. You are promted to select usertype.
26-
$ ./bin/users create-user <username> <cardKey>
17+
$ yarn && yarn start
2718
```
2819

2920
## Usage
3021

31-
vote uses a RFID-reader to register and activate/deactivate users. This is done to make sure that only people that are at the location can vote. The RFID-reader needs to be connected to the computer that is logged in to the moderator panel.
22+
#### Users
3223

33-
An example deployment can be found in the `./deployment` folder.
24+
Initially you will need to create a moderator and or admin user in order to login
25+
26+
```bash
27+
# Create a user via the CLI. You are prompted to select usertype.
28+
$ ./bin/users create-user <username> <cardKey>
29+
```
30+
31+
#### Card-readers
32+
33+
vote uses a RFID-reader to register and activate/deactivate users. This is done to make sure that only people that are at the location can vote. The RFID-reader needs to be connected to the computer that is logged in to the moderator panel. See section about using the card reader further down this readme.
3434

3535
### Development
3636

37+
> Check docs for the environment variable `ETHEREAL` if you intend to develop email related features
38+
3739
```bash
3840
$ yarn start
3941
```
@@ -46,25 +48,40 @@ $ yarn start
4648
- `REDIS_URL`
4749
- Hostname of the redis server
4850
- `default`: `localhost`
49-
- `LOGO_SRC` _(optional)_
50-
- Url to the main logo on all pages
51+
- `ICON_SRC` _(optional)_
52+
- Url to the main icon on all pages
5153
- `default`: `/static/images/Abakule.jpg`
5254
- `COOKIE_SECRET`
5355
- **IMPORTANT** to change this to a secret value in production!!
5456
- `default`: in dev: `localsecret`, otherwise empty
55-
56-
See `app.js` for the rest
57+
- `FRONTEND_URL`
58+
- The site where vote should run
59+
- `defualt`: `http://localhost:3000`
60+
- `FROM`
61+
- The name we send mail from
62+
- `default`: `Abakus`
63+
- `FROM_MAIL`
64+
- The email we send mail from
65+
- `default`: `[email protected]`
66+
- `SMTP_URL`
67+
- An SMTP connection string of the form `smtps://username:[email protected]/?pool=true`
68+
- `GOOGLE_AUTH`
69+
- A base64 encoded string with the json data of a service account that can send mail.
70+
71+
See `app.js` and `env.js` for the rest
5772

5873
### Production
5974

75+
> For a production deployment example, see [deployment](./deployment/README.md) in the `deployment` folder
76+
6077
```bash
6178
$ yarn build
62-
$ LOGO_SRC=https://my-domain.tld/logo.png NODE_ENV=production yarn start
79+
$ ICON_SRC=https://some-domain/image.png NODE_ENV=production GOOGLE_AUTH=base64encoding yarn start
6380
```
6481

6582
## Using the card-readers
6683

67-
Make sure you have enabled Experimental Web Platform features and are using Google Chrome. Experimental features can be enabled by navigating to: chrome://flags/#enable-experimental-web-platform-features.
84+
Make sure you have enabled Experimental Web Platform features and are using Google Chrome. Experimental features can be enabled by navigating to: **chrome://flags/#enable-experimental-web-platform-features**.
6885
Please check that the USB card reader is connected. When prompted for permissions, please select the card reader (CP210x).
6986

7087
### Serial permissions (Linux)
@@ -105,7 +122,7 @@ $ yarn test
105122
$ HEADLESS=true yarn test
106123
```
107124

108-
## Vote occasion
125+
## Vote Occasion
109126

110127
We have a list of every occasion vote has been used. If you or your organization use vote for your event we would love if you made a PR where you append your event to the list.
111128

app.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mongoose.connect(app.get('mongourl'), {
2626
useCreateIndex: true,
2727
useUnifiedTopology: true,
2828
useNewUrlParser: true,
29+
useFindAndModify: true,
2930
});
3031

3132
raven.config(env.RAVEN_DSN).install();
@@ -66,10 +67,10 @@ app.use(
6667
resave: false,
6768
})
6869
);
69-
const { LOGO_SRC, NODE_ENV } = env;
70+
const { ICON_SRC, NODE_ENV } = env;
7071
app.locals = Object.assign({}, app.locals, {
7172
NODE_ENV,
72-
LOGO_SRC,
73+
ICON_SRC,
7374
});
7475

7576
/* istanbul ignore if */

app/controllers/election.js

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const Bluebird = require('bluebird');
22
const mongoose = require('mongoose');
33
const Election = require('../models/election');
4+
const User = require('../models/user');
45
const Alternative = require('../models/alternative');
56
const errors = require('../errors');
67
const app = require('../../app');
@@ -17,20 +18,46 @@ exports.load = (req, res, next, electionId) =>
1718
});
1819

1920
exports.retrieveActive = (req, res) =>
20-
Election.findOne({ active: true })
21-
.where('hasVotedUsers.user')
22-
.ne(req.user.id)
21+
Election.findOne({
22+
active: true,
23+
hasVotedUsers: { $ne: req.user._id },
24+
})
2325
.select('-hasVotedUsers')
2426
.populate('alternatives')
2527
.exec()
26-
.then((election) => {
27-
res.status(200).json(election);
28+
.then(async (election) => {
29+
const { user, query } = req;
30+
// There is no active election (that the user has not voted on)
31+
if (!election) {
32+
throw new errors.NotFoundError('election');
33+
}
34+
35+
// User is active, return the election
36+
if (user.active) {
37+
return res.status(200).json(election);
38+
}
39+
40+
// Active election but wrong or not access code submitted,
41+
// so we return 403 which prompts a access code input field.
42+
if (
43+
!query.accessCode ||
44+
election.accessCode !== Number(query.accessCode)
45+
) {
46+
throw new errors.AccessCodeError();
47+
}
48+
49+
// Active election and the inactive user has the correct access code.
50+
// Therefore we activate the users account, and return the elction.
51+
await User.findByIdAndUpdate({ _id: user._id }, { active: true });
52+
return res.status(200).json(election);
2853
});
2954

3055
exports.create = (req, res) =>
3156
Election.create({
3257
title: req.body.title,
3358
description: req.body.description,
59+
seats: req.body.seats,
60+
useStrict: req.body.useStrict,
3461
})
3562
.then((election) => {
3663
const alternatives = req.body.alternatives;
@@ -69,20 +96,27 @@ function setElectionStatus(req, res, active) {
6996
return req.election.save();
7097
}
7198

72-
exports.activate = (req, res) =>
73-
setElectionStatus(req, res, true).then((election) => {
99+
exports.activate = async (req, res) => {
100+
const otherActiveElection = await Election.findOne({ active: true });
101+
if (otherActiveElection) {
102+
throw new errors.AlreadyActiveElectionError();
103+
}
104+
return setElectionStatus(req, res, true).then((election) => {
74105
const io = app.get('io');
75106
io.emit('election');
76107
return res.status(200).json(election);
77108
});
109+
};
78110

79111
exports.deactivate = (req, res) =>
80-
setElectionStatus(req, res, false).then((election) =>
81-
res.status(200).json(election)
82-
);
112+
setElectionStatus(req, res, false).then((election) => {
113+
const io = app.get('io');
114+
io.emit('election');
115+
res.status(200).json(election);
116+
});
83117

84-
exports.sumVotes = (req, res) =>
85-
req.election.sumVotes().then((alternatives) => res.json(alternatives));
118+
exports.elect = (req, res) =>
119+
req.election.elect().then((result) => res.json(result));
86120

87121
exports.delete = (req, res) => {
88122
if (req.election.active) {

app/controllers/register.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const Register = require('../models/register');
2+
const ObjectId = require('mongoose').Types.ObjectId;
3+
const errors = require('../errors');
4+
5+
exports.list = (req, res) =>
6+
Register.find().then((register) => res.json(register));
7+
8+
exports.delete = async (req, res) => {
9+
if (!ObjectId.isValid(req.params.registerId)) {
10+
throw new errors.ValidationError('Invalid ObjectID');
11+
}
12+
13+
const register = await Register.findOne({
14+
_id: req.params.registerId,
15+
});
16+
17+
if (!register) {
18+
throw new errors.NotFoundError('register');
19+
}
20+
21+
if (!register.user) {
22+
throw new errors.NoAssociatedUserError();
23+
}
24+
25+
return register.remove().then(() =>
26+
res.status(200).json({
27+
message: 'Register and associated user deleted.',
28+
status: 200,
29+
})
30+
);
31+
};

0 commit comments

Comments
 (0)