- Run
yarn install
- Create
serverless-config/secrets.json
file referenced below - Create
src/secrets.js
:
export const PRIMARY_AUDIENCE = '<CHANGE_ME>';
export const GOOGLE_AUDIENCES = [
'<CHANGE_ME>', // iOS Dev
'<CHANGE_ME>', // Android Dev
'<CHANGE_ME>', // iOS Prod
'<CHANGE_ME>', // Android Prod
'<CHANGE_ME>', // Web Dev
'<CHANGE_ME>', // Web Prod
'<CHANGE_ME>' // PWA Prod
];
-
Setup a deploy script
deploy.sh
that exports the appropriate tokens:SERVERLESS_ACCESS_KEY
,AWS_ACCESS_KEY_ID
, andAWS_SECRET_ACCESS_KEY
#!/bin/bash export SERVERLESS_ACCESS_KEY="<CHANGE_ME>" export AWS_ACCESS_KEY_ID="<CHANGE_ME>" export AWS_SECRET_ACCESS_KEY="<CHANGE_ME>"
-
Run
yarn deploy
-
Follow serverless directions for deploying
-
Need to create a
login.sh
like the following:#!/bin/bash export SERVERLESS_ACCESS_KEY="<CHANGE_ME>"
-
Need to create a
serverless-config/secrets.json
file of the following signature. The values do not have to be real for development purposes except for ones pertaining to the database connections.{ "SERVERLESS_ACCESS": "<CHANGE_ME>", "AUTH_SECRET": "<CHANGE_ME>", "AWS_ACCESS_KEY_ID": "<CHANGE_ME>", "AWS_SECRET_ACCESS_KEY": "<CHANGE_ME>", "MONGODB_URI": "<CHANGE_ME>" }
-
Run
yarn offline
MySQL
was used in v0.1 but for v1.0 we are moving to store all data inMongo
. Originally theMySQL
hosting was viaHeroku
but we hit free tier limitations and AWS RDS pricing is not perpetual unlikeMongo
.Mongo
's 512mb free tier should be plenty to store all the data for the app. Any references toSQL
are for legacy purposes but are going to be (or are) deprecated as of v1.0
This project uses serverless
in conjunction with Mongo
and Docker
. In order to test offline, you must have docker installed and our ktt-docker
repos cloned. You must also have mongo installed locally, we recommend using Homebrew for MacOS users.
Installation
- Clone the
ktt-docker-mongo
repos, with their default names, as sibling folders of thisktt-backend
repo. - Install Docker
Running
- Run the Docker Daemon
- Make sure none of our docker instances are running
- Run
yarn run test
Note: There are currently no tests to run
There is a pre-test hook that will spin up a Mongo docker instance. Both of these will require downloading the first time they are run which may be time intensive. The database will be empty once the hook is complete. The Mongo container is at 127.0.0.1:27017
assuming they complete successfully. The tests will be run with Jest
and then the post-test hook will spin down both docker instances and clear data.
usage | url |
---|---|
directory of allowed users | https://kappathetatau.org/assets/js/directory.json |
users | MongoDB Atlas |
events | MongoDB Atlas |
attendance | MongoDB Atlas |
points | MongoDB Atlas |
Account sign in and sign up are handled by the login
route. A user provides their credentials which are verified against Google OAuth2 as well as our official records (directory
data source). This information is then combined. Assuming the user passes both checks, the user is added to the database if not already present. A session token is generated by signing their email and the AUTH_SECRET
and is returned to the user. The user then only needs their session token to authenticate and does not need to validate against Google or our records.
- client signs in with google
- client sends google credentials to backend
- backend verifies email against directory data source
- backend verifies google token
- backend generates a JWT
- backend creates new user in database if not found
- returns authorized data and the generated token
Header authorization is handled by middleware. Step 3 is handled by each route that requires authorization. After authorization occurs, the data will be automatically attached to the route's event
object and is accessible. See users/update-one.js
for an example. The middleware will automatically assume authorization is required unless specified otherwise in the route's middyfy
call.
- client sends bearer token
- backend verifies token against email
- executes action and returns authorized data
Privileges are stored in official records in the directory
data source. These are transitioned into the user database upon account creation. Exec transitions happen once yearly. To propogate changes to the directory
, the auto/update-roles.js
route will verify each user in the database against the directory
and strip/add privileges and rolls where necessary.
- looked up during initial login from directory and saved to database
- auto-route that updates privileges and roles across the board
USER: {
_id: ID, // generated by MongoDB upon creation
// 5e77a2d370da5ae6e12bf99c
email: STRING, // <netid>@illinois.edu provided by Google OAuth
// netid@illinois.edu
familyName: STRING, // last name provided by Google OAuth
// Taylor-Chang
givenName: STRING, // given name provided by Google OAuth
// Jeffrey
semester: STRING, // pledge semester from official directory
// Fall 2018
type: STRING, // member type from official directory (currently only "B")
// B
role?: STRING, // executive role from official directory
// Web Chair
privileged?: BOOLEAN, // is a power-user (all executives are by default) from official directory
// true
firstYear: STRING, // the first year the user attended UIUC
// "2017"
gradYear: STRING, // graduation term provided by user
// Spring 2021
phone: STRING // phone number provided by user
// 978xxxxxxx
}
EVENT: {
id: STRING, // the cryptographically strong unique id for the event created
// => 111ce678-6929-480c-964b-7cf355f7d282
creator: STRING, // the netid of the power-user responsible for creating the event
// => jjt4
event_type: STRING, // the category type of the event
// => GM
event_code: STRING, // a cryptographically-strong uniquely generated code (or manually supplied)
// => 647319
mandatory: BOOLEAN, // if the event unexcused would result in probation
// => false
excusable: BOOLEAN, // if the event can be excused
// => true
title: STRING, // the short title of the event
// => General Meeting
description: STRING, // the description of the event
// => Weekly chapter meeting for exec to provide updates to the chapter
start: STRING, // the ISO datetime of when th eevent begins
// => 2020-03-24T01:00:00.000Z
duration: INT // the duration of the event in minutes
// => 60
}
ATTENDANCE: {
event_id: STRING, // the foreign key to an event
// => 111ce678-6929-480c-964b-7cf355f7d282
netid: STRING // the netid of the user who attended
// => jjt4
}
EXCUSE: {
event_id: STRING, // the foreign key to an event
// => 111ce678-6929-480c-964b-7cf355f7d282
netid: STRING, // the netid of the user who requested an excuse
// => jjt4
reason: STRING, // the reason for the request
// => I have a meeting with a professor
approved: BOOLEAN // if an exec has approved the requested excuse
// => true
}
POINT: {
event_id: STRING, // the foreign key to an event
// => 111ce678-6929-480c-964b-7cf355f7d282
category: STRING, // the category the points are valid for
// => Rush
count: INT // the number of points the event is worth
// => 2
}
Route | Method | Request | Response | Privilege |
---|---|---|---|---|
dev/users/login | POST | email, id token | their user data and session token or error (bad cred or unapproved) | none |
dev/users/ | GET | session token | all users' data | any user |
dev/users/{email} | PATCH | session token, target user, changes | updated user data or error | target user or privileged user |
dev/events/ | GET | session token | all events (which data elements depends on privilege) and points | any user |
dev/events/ | POST | session token, event details, points | created event or error | privileged user |
dev/events/{event_id} | PATCH | session token, target event, changes | updated event data and/or points or error | privileged user |
dev/events/{event_id} | DELETE | session token, target event | deleted event or error | privileged user |
dev/attendance | POST | session token, event id, event code | success or error | any user |
dev/attendance/users/{email} | GET | session token, target user | all events attended or excused by target user and point aggregates | target user or privileged user |
dev/attendance/events/{event_id} | GET | session token, target event | attendance and excuses of all users for the target event | privileged user |
dev/excuse | GET | session token | all excuses for all events | privileged user |
dev/excuse | POST | session token, event id, reason | success or error | any user |
dev/excuse | PATCH | session token, event id, approval | success or error | privileged user |
dev/auto/roles | POST | session token | success or error | privileged user |
Note: this project uses my custom Optional Chaining util, see: https://github.com/jtaylorchang/js-optchain
CREATE TABLE `event` (
`id` varchar(36) NOT NULL DEFAULT '',
`creator` varchar(16) NOT NULL DEFAULT '',
`event_type` varchar(32) DEFAULT NULL,
`event_code` varchar(4) DEFAULT NULL,
`mandatory` tinyint(1) DEFAULT NULL,
`excusable` tinyint(1) DEFAULT NULL,
`title` varchar(32) DEFAULT '',
`description` varchar(256) DEFAULT '',
`start` varchar(32) DEFAULT '',
`duration` int(11) DEFAULT NULL,
`location` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `attendance` (
`event_id` varchar(36) NOT NULL DEFAULT '',
`netid` varchar(16) NOT NULL DEFAULT '',
PRIMARY KEY (`event_id`,`netid`),
CONSTRAINT `attendance_event_id` FOREIGN KEY (`event_id`) REFERENCES `event` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `excuse` (
`event_id` varchar(36) NOT NULL DEFAULT '',
`netid` varchar(16) NOT NULL DEFAULT '',
`reason` varchar(128) NOT NULL DEFAULT '',
`late` tinyint(1) DEFAULT '0',
`approved` tinyint(1) DEFAULT '0',
PRIMARY KEY (`event_id`,`netid`),
CONSTRAINT `excuse_event_id` FOREIGN KEY (`event_id`) REFERENCES `event` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `point` (
`event_id` varchar(36) NOT NULL DEFAULT '',
`category` varchar(16) NOT NULL DEFAULT '',
`count` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`event_id`,`category`),
CONSTRAINT `point_event_id` FOREIGN KEY (`event_id`) REFERENCES `event` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
This project is GPLv2 licensed