-
Notifications
You must be signed in to change notification settings - Fork 3
Home
This document describes the Hacklab Booking System. It outlines its high level purpose, design, as well as documentation for the backend API and frontend components. There is also be a brief software bill of materials and a rationale for why each dependency is used.
Version | Date | Changelog |
---|---|---|
2.0 | 2023-06-07 | Initial release |
The Hacklab is a space for students and student organizations to work, socialize, learn, and develop their skills. As of now, few groups can use Hacklab for meetings because it requires TCard access. Our solution makes Hacklab a more accessible place for everyone who needs it. For example, groups for upper-year courses may want Hacklab to host standups but are unable due to the TCard requirement. To allow clubs and student groups to access the Hacklab for these meetings, we proposed a booking system allowing students to book the Hacklab. Any current bookings may then be viewed on the Hacklab iPad that will be placed in front of the Hacklab, alerting students of private meeting occupancy. The Hacklab Booking System project from Winter 2023 is the solution to this problem, but it currently needs features and stability for production deployment.
The goal of the Hacklab Booking System to make it ready for production use. First and foremost, a rewrite of the backend server is needed to scale better with users and rooms. For instance, some backend routes currently run at O(n) with respect to the number of users, which would be unacceptable for a production environment. Another issue is that our backend routes are integrated too tightly with the frontend, which makes our API less extensible for people who would like to use it. Our proposed solution is to migrate the current database from MongoDB to PostgreSQL.
Next, the current booking system allows faculty to approve and deny requests; however, there is only one avenue for notifications–email, which not everyone may check frequently. This issue may be solved by adding more venues for notifications, including Discord, Slack, and Web notifications via service workers. Furthermore the user experience for request approvers to approve and deny requests in the notification itself is also improved.
Finally, the code quality of the existing codebase is improved by using TypeScript, which would increase the readability and predictability of our codebase. There is also a GitHub Wiki to document backend routes and functions to increase maintainability for future teams, decreasing the need to first acquire tribal knowledge. This would allow our booking system to be safely deployed for the Hacklab for years to come.
A full software bill of materials may be found in the backend package.json, the frontend package.json, and in the Dockerfile
s of both.
For our backend and frontend, we aim to use TypeScript as much as possible for easier readability. We also aim to use ESLint as a pre-commit hoot for the same reason.
Our code will be hosted on our GitHub repository and production servers are deployed with Docker Compose using Docker containers.
The Docker containers will be hosted in a UofT-provided virtual machine, and authentication is handled by UofT's SSO auth, Shibboleth.
-
React - A component-based library for JavaScript. Our frontend is bootstrapped with
create-react-app
. - Material UI - A majority of our frontend components are from MaterialUI, which are a library of components implementing Google's Material Design.
- Axios - An HTTP client for JavaScript, which simplify network requests to the backend API.
-
DayJS - Required for the MaterialUI
DatePicker
component to work. Also simplifies the manipulation of dates for the booking pages. - Apple iPad - For displaying current events in front of Hacklab
The Material UI component library was chosen as our project is under GDSC UTM, a club affiliated with Google. Thus, a component library that mimics Google's design language will better align with our organization's design.
- NGINX - Reverse proxy that allows our webapp to be hosted on port 443.
- NodeJS - JavaScript on server side 😋
- ExpressJS - An RESTful backend web application framework for NodeJS.
- Google API - Allows for integration with Gmail, Google Calendar.
- Prisma - Next-generation Node.js and TypeScript ORM that supports PostgreSQL
-
PostgreSQL - An open source SQL-compliant relational database system.
A relational databse was chosen for this project as a replacement for the previous MongoDB, as our dataset is highly relational. For instance, each request has an author (which is a
User
), aGroup
, and aRoom
.Group
s store different arrays forUser
s depending on whether they're a member, invited, or a group manager, and so on.
This heavily simplified flowchart depicts the system architecture. The user logs in using Shibboleth to access the frontend. The frontend communicates using the REST API to make requests to the server, which modifies the database accordindly using the ORM (Prisma) interface.
The student role is the default role given to everyone upon first launch (given already authenticated with Shibboleth. It gives the following permissions:
- Create a group
The request approver role is manually given to faculty upon request. It gives the following permissions:
- View a log of all previous requests
- Can approve/deny requests made by any student
The TCard Approver role is given to system administrators (e.g., Andrew Wang) for the purposes of notifying the student that TCard access has been granted. It gives the following permissions:
- View a log of all previous requests
- Modify students’ database entry regarding TCard access
The administrator role has all permissions (i.e., the permissions of both Request Approver and TCard Approver) and is given to faculty upon request. It gives the following permissions:
- View a log of all previous requests
- Modify students’ database entry regarding TCard access
- Can approve/deny requests made by any student
- Reject bookings that are already approved
- Can delete student groups
Groups are a concept used by the Hacklab Booking System to allow multiple people to view the status of bookings. There is no limit for how many members a group can have, and there is no limit for the number of total groups. Students must be added into a group or create a group in order to create requests.
In a group, there are two roles with the following permissions:
- Can add/remove people to the group (without approval from faculty)
- Can delete other bookings within their group
- Can delete the group
- Can request bookings under their group name
- Can leave the group
- Can view all the group’s bookings
Our design has some limitations:
- Administrators do not have permission to make other people administrator. Their role must be changed in the database manually.
This diagram depicts a high level overview of the data design.
This table stores the account infomation for all users (students and faculty). The permission level is identified by the role field.
Table accounts {
id integer [pk]
utorid string
email string
name string
role account_role
}
This enum defines the permission level of a user, for more information see User Roles
enum account_role {
student
admin
approver
tcard
}
This table stores all requests. Each request as a group and a room assigned to it. The status field is an enum which represents the current status of the request, see Request status enum.
Table requests {
id integer [pk]
status request_status // the status of the request defined by the Request status enum
group integer [ref: > groups.id] // the group making the request
author integer [ref: > accounts.id] // the author of the request
approver integer [ref: > accounts.id] // (optional) if the request has been approved
start_date date // the starting date and time of the request in ISO 8601 format
end_date date // the ending date and time of the request in ISO 8601 format
description string // a summary of the event provided to the approvers for review
title string // the name of the event
room integer [ref: > rooms.id] // the room booked
reason string [null] // (optional) the reason for the denial/approval of a request
}
Every request must have a status, the status shows the state of a given request.
enum request_status {
pending // the request is awaiting approver's approval
denied // the request has been denied by approver
cancelled // the request has been cancelled by the author or group manager
need_tcard // the request has been approved by an approver but still needs TCard approval
completed // the request is approved and author has TCard access
}
This table contains information about the groups.
Table groups {
id integer [pk]
members integer[] [ref: <> accounts.id] // all members of the group, including managers, excluding any invited users
invited integer[] [ref: <> accounts.id] // users that have been invited to the group and have yet to accept
managers integer[] [ref: <> accounts.id] // the managers of the group
}
This table stores all bookable rooms.
Table rooms {
id integer [pk]
room_name string // the room number, eg. DH2014
friendly_name string // friendly name, eg. Hacklab
capacity integer // (optional) occupancy limit
}
Google API integration is handelled by google-api-javascript-client. The backend is linked with the Google Account hacklab.booking<at>gmail<dot>com
It is currently used for updating the Hacklab Google Calendar and sending emails.
Email notifications are handled by the Google API integration. Emails are sent on the Hacklab Booking Google account for the purposes of notification only. Emails are sent in a plain-text format with actionable buttons, which lead to the Hacklab Booking website.
Items that are not checked as "Optional" are required fields.
Creates a request under a group.
Parameters are not required for this API route.
This HTTP request can only be performed by members of the group specified in the group
parameter.
Field | Type | Description | Optional |
---|---|---|---|
group |
string |
The ID of the group that will be representing this request. | |
start_date |
Date |
The start date for the booking | |
end_date |
Date |
The end date for the booking | |
description |
string |
Description of the booking | |
title |
string |
The title of the booking | |
room |
string |
The room number of the booking |
If successful, this endpoint returns an HTTP 200 code with the newly created Request
object.
If the user is not part of the supplied group, HTTP 403 is returned.
If the start_date
is after end_date
HTTP 400 is returned.
Get requests by user or group. See the Authorization section for more information on default behaviour.
Field | Type | Description | Optional |
---|---|---|---|
start_date |
Date |
The start date for the booking | ✔️ |
group |
string |
The ID of the group that will be representing this request. | ✔️ |
end_date |
Date |
The end date for the booking | ✔️ |
description |
string |
Description of the booking | ✔️ |
title |
string |
The title of the booking | ✔️ |
room |
string |
The room number of the booking | ✔️ |
For administrators, this request returns all active future requests by default (i.e., when no parameters are supplied).
For students, this request returns all active future requests of any groups the user is a part of.
If successful, this endpoint returns an HTTP 200 code with a Request
array.
If a student tries to access other user's or group's requests, the endpoint will return HTTP 403.
Get request info given a request ID.
Do not supply request parameters with this endpoint.
For administrators, this request returns all active future requests by default (i.e., when no parameters are supplied).
For students, this request returns all active future requests of any groups the user is a part of.
If successful, this endpoint returns an HTTP 200 code with the Request
object specified.
If the user does not have permission to view this request, then this endpoint returns HTTP 403. It will return HTTP 404 if the request ID does not exist.
Delete a request given a request ID.
Do not supply request parameters with this endpoint.
Only managers of the group which the request belongs to, the author of the request, and administrators may delete the request.
If successful, this endpoint returns an HTTP 200 code.
If the user does not have permission to delete this request, then this endpoint returns HTTP 403. It will return HTTP 404 if the request ID does not exist.
Modify a request given its ID.
Field | Type | Description | Optional |
---|---|---|---|
start_date |
Date |
The start date for the booking | ✔️ |
group |
string |
The ID of the group that will be representing this request. | ✔️ |
end_date |
Date |
The end date for the booking | ✔️ |
description |
string |
Description of the booking | ✔️ |
title |
string |
The title of the booking | ✔️ |
room |
string |
The room number of the booking | ✔️ |
Only managers of the group which the request belongs to, the author of the request, and administrators may edit the request.
If successful, this endpoint returns an HTTP 200 code.
If the user does not have permission to delete this request, then this endpoint returns HTTP 403. It will return HTTP 404 if the request ID does not exist.
Get the current user info (based on who is currently logged in)
Parameters are not required for this API route.
Can be used by anyone authenticated by Shibboleth.
this endpoint returns an HTTP 200 code with the requested Account
object.
Get the user info of a certain user.
Parameters are not required for this API route.
Can only be used by admins.
this endpoint returns an HTTP 200 code with the requested Account
object or a HTTP 403 if the user lacks permissions.
Change the role of a user.
Do not supply request parameters with this endpoint.
Field | Type | Description | Optional |
---|---|---|---|
role |
string |
The new role of the user (can either be student , approver , tcard , or admin ) |
This is an admin only endpoint.
If successful, this endpoint returns an HTTP 200 code. If anyone other than admins tries to access this endpoint, the endpoint will return HTTP 403.
Change the current theme of the logged in user. system
specifies that the theme will be decided based on the system preferences.
Parameters are not required for this API route.
Field | Type | Description | Optional |
---|---|---|---|
theme |
string |
The new role of the user (can either be light , dark , or system ) |
Can be used by anyone authenticated by Shibboleth.
This endpoint returns HTTP 200 on a successful theme change, otherwise HTTP 400 is returned.
Get room info given its room number.
Do not supply request parameters with this endpoint.
Only managers of the group which the request belongs to, the author of the request, and administrators may edit the request.
If successful, this endpoint returns an HTTP 200 code and the Room
object specified by the path parameter.
If the user does not have permission to delete this request, then this endpoint returns HTTP 403. It will return HTTP 404 if the request ID does not exist.
Return all the dates already booked or pending, defaults to within the next week.
Field | Type | Description | Optional |
---|---|---|---|
start_date |
Date |
The start date to filter by in the ISO 8601 format. (YYYY-MM-DD). Defaults to the current date. | ✔️ |
group |
string |
The end date to filter by in ISO 8601 format. (YYYY-MM-DD). Defaults to the current date + 7 days. | ✔️ |
All authenticated users can access this endpoint.
If successful, this endpoint returns an HTTP 200 code and an array of date strings in ISO 8061 format.
Given a room and UTORid, update the user with the supplied UTORid to have access to the room supplied by the path parameters.
Do not supply request parameters with this endpoint.
Only tcard
and admin
roles are approved to call this endpoint.
Field | Type | Description | Optional |
---|---|---|---|
utorid |
string |
The UTORid of the student that will be granted access. |
If successful, this endpoint returns an HTTP 200 code.
If the user or room doesn't exist or is not found, this endpoint returns an HTTP 404 code.
If the method caller does not have the tcard
or admin
role, this endpoint returns an HTTP 430 code.
Return all the dates already booked or pending, defaults to all requests.
Field | Type | Description | Optional |
---|---|---|---|
start_date |
Date |
The start date to filter by in the ISO 8601 format. (YYYY-MM-DD). Defaults to the current date. | ✔️ |
group |
string |
The end date to filter by in ISO 8601 format. (YYYY-MM-DD). Defaults to the current date + 7 days. | ✔️ |
All authenticated users can access this endpoint.
If successful, this endpoint returns an HTTP 200 code and an array of request objects.
Get all groups you belong to (will return all existing groups for staff)
Do not supply request parameters with this endpoint.
Can be used by anyone authenticated by Shibboleth.
This endpoint returns an HTTP 200 code with an array of Group
objects.
Get information about a certain group.
Do not supply request parameters with this endpoint.
Can be used by anyone authenticated by Shibboleth.
If successful, this endpoint returns an HTTP 200 code with a Group
object.
Returns 403 if the user is not an admin and is not part of the group.
Create a new student group, with the current user as its manager.
Do not supply request parameters with this endpoint.
Can be used by anyone authenticated by Shibboleth.
Field | Type | Description | Optional |
---|---|---|---|
name |
string |
The name of the new group. |
If successful, this endpoint returns an HTTP 200 code.
If a group with the same name already exists, HTTP 409 will be returned.
Change the role of a user. Note that the caller cannot change their own role. Other members must change their role for them.
Do not supply request parameters with this endpoint.
The caller of this endpoint must be a manager of the group identified by the path parameters.
Field | Type | Description | Optional |
---|---|---|---|
utorid |
string |
The utorid of the target user | |
role |
string |
The the new role of the specified user (either manager or member ). |
If successful, this endpoint returns an HTTP 200 code. If the caller is not a manager of the group, HTTP 403 will be returned. If the caller tries to change their own role, HTTP 400 will be returned. If the user is not found or is not in the group, 404 will be returned
Invite a user to the group.
Do not supply request parameters with this endpoint.
The caller of this endpoint must be a manager of the group identified by the path parameters.
Field | Type | Description | Optional |
---|---|---|---|
utorid |
string |
The utorid of the target user |
If successful, this endpoint returns an HTTP 200 code. If the caller is not a manager of the group, HTTP 403 will be returned. If the caller tries to invite someone who is already in the group, HTTP 400 will be returned. If the user is not found, 404 will be returned
Accept a pending invite from a group.
Do not supply request parameters with this endpoint.
The caller of this endpoint must be a manager of the group identified by the path parameters.
Do not supply body with this endpoint.
If successful, this endpoint returns an HTTP 200 code. If the caller is not invited to the group HTTP 400 will be returned.
Remove a user from the group.
Do not supply request parameters with this endpoint.
The caller of this endpoint must be a manager of the group identified by the path parameters.
Field | Type | Description | Optional |
---|---|---|---|
utorid |
string |
The utorid of the target user |
If successful, this endpoint returns an HTTP 200 code. If the caller is not a manager of the group, HTTP 403 will be returned. If the user is not found or is not in the group, 404 will be returned.
Delete a group.
Do not supply request parameters with this endpoint.
The caller of this endpoint must be a manager of the group identified by the path parameters.
Do not supply body with this endpoint.
If successful, this endpoint returns an HTTP 200 code. If the caller is not a manager of the group, HTTP 403 will be returned.
A local instance may be deployed either
- On the host machine using
npm
, or - Using
docker-compose
.
-
install npm:
> npm install npm@latest -g
-
install dependencies:
> cd frontend > npm install
-
start the app:
> npm start
-
make sure
docker-compose
is installed:> docker-compose version Docker Compose version v2.17.3
-
load
docker-compose.dev.yml
into docker> docker-compose up -f docker-compose.dev.yml
Only important files and directories are shown below.
frontend
├── public # All public facing assets/files
│ ├── index.html
├── contexts
│ ├── UserContext.jsx
├── components
├── pages
│ ├── Admin
│ ├── Calendar
│ ├── CreateBooking
│ ├── Dashboard
│ ├── Group
│ ├── NotFound
│ └── Settings
│ ├── Webhooks
├── layouts
│ ├── ErrorPage.jsx
│ ├── SubPage.jsx
├── theme
├── axios.ts
└── App.tsx # Starting point of the client
Each component is documented using the JSDoc Standard, which allows compatability with VS Code Intellisense. To learn more about each component, please refer to the components folder and view the JSDoc written in each file.
View the original Figma mockup
The groups page can be reached by clicking "Your Groups" on the Dashboard. By clicking "view" on each group card, more information about the group can be viewed.
The booking creation page can be reached by clicking "Book" on the Dashboard. After being prompted some information about the booking, it is created and sent to the select approver for review.
Request tracking changes state from sent, to pending T-Card access, to complete.
All approvers have to do is click "Approve" or "Deny", then provide a reason for doing so.
Administrators can view users who are approved for T-Card access to the Hacklab but do not have it.
When a user logs in, their role is identified by the backend. If the user is new, they are given the student role.
Upon being recognized as a student, relevant booking information and user information is retrieved. Students are given the base permissions as shown in the flowchart above.
Upon being recognized as an approver, active request information and user information is retrieved. Approvers are given the base permissions plus permissions to approve/deny requests, as shown in the flowchart above.
Upon being recognized as a admin, active request information and user information is retrieved. Admins are given all permissions as shown in the flowchart above. They also have permission to use a special "Admin" page, where they can view a history of all requests, and modify a list of people who need TCard access to each room which that requires it.
All users can control the same settings, including webhook notifications, light/dark theme, and profile information.
Upon opening the groups directory, a list of all groups a user is in is shown. Users are also given the opportunity to accept any group invitations they may have, or create a new group. They can also view details groups they may be in.
The booking process follows the flow chart process above. When creating a booking, a user has to input an explanation, room, event title, group, and time. Then, they have to select an approver to notify. Upon approval, the request is marked as accepted and TCard access is provided by admins.
Modified from the GitHub Wiki This part is subject to change as our tech stack continues to evolve
As of now, the project is hosted on a VM provided by UTM. There is an Apache instance listening on ports 80 and 443 and reverse proxies to port 3555.
Our app is self-contained and has its own nginx reverse proxy to have all requests to /api
directed to the backend container and all other requests to the frontend container.
The front end is created with react and is served by serve. The backend is a nodejs app.
To deploy, get the latest code, and run docker-compose up -d --build
. This will start up our application on port 3555. A different port would require modifying the compose file.