Skip to content

Commit

Permalink
Merge pull request #412 from webkom/develop
Browse files Browse the repository at this point in the history
Merge develop -> master
  • Loading branch information
SmithPeder authored Feb 9, 2021
2 parents d0b77e2 + 2570b15 commit 1613398
Show file tree
Hide file tree
Showing 93 changed files with 5,131 additions and 715 deletions.
14 changes: 7 additions & 7 deletions .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: default

steps:
- name: setup
image: node:13
image: node:14
when:
event:
- push
Expand All @@ -16,7 +16,7 @@ steps:
- clone

- name: lint
image: node:13
image: node:14
when:
event:
- push
Expand All @@ -27,7 +27,7 @@ steps:
- yarn lint

- name: test
image: node:13
image: node:14
when:
event:
- push
Expand All @@ -38,7 +38,7 @@ steps:
- MONGO_URL=mongodb://mongodb:27017/vote-test REDIS_URL=redis yarn mocha

- name: coverage
image: node:13
image: node:14
when:
event:
- pull_request
Expand All @@ -54,7 +54,7 @@ steps:
COVERALLS_SERVICE_NUMBER: ${DRONE_BUILD_NUMBER}

- name: build
image: node:13
image: node:14
when:
event:
- push
Expand Down Expand Up @@ -114,7 +114,7 @@ steps:

services:
- name: mongodb
image: mongo:3.6
image: mongo:4.4

- name: redis
image: redis:latest
image: redis:6.0
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"eslint:recommended",
"prettier"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 8,
"sourceType": "module"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ public/*.js
public/main.css
screenshots
*.mp4
client_secret.json
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
build/Release
node_modules
*.map
dist
public
app/stv/stv.js
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:13
FROM node:14-slim
MAINTAINER Abakus Webkom <webkom@abakus.no>

# Create app directory
Expand Down
63 changes: 40 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,41 @@
# 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)

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

![vote](http://i.imgur.com/DU1CXQx.png)
![vote](https://i.imgur.com/DIMAJfj.png)

## Setup

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.
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.

```bash
$ git clone git@github.com:webkom/vote.git
$ cd vote

# Start MongoDB and Redis, both required for development and production
$ docker-compose up -d

# Install all dependencies
$ yarn

# Create a user via the CLI. You are promted to select usertype.
$ ./bin/users create-user <username> <cardKey>
$ yarn && yarn start
```

## Usage

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.
#### Users

An example deployment can be found in the `./deployment` folder.
Initially you will need to create a moderator and or admin user in order to login

```bash
# Create a user via the CLI. You are prompted to select usertype.
$ ./bin/users create-user <username> <cardKey>
```

#### Card-readers

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.

### Development

> Check docs for the environment variable `ETHEREAL` if you intend to develop email related features
```bash
$ yarn start
```
Expand All @@ -46,25 +48,40 @@ $ yarn start
- `REDIS_URL`
- Hostname of the redis server
- `default`: `localhost`
- `LOGO_SRC` _(optional)_
- Url to the main logo on all pages
- `ICON_SRC` _(optional)_
- Url to the main icon on all pages
- `default`: `/static/images/Abakule.jpg`
- `COOKIE_SECRET`
- **IMPORTANT** to change this to a secret value in production!!
- `default`: in dev: `localsecret`, otherwise empty

See `app.js` for the rest
- `FRONTEND_URL`
- The site where vote should run
- `defualt`: `http://localhost:3000`
- `FROM`
- The name we send mail from
- `default`: `Abakus`
- `FROM_MAIL`
- The email we send mail from
- `default`: `admin@abakus.no`
- `SMTP_URL`
- An SMTP connection string of the form `smtps://username:password@smtp.example.com/?pool=true`
- `GOOGLE_AUTH`
- A base64 encoded string with the json data of a service account that can send mail.

See `app.js` and `env.js` for the rest

### Production

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

## Using the card-readers

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.
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**.
Please check that the USB card reader is connected. When prompted for permissions, please select the card reader (CP210x).

### Serial permissions (Linux)
Expand Down Expand Up @@ -105,7 +122,7 @@ $ yarn test
$ HEADLESS=true yarn test
```

## Vote occasion
## Vote Occasion

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.

Expand Down
5 changes: 3 additions & 2 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mongoose.connect(app.get('mongourl'), {
useCreateIndex: true,
useUnifiedTopology: true,
useNewUrlParser: true,
useFindAndModify: true,
});

raven.config(env.RAVEN_DSN).install();
Expand Down Expand Up @@ -66,10 +67,10 @@ app.use(
resave: false,
})
);
const { LOGO_SRC, NODE_ENV } = env;
const { ICON_SRC, NODE_ENV } = env;
app.locals = Object.assign({}, app.locals, {
NODE_ENV,
LOGO_SRC,
ICON_SRC,
});

/* istanbul ignore if */
Expand Down
58 changes: 46 additions & 12 deletions app/controllers/election.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const Bluebird = require('bluebird');
const mongoose = require('mongoose');
const Election = require('../models/election');
const User = require('../models/user');
const Alternative = require('../models/alternative');
const errors = require('../errors');
const app = require('../../app');
Expand All @@ -17,20 +18,46 @@ exports.load = (req, res, next, electionId) =>
});

exports.retrieveActive = (req, res) =>
Election.findOne({ active: true })
.where('hasVotedUsers.user')
.ne(req.user.id)
Election.findOne({
active: true,
hasVotedUsers: { $ne: req.user._id },
})
.select('-hasVotedUsers')
.populate('alternatives')
.exec()
.then((election) => {
res.status(200).json(election);
.then(async (election) => {
const { user, query } = req;
// There is no active election (that the user has not voted on)
if (!election) {
throw new errors.NotFoundError('election');
}

// User is active, return the election
if (user.active) {
return res.status(200).json(election);
}

// Active election but wrong or not access code submitted,
// so we return 403 which prompts a access code input field.
if (
!query.accessCode ||
election.accessCode !== Number(query.accessCode)
) {
throw new errors.AccessCodeError();
}

// Active election and the inactive user has the correct access code.
// Therefore we activate the users account, and return the elction.
await User.findByIdAndUpdate({ _id: user._id }, { active: true });
return res.status(200).json(election);
});

exports.create = (req, res) =>
Election.create({
title: req.body.title,
description: req.body.description,
seats: req.body.seats,
useStrict: req.body.useStrict,
})
.then((election) => {
const alternatives = req.body.alternatives;
Expand Down Expand Up @@ -69,20 +96,27 @@ function setElectionStatus(req, res, active) {
return req.election.save();
}

exports.activate = (req, res) =>
setElectionStatus(req, res, true).then((election) => {
exports.activate = async (req, res) => {
const otherActiveElection = await Election.findOne({ active: true });
if (otherActiveElection) {
throw new errors.AlreadyActiveElectionError();
}
return setElectionStatus(req, res, true).then((election) => {
const io = app.get('io');
io.emit('election');
return res.status(200).json(election);
});
};

exports.deactivate = (req, res) =>
setElectionStatus(req, res, false).then((election) =>
res.status(200).json(election)
);
setElectionStatus(req, res, false).then((election) => {
const io = app.get('io');
io.emit('election');
res.status(200).json(election);
});

exports.sumVotes = (req, res) =>
req.election.sumVotes().then((alternatives) => res.json(alternatives));
exports.elect = (req, res) =>
req.election.elect().then((result) => res.json(result));

exports.delete = (req, res) => {
if (req.election.active) {
Expand Down
31 changes: 31 additions & 0 deletions app/controllers/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const Register = require('../models/register');
const ObjectId = require('mongoose').Types.ObjectId;
const errors = require('../errors');

exports.list = (req, res) =>
Register.find().then((register) => res.json(register));

exports.delete = async (req, res) => {
if (!ObjectId.isValid(req.params.registerId)) {
throw new errors.ValidationError('Invalid ObjectID');
}

const register = await Register.findOne({
_id: req.params.registerId,
});

if (!register) {
throw new errors.NotFoundError('register');
}

if (!register.user) {
throw new errors.NoAssociatedUserError();
}

return register.remove().then(() =>
res.status(200).json({
message: 'Register and associated user deleted.',
status: 200,
})
);
};
Loading

0 comments on commit 1613398

Please sign in to comment.