Author SHA1 Message Date
SowinskiBraeden c05e3a3879 Update Version to beta 2022-06-11 10:12:40 -07:00
SowinskiBraeden fa8acec7a8 change yog to any year 2022-06-11 10:08:42 -07:00
SowinskiBraeden 7ed53fc539 allow teachers to change personal email 2022-06-11 09:59:18 -07:00
SowinskiBraeden 315f33b3e7 Merge branch 'master' of https://github.com/SowinskiBraeden/school-management-api 2022-06-11 09:48:02 -07:00
SowinskiBraeden 3aa4f72b33 add variable school email 2022-06-11 09:47:51 -07:00
SowinskiBraeden 2071e94516 Merge pull request #25 from SowinskiBraeden/update-admin-information
add routes and functions to update admin
2022-06-11 09:41:39 -07:00
SowinskiBraeden 50e672849b add routes and functions to update admin 2022-06-11 09:40:20 -07:00
SowinskiBraeden 956fe306e5 minor bug fix 2022-06-10 15:42:19 -07:00
SowinskiBraeden dcabacdc8c Merge branch 'master' of https://github.com/SowinskiBraeden/school-management-api 2022-06-10 14:09:22 -07:00
SowinskiBraeden 8032b79989 remove deprecated debug response 2022-06-10 14:09:07 -07:00
SowinskiBraeden 255ea25edf Continue Documentation 2022-06-10 14:04:45 -07:00
SowinskiBraeden 7e85215fcc remove deprecated messages 2022-06-10 13:20:54 -07:00
SowinskiBraeden 84b8c1ae5e Finish updating documentation 2022-06-10 13:19:58 -07:00
SowinskiBraeden ca4020f0ee Update DOCUMENTATION.md 2022-06-10 12:29:29 -07:00
SowinskiBraeden 596533753c remove deprecated test response 2022-06-10 12:28:50 -07:00
SowinskiBraeden e97f185e22 remove deprecated test code 2022-06-10 10:45:56 -07:00
SowinskiBraeden fdd489345d Update DOCUMENTATION.md 2022-06-10 10:45:20 -07:00
SowinskiBraeden 724b61cc2e add previews 2022-06-09 19:21:40 -07:00
SowinskiBraeden b456a02178 remove debug 2022-06-09 19:19:58 -07:00
SowinskiBraeden 41e7ca4f87 work on documentation 2022-06-09 19:14:01 -07:00
SowinskiBraeden ae88f5d149 work on documentation 2022-06-09 19:00:56 -07:00
SowinskiBraeden 421943d5d4 Begin Documentation 2022-06-09 14:07:51 -07:00
SowinskiBraeden 8ee6c40bde Update README.md 2022-06-09 10:47:35 -07:00
SowinskiBraeden c83f05674e Rename DOCUMENTATION to DOCUMENTATION.md 2022-06-09 10:42:05 -07:00
SowinskiBraeden e8c3272677 Update README.md 2022-06-09 10:41:34 -07:00
SowinskiBraeden 974c512cd1 Create DOCUMENTATION 2022-06-09 10:39:40 -07:00
SowinskiBraeden f177e8cef7 solve issue #15 2022-06-08 13:47:08 -07:00
SowinskiBraeden 55ca35034e bug fix student login thinks its admin 2022-06-08 13:02:10 -07:00
SowinskiBraeden 2ef9c71f59 simple change 2022-06-07 13:11:39 -07:00
SowinskiBraeden f402b27222 fix UpdateTeacherName 2022-06-07 13:10:33 -07:00
SowinskiBraeden 7bd33decc9 fix UpdateTeacherName 2022-06-07 13:08:11 -07:00
SowinskiBraeden 456dab3d6f update version 2022-06-07 13:00:19 -07:00
SowinskiBraeden 27eb0937c7 set parentType in idManager.go 2022-06-07 11:14:00 -07:00
SowinskiBraeden eb065c4142 generalize authentication in #13 2022-06-07 11:12:28 -07:00
SowinskiBraeden 22d41d04dc fix Path traversal security 2022-06-06 15:27:35 -07:00
SowinskiBraeden dd0cd7d76c update jwt 2022-06-06 15:24:17 -07:00
SowinskiBraeden 52e1c0fb5b better default admin creation 2022-06-06 13:37:03 -07:00
SowinskiBraeden f8ff2cf90e update version number 2022-06-06 13:13:59 -07:00
SowinskiBraeden 66112d4158 bug fix #11 2022-06-06 13:11:59 -07:00
SowinskiBraeden 39400b371a update testing readme.md 2022-05-31 11:52:21 -07:00
SowinskiBraeden 9d44f98ff1 block dashboard on disabled account 2022-05-28 11:09:45 -07:00
SowinskiBraeden c054e3f065 bug-fix/failing to disable account 2022-05-26 12:46:57 -07:00
SowinskiBraeden b708f1d815 use log over fmt 2022-05-25 16:01:57 -07:00
SowinskiBraeden 471d1c94a0 Merge pull request #6 from SowinskiBraeden/improved-system-email
better emails
2022-05-25 13:44:26 -07:00
SowinskiBraeden f37e677dbf better emails 2022-05-25 13:42:44 -07:00
21 changed files with 1879 additions and 224 deletions

No files matched your search

+1
View File
@@ -2,5 +2,6 @@ mongoURI=mongodb://localhost:27017
dbo=school
secret=<your 256 bit secret>
PORT=8000
SYSTEM_EMAIL_ADDRESS=<@exampleschoolemail.com>
SYSTEM_EMAIL=<your system email>
SYSTEM_PASSWORD=<your system password>
+940
View File
@@ -0,0 +1,940 @@
# School Management API Documentation
The School Management API Documentation, defining all functions and their purpose. As well as how to use the API, how to properly call functions and provide the necessary fields.
### Contents
* [Getting Started](#getting-started)
* [General Functions](#general-functions)
* [Creating Accounts](#creating-accounts)
* [Creating Administrators](#creating-administrators)
* [Registering a Teacher](#registering-a-teacher)
* [Enrolling a Student](#enrolling-a-student)
* [Additional Info](#additional-information-after-creating-an-account)
* [Log into Accounts](#log-into-accounts)
* [Logging Into Admin](#logging-into-admin)
* [Logging Into Teacher](#logging-into-teacher)
* [Logging Into Student](#logging-into-student)
* [Get Account](#get-account)
* [Get Admin Account](#get-admin-account)
* [Get Teacher Account](#get-teacher-account)
* [Get Student Account](#get-student-account)
* [Updating Account information](#updating-accounts)
* **Update Student...**
* [Name](#update-student-name)
* [Grade Level](#update-student-grade-level)
* [Homeroom](#update-student-homeroom)
* [Locker](#update-student-locker)
* [YOG](#update-student-year-of-graduation)
* [Password](#update-student-password)
* [Email](#update-student-email)
* [Address](#update-student-address)
* [Photo](#update-student-photo)
<br></br>
* **Update Teacher...**
* [Name](#update-teacher-name)
* [Homeroom](#update-teacher-homeroom)
* [Password](#update-teacher-password)
* [Email](#update-teacher-email)
* [Address](#update-teacher-address)
* [Photo](#update-teacher-photo)
<br></br>
* **Update Admin...**
* [Name](#update-admin-name) - **(Coming Soon)**
* [Password](#update-admin-password) - **(Coming Soon)**
* [Email](#update-admin-email) - **(Coming Soon)**
* [Photo](#update-admin-photo) - **(Coming Soon)**
* [Managing Contacts](#managing-contacts) - **(Not Documented)**
* [Create Contact](#create-contact) - **(Not Documented)**
* [Update Contact Name](#update-contact-name) - **(Not Documented)**
* [Update Contact Address](#update-contact-address) - **(Not Documented)**
* [Update Contact Home Phone](#update-contact-home-phone) - **(Not Documented)**
* [Update Contact Work Phone](#update-contact-work-phone) - **(Not Documented)**
* [Update Contact Email](#update-contact-email) - **(Not Documented)**
* [Update Contact Priority](#update-contact-priority) - **(Not Documented)**
* [Delete Contact](#delete-contact) - **(Not Documented)**
<br>
## Getting Started
* ### Installation & Deployment
The API is an all in one package and is simple and easy to deploy. Eliminating any long unforgiving configurations you may face in other software.
1. Clone the repo
```bash
$ git clone https://github.com/SowinskiBraeden/school-management-api
$ cd school-management-api
```
2. Rename `.env.example` to `.env`
3. Enter desired values into `.env`
```bash
mongoURI='your mongo URI'
dbo='school'
secret='your 256 bit secret'
# Suggested port for Production: 80
# Suggested port for Development: 8000
PORT='desired port'
# This is to enable the system to send emails
SYSTEM_EMAIL='your system email'
SYSTEM_PASSWORD='your system email password'
```
4. Run the system in your terminal
```bash
$ go run main.go
```
Or...
5. Build and compile the system into an executable
```bash
$ go build
```
* ### Initializing the System
Upon running the system for the first time, you will be prompted to create an admin.
In order to perform many actions with the API, an Admin account is required. You will be prompted to create your default admin account seen below...
![init](previews/init.png)
Upon completing the initial setup and creating your default administrator for the system, it is ready to use and this box will appear below, displaying basic system details seen below...
![running](previews/running.png)
<br>
## General Functions
* ### API Status
**Method:** `GET`
```
<API_URL>/api/v1/status
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "the API is active"
}
```
<br>
## Creating Accounts
There are several account that can be registered into the system. As you may guess they are Administrators, Teachers and Students.
<br>
+ ### Creating Administrators
You can create Administrates- who will have permissions to perform majority of the actions in the API. It is common that there are more than one Administrator to help manage the system.
**Method:** `POST`
```
<API_URL>/api/v1/admin/create
```
**Required:**
+ Logged into an existing Admin account
+ JSON:
```jsonc
{
"firstname": "John",
"lastname": "Doe",
"dob": "01-01-1999",
"email": "john_doe@example.com"
}
```
**Returns:**
+ Status 200: `OK`
+ JSON:
```jsonc
{
"success": true,
"message": "successfully inserted admin"
}
```
<br>
+ ### Registering a Teacher
Obviously a school management system will require teachers to manage and teach students. Teachers have an important role for the system.
**Method:** `POST`
```
<API_URL>/api/v1/teacher/register
```
**Required:**
+ Logged into an existing Admin account
+ JSON:
```jsonc
{
"firstname": "Homer",
"middlename": "Jay",
"lastname": "Simson",
"dob": "12-05-1956",
"email": "homerdog_simpson@example.com"
}
```
**Returns:**
+ Status 200: `OK`
+ JSON:
```jsonc
{
"success": true,
"message": "successfully inserted teacher"
}
```
+ ### Enrolling a Student
What is a school without students? A professional day, but not the point. Students when their application to the school has been accepted by an admin can be enrolled into the school.
**Method:** `POST`
```
<API_URL>/api/v1/student/enroll
```
**Required:**
+ Logged into an existing Admin account
+ JSON:
```jsonc
{
"firstname": "Bart",
"middlename": "JoJo",
"lastname": "Simpson",
"age": 10,
"gradelevel": 4,
"dob": "17-12-1979",
"province": "...",
"city": "Springfield",
"address": "742 Evergreen Terrace",
"postal": "..."
}
```
**Returns:**
+ Status 200: `OK`
+ JSON:
```jsonc
{
"success": true,
"successfully inserted student"
}
```
+ ### Additional Information After creating an account
1. After successfully creating an account for another admin, teacher or student. A school email will be generated for them using their first and last name, each formatted differently based on the type of account.
2. All users are given a random ID used to sign into the system. Each ID is a random 6 digit number.
3. Students are given a random PEN (Personal Education Number). A random 12 digit number.
4. Accounts are given a default profile image. This can be updated in the future.
**(Coming Soon)**
5. All new users, once have verified their email, will receive and email containing their ID.
**(Currently No verified email required)**
6. All new users, once have verified their email, will receive and email containing their temporary password.
<br>
## Log Into Accounts
+ ### Logging into Admin
**Method:** `POST`
```
<API_URL>/api/v1/admin/login
```
**Required:**
* JSON:
```jsonc
{
"tid": "123456",
"password": "myawesomepassword123"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```
{
"success": true,
"message": "correct password"
}
```
<br>
+ ### Logging into Teacher
**Method:** `POST`
```
<API_URL>/api/v1/teacher/login
```
**Required:**
* JSON:
```jsonc
{
"tid": "123456",
"password": "myawesomepassword123"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "correct password"
}
```
<br>
+ ### Logging into Student
**Method:** `POST`
```
<API_URL>/api/v1/student/login
```
**Required:**
* JSON:
```jsonc
{
"sid": "123456",
"password": "myawesomepassword123"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "correct password"
}
```
<br>
## Get Account
+ ### Get Admin Account
**Method:** `GET`
```
<API_URL>/api/v1/admin
```
**Required:**
* Logged into an admin account
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully logged into admin",
"admin:" <admin object>
}
```
<br>
+ ### Get Teacher Account
**Method:** `GET`
```
<API_URL>/api/v1/teacher
```
**Required:**
* Logged into an teacher account
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully logged into teacher",
"teacher:" <teacher object>
}
```
<br>
+ ### Get Student Account
**Method:** `GET`
```
<API_URL>/api/v1/student
```
**Required:**
* Logged into an admin account
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully logged into teacher",
"student:" <student object>
}
```
<br>
## Updating Account Information
+ ### Update Student Name
**Method:** `POST`
```
<API_URL>/api/v1/student/updateName
```
**Required:**
* Logged into an admin account
* JSON:
```jsonc
{
"sid": "123456",
"firstname": "Lisa",
"middlename": "Marie", // (OPTIONAL)
"lastname": "Simpson"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated student"
}
```
<br></br>
+ ### Update Student Grade Level
**Method:** `POST`
```
<API_URL>/api/v1/student/updateGradeLevel
```
**Required:**
* Logged into an admin account
* JSON":
```jsonc
{
"sid": "123456",
"gradelevel": 11
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated student"
}
```
<br></br>
+ ### Update Student Homeroom
**Method:** `POST`
```
<API_URL>/api/v1/student/updateHomeroom
```
**Required:**
* Logged into an admin account
* JSON:
```jsonc
{
"sid": "123456",
"homeroom": "A123"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated student"
}
```
<br></br>
+ ### Update Student Locker
**Method:** `POST`
```
<API_URL>/api/v1/student/updateLocker
```
**Required:**
* Logged into an admin account
* JSON:
```jsonc
{
"sid": "123456",
"lockernumber": "B123"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated student"
}
```
<br></br>
+ ### Update Student Year of Graduation
**Method:** `POST`
```
<API_URL>/api/v1/student/updateYOG
```
**Required:**
* Logged into an admin account
* JSON:
```jsonc
{
"sid": "123456",
"yog": 2020
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated student"
}
```
<br></br>
+ ### Update Student Password
**Method:** `POST`
```
<API_URL>/api/v1/student/updatePassword
```
**Required:**
* Logged into students account
* JSON:
```jsonc
{
"password": "myOldPassword",
"newpassword1": "myNewPassword",
"newpassword2": "myNewPassword"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated student password"
}
```
* Emails student an alert of password change
<br></br>
+ ### Update Student Email
**Method:** `POST`
```
<API_URL>/api/v1/student/updateEmail
```
**Required:**
* Logged into an admin account
__or__
* Logged into students account
* JSON:
```jsonc
{
"sid": "123456", // (Only required if signed in as admin)
"email": "BartSimpsonsCoolEmail@example.com"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated"
}
```
<br></br>
+ ### Update Student Address
**Method:** `POST`
```
<API_URL>/api/v1/student/updateAddress
```
**Required:**
* Logged into an admin account
* JSON:
```jsonc
{
"sid": "123456",
"address": "742 Evergreen Terrace",
"city": "Springfield",
"province": "...",
"postal": "..."
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated student"
}
```
<br></br>
+ ### Update Student Photo
**Method:** `POST`
```
<API_URL>/api/v1/student/updatePhoto
```
**Required:**
* Logged into an admin account
* Form Submission:
```
sid: "123456"
image: newStudentPhoto.png
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated student photo"
}
```
<br></br>
+ ### Update Teacher Name
**Method:** `POST`
```
<API_URL>/api/v1/teacher/updateName
```
**Required:**
* Logged into an admin account
* JSON:
```jsonc
{
"tid": "123456",
"firstname": "Marjorie",
"middlename": "Jacqueline", // (Optional)
"lastname": "Simpson"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated teacher"
}
```
<br></br>
+ ### Update Teacher Homeroom
**Method:** `POST`
```
<API_URL>/api/v1/teacher/updateHomeroom
```
**Required:**
* Logged into an admin account
* JSON:
```
{
"tid": "123456",
"homeroom": "A123"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated teacher"
}
```
<br></br>
+ ### Update Teacher Password
**Method:** `POST`
```
<API_URL>/api/v1/teacher/updatePassword
```
**Required:**
* Logged into teacher account
* JSON:
```jsonc
{
"password": "myOldPassword",
"newpassword1": "myNewPassword",
"newpassword2": "myNewPassword"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated teacher password"
}
```
* Emails teacher an alert of password change
<br></br>
+ ### Update Teacher Email
**Method:** `POST`
```
<API_URL>/api/v1/teacher/updateEmail
```
**Required:**
* Logged into an admin account
**or**
* Logged into teachers account
* JSON:
```jsonc
{
"tid": "123456", // (Only required if igned in as admin)
"email": "MargeSimpson@example.com"
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated teacher"
}
```
<br></br>
+ ### Update Teacher Address
**Method:** `POST`
```
<API_URL>/api/v1/teacher/updateAddress
```
**Required:**
* Logged into an admin account
* JSON:
```jsonc
{
"tid": "123456",
"address": "742 Evergreen Terrace",
"city": "Springfield",
"province": "...",
"postal": "..."
}
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated teacher"
}
```
<br></br>
+ ### Update Teacher Photo
**Method:** `POST`
```
<API_URL>/api/v1/teacher/updatePhoto
```
**Required:**
* Logged into an admin
* Form Submission:
```
tid: "123456"
image: newTeacherPhoto.png
```
**Returns:**
* Status 200: `OK`
* JSON:
```jsonc
{
"success": true,
"message": "successfully updated teacher photo"
}
```
<br></br>
+ ### Update Admin Name
**Coming Soon...**
<br></br>
+ ### Update Admin Password
**Coming Soon...**
<br></br>
+ ### Update Admin Email
**Coming Soon...**
<br></br>
+ ### Update Admin Photo
**Coming Soon...**
<br></br>
## Managing Conacts
**To be Documented...**
+ ### Creating Contact
**To be Documented...**
<br></br>
+ ### Update Contact Name
**To be Documented...**
<br></br>
+ ### Update Contact Address
**To be Documented...**
<br></br>
+ ### Update Contact Home Phone
**To be Documented...**
<br></br>
+ ### Update Contact Work Phone
**To be Documented...**
<br></br>
+ ### Update Contact Email
**To be Documented...**
<br></br>
+ ### Update Contact Priority
**To be Documented...**
<br></br>
+ ### Delete Conotact
**To be Documented...**
<br></br>
## Remove Users
**To be Documented...**
+ ### Remove Admin
**To be Documented...**
<br></br>
+ ### Remove Teacher
**To be Documented...**
<br></br>
+ ### Remove Student
**To be Documented...**
<br></br>
### Admin Commands
Administrators play a very important role in managing a school, they do much work behind the scenes to make sure that everything is correctly organized and working.
+ ### Update Locker Combination
**To be Documented...**
<br></br>
+ ### Enable Student Account
In the case a students acount has been disabled, an admin would require to enable the account.
**To be Documented...**
<br></br>
+ ### Enable Teacher Account
In the case a teachers acount has been disabled, an admin would require to enable the account.
**To be Documented...**
<br></br>
+8 -1
View File
@@ -2,7 +2,12 @@
The backend API for a school management system accessible by staff and students.
<a href="https://github.com/SowinskiBraeden/school-management-api"><strong>Explore the docs »</strong></a>
***Explore the docs »***
* [Getting Started](#getting-started)
* [Usage/Documenation](#usage)
* [Contributing](#contributing)
* [License](#license)
* [Contact](#contact)
<br>
<br>
@@ -82,6 +87,8 @@ The systems is easy to start and your local machine. Eliminating any long unforg
The usage for this API is in the name, school management, it's not an easy task in the areas I live near. Overcrowded schools are far too common and the need for a better school management system is there. The current systems issued by the government work but are poor and outdated.
To see a full list of features and how to use the system, read the [documentation](DOCUMENTATION.md)
<!-- CONTRIBUTING -->
## Contributing
+147 -79
View File
@@ -4,6 +4,7 @@ import (
"bufio"
"context"
"fmt"
"log"
"os"
"strings"
"time"
@@ -12,10 +13,8 @@ import (
"github.com/SowinskiBraeden/school-management-api/models"
"github.com/howeyc/gopass"
"net/smtp"
"github.com/dgrijalva/jwt-go"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
@@ -27,14 +26,20 @@ var studentCollection *mongo.Collection = database.OpenCollection(database.Clien
var contactCollection *mongo.Collection = database.OpenCollection(database.Client, "contacts")
var adminCollection *mongo.Collection = database.OpenCollection(database.Client, "admins")
func NewSystem() {
count, err := adminCollection.CountDocuments(context.Background(), bson.D{})
func confirm(s string) bool {
r := bufio.NewReader(os.Stdin)
fmt.Printf("%s [y/n]: ", s)
res, err := r.ReadString('\n')
if err != nil {
fmt.Println("Unable to detect new system")
log.Fatal(err)
}
if count == 0 {
fmt.Println("Admin account setup...")
return strings.ToLower(strings.TrimSpace(res))[0] == 'y'
}
func CreateDefaultAdmin() models.Admin {
fmt.Println()
reader := bufio.NewReader(os.Stdin)
fmt.Print("First Name: ")
firstname, _ := reader.ReadString('\n')
@@ -45,10 +50,18 @@ func NewSystem() {
fmt.Print("Password: ")
password, _ := gopass.GetPasswd()
// Clear values of new lines and enter characters
firstname = strings.ReplaceAll(firstname, "\n", "")
lastname = strings.ReplaceAll(lastname, "\n", "")
email = strings.ReplaceAll(email, "\n", "")
firstname = strings.ReplaceAll(firstname, "\r", "")
lastname = strings.ReplaceAll(lastname, "\r", "")
email = strings.ReplaceAll(email, "\r", "")
var admin models.Admin
admin.FirstName = strings.TrimSuffix(firstname, "\n")
admin.LastName = strings.TrimSuffix(lastname, "\n")
admin.Email = strings.TrimSuffix(email, "\n")
admin.FirstName = firstname
admin.LastName = lastname
admin.Email = email
admin.SchoolEmail = admin.GenerateSchoolEmail()
@@ -59,7 +72,7 @@ func NewSystem() {
var aid string
for {
aid = GenerateID(6)
if ValidateID(aid) == true {
if ValidateID(aid, 3) == true {
break
}
}
@@ -69,13 +82,32 @@ func NewSystem() {
admin.Updated_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))
admin.ID = primitive.NewObjectID()
_, insertErr := adminCollection.InsertOne(context.Background(), admin)
if insertErr != nil {
fmt.Print("Failed to create admin")
return admin
}
func NewSystem() {
count, err := adminCollection.CountDocuments(context.Background(), bson.D{})
if err != nil {
fmt.Println("Unable to detect new system")
}
fmt.Println("Successfully created system admin")
fmt.Println("Your admin ID is ", aid)
if count == 0 {
fmt.Println("Admin account setup...")
for {
defaultAdmin := CreateDefaultAdmin()
if confirm("Are the above credentials correct?") {
_, insertErr := adminCollection.InsertOne(context.Background(), defaultAdmin)
if insertErr != nil {
log.Printf("Failed to create an admin\n")
}
log.Printf("Successfully created default admin")
log.Printf("Your default admin ID is %s", defaultAdmin.AID)
break
}
}
}
}
@@ -84,29 +116,11 @@ var SecretKey = os.Getenv("secret")
var systemEmail string = os.Getenv("SYSTEM_EMAIL")
var systemPassword string = os.Getenv("SYSTEM_PASSWORD")
func AuthAdmin(c *fiber.Ctx) bool {
cookie := c.Cookies("jwt")
token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(SecretKey), nil
})
if err != nil {
return false
func AuthenticateUser(c *fiber.Ctx, userType int) (bool, string) {
if userType < 1 || userType > 3 {
log.Fatal("Invalid userType")
}
claims := token.Claims.(*jwt.StandardClaims)
// Though admin is not used, it's required to prevent findErr
var admin models.Admin
findErr := adminCollection.FindOne(context.TODO(), bson.M{"aid": claims.Issuer}).Decode(&admin)
if findErr != nil {
return false
}
return true
}
func AuthStudent(c *fiber.Ctx) (verified bool, sid string) {
cookie := c.Cookies("jwt")
token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
@@ -118,14 +132,17 @@ func AuthStudent(c *fiber.Ctx) (verified bool, sid string) {
claims := token.Claims.(*jwt.StandardClaims)
// Though student is not used, it's required to prevent findErr
var student models.Student
findErr := studentCollection.FindOne(context.TODO(), bson.M{"sid": claims.Issuer}).Decode(&student)
var userID models.Id
findErr := idCollection.FindOne(context.TODO(), bson.M{"cid": claims.Issuer}).Decode(&userID)
if findErr != nil {
return false, ""
}
return true, claims.Issuer
if userID.ParentType != userType {
return false, ""
}
return true, userID.CID
}
func Enroll(c *fiber.Ctx) error {
@@ -142,7 +159,7 @@ func Enroll(c *fiber.Ctx) error {
}
// Ensure Authenticated admin sent request
if !AuthAdmin(c) {
if verified, _ := AuthenticateUser(c, 3); !verified {
cancel()
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
@@ -199,6 +216,7 @@ func Enroll(c *fiber.Ctx) error {
// Disable login block
student.AccountData.AccountDisabled = false
student.AccountData.Alerted = false
student.AccountData.Attempts = 0
// Generate temporary password
@@ -207,23 +225,22 @@ func Enroll(c *fiber.Ctx) error {
student.AccountData.TempPassword = true
// Send student personal email temp password
message := []byte("Your temporary password is " + tempPass)
auth := smtp.PlainAuth("", systemEmail, systemPassword, "smtp.gmail.com")
subject := "Password Changed"
receiver := student.PersonalData.Email
r := NewRequest([]string{receiver}, subject)
err := smtp.SendMail("smtp.gmail.com:587", auth, systemEmail, []string{student.PersonalData.Email}, message)
if err != nil {
if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": student.PersonalData.FirstName, "password": tempPass}); !sent {
cancel()
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"message": "Could not send password to students email",
"error": err,
})
}
var sid string
for {
sid = GenerateID(6)
if ValidateID(sid) == true {
if ValidateID(sid, 1) == true {
break
}
}
@@ -283,7 +300,7 @@ func RegisterTeacher(c *fiber.Ctx) error {
}
// Ensure Authenticated admin sent request
if !AuthAdmin(c) {
if verified, _ := AuthenticateUser(c, 3); !verified {
cancel()
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
@@ -342,16 +359,15 @@ func RegisterTeacher(c *fiber.Ctx) error {
teacher.AccountData.TempPassword = true
// Send teacher personal email temp password
message := []byte("Your temporary password is " + tempPass)
auth := smtp.PlainAuth("", systemEmail, systemPassword, "smtp.gmail.com")
subject := "Password Changed"
receiver := teacher.PersonalData.Email
r := NewRequest([]string{receiver}, subject)
err := smtp.SendMail("smtp.gmail.com:587", auth, systemEmail, []string{teacher.PersonalData.Email}, message)
if err != nil {
if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": teacher.PersonalData.FirstName, "password": tempPass}); !sent {
cancel()
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"message": "Could not send password to teacners email",
"error": err,
"message": "Could not send password to teachers email",
})
}
@@ -359,7 +375,7 @@ func RegisterTeacher(c *fiber.Ctx) error {
// For the unlikely event that an ID is already in use this will simply try again till it gets a id not in use
for {
tid = GenerateID(6)
isValid := ValidateID(tid)
isValid := ValidateID(tid, 2)
if isValid == true {
break
}
@@ -401,7 +417,7 @@ func CreateAdmin(c *fiber.Ctx) error {
}
// Ensure Authenticated admin sent request
if !AuthAdmin(c) {
if verified, _ := AuthenticateUser(c, 3); !verified {
cancel()
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
@@ -430,23 +446,22 @@ func CreateAdmin(c *fiber.Ctx) error {
admin.TempPassword = true
// Send admin personal email temp password
message := []byte("Your temporary password is " + tempPass)
auth := smtp.PlainAuth("", systemEmail, systemPassword, "smtp.gmail.com")
subject := "Password Changed"
receiver := admin.Email
r := NewRequest([]string{receiver}, subject)
err := smtp.SendMail("smtp.gmail.com:587", auth, systemEmail, []string{admin.Email}, message)
if err != nil {
if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": admin.FirstName, "password": tempPass}); !sent {
cancel()
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"message": "Could not send password to admins email",
"error": err,
"message": "Could not send password to students email",
})
}
var aid string
for {
aid = GenerateID(6)
if ValidateID(aid) == true {
if ValidateID(aid, 3) == true {
break
}
}
@@ -497,7 +512,6 @@ func StudentLogin(c *fiber.Ctx) error {
var student models.Student
err := studentCollection.FindOne(ctx, bson.M{"schooldata.sid": data["sid"]}).Decode(&student)
defer cancel()
if err != nil {
cancel()
@@ -508,21 +522,29 @@ func StudentLogin(c *fiber.Ctx) error {
})
}
var localAccountDisabled = false
if student.AccountData.Attempts >= 5 {
var verified bool = student.ComparePasswords(data["password"])
var localAccountDisabled bool = false
var localAttempts int = student.AccountData.Attempts
if !verified {
localAttempts += 1
}
if student.AccountData.Attempts >= 5 || localAttempts >= 5 {
localAccountDisabled = true // Catches newly disbaled account before student obj is updated
update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))
update := bson.M{
"$set": bson.M{
"AccountData.accountdisabled": true,
"AccountData.attempts": 0,
"accountdata.accountdisabled": true,
"accountdata.alerted": true,
"accountdata.attempts": 0,
"updated_at": update_time,
},
}
_, updateErr := studentCollection.UpdateOne(
ctx,
bson.M{"sid": data["sid"]},
bson.M{"schooldata.sid": data["sid"]},
update,
)
if updateErr != nil {
@@ -536,26 +558,42 @@ func StudentLogin(c *fiber.Ctx) error {
}
if localAccountDisabled || student.AccountData.AccountDisabled {
if !student.AccountData.Alerted {
// Send student email warning of disabled account
subject := "Account Disabled"
receiver := student.PersonalData.Email
r := NewRequest([]string{receiver}, subject)
if sent := r.Send("./templates/accountDisabled.html", map[string]string{"username": student.PersonalData.FirstName}); !sent {
cancel()
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"message": "Could not send password to students email",
"error": err,
})
}
}
cancel()
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"success": false,
"message": "Account is Disabled, contact an Admin",
})
}
var verified bool = student.ComparePasswords(data["password"])
if verified == false {
if !verified {
update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))
update := bson.M{
"$set": bson.M{
"AccountData.attempts": student.AccountData.Attempts + 1,
"accountdata.attempts": (student.AccountData.Attempts + 1),
"updated_at": update_time,
},
}
_, updateErr := studentCollection.UpdateOne(
ctx,
bson.M{"sid": data["sid"]},
bson.M{"schooldata.sid": data["sid"]},
update,
)
cancel()
@@ -566,10 +604,33 @@ func StudentLogin(c *fiber.Ctx) error {
"error": updateErr,
})
}
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"success": false,
"message": "incorrect password",
})
} else {
update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339))
update := bson.M{
"$set": bson.M{
"accountdata.attempts": 0,
"updated_at": update_time,
},
}
_, updateErr := studentCollection.UpdateOne(
ctx,
bson.M{"schooldata.sid": data["sid"]},
update,
)
if updateErr != nil {
cancel()
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"message": "the student could not be updated",
"error": updateErr,
})
}
}
defer cancel()
@@ -741,7 +802,7 @@ func AdminLogin(c *fiber.Ctx) error {
func Student(c *fiber.Ctx) error {
var sid string
if AuthAdmin(c) {
if verified, _ := AuthenticateUser(c, 3); verified {
var data map[string]string
if err := c.BodyParser(&data); err != nil {
@@ -793,6 +854,13 @@ func Student(c *fiber.Ctx) error {
})
}
if student.AccountData.AccountDisabled {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Student Accound Disabled, Contact and Admin",
})
}
responseData["student"] = student
var locker models.Locker
@@ -919,7 +987,7 @@ func CreateContact(c *fiber.Ctx) error {
}
// Ensure Authenticated admin sent request
if !AuthAdmin(c) {
if verified, _ := AuthenticateUser(c, 3); !verified {
cancel()
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
@@ -1004,7 +1072,7 @@ func DeleteContact(c *fiber.Ctx) error {
}
// Ensure Authenticated admin sent request
if !AuthAdmin(c) {
if verified, _ := AuthenticateUser(c, 3); !verified {
cancel()
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
+79
View File
@@ -0,0 +1,79 @@
package controllers
import (
"bytes"
"fmt"
"html/template"
"log"
"net/smtp"
"os"
)
type Config struct {
Server string
Port int
Email string
Password string
}
var config Config = Config{
Server: "smtp.gmail.com",
Port: 587,
Email: os.Getenv("SYSTEM_EMAIL"),
Password: os.Getenv("SYSTEM_PASSWORD"),
}
type Request struct {
from string
to []string
subject string
body string
}
const (
MIME = "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
)
func NewRequest(to []string, subject string) *Request {
return &Request{
to: to,
subject: subject,
}
}
func (r *Request) parseTemplate(fileName string, data interface{}) error {
t, err := template.ParseFiles(fileName)
if err != nil {
return err
}
buffer := new(bytes.Buffer)
if err = t.Execute(buffer, data); err != nil {
return err
}
r.body = buffer.String()
return nil
}
func (r *Request) sendMail() bool {
body := "To: " + r.to[0] + "\r\nSubject: " + r.subject + "\r\n" + MIME + "\r\n" + r.body
SMTP := fmt.Sprintf("%s:%d", config.Server, config.Port)
if err := smtp.SendMail(SMTP, smtp.PlainAuth("", config.Email, config.Password, config.Server), config.Email, r.to, []byte(body)); err != nil {
return false
}
return true
}
func (r *Request) Send(templateName string, items interface{}) bool {
err := r.parseTemplate(templateName, items)
if err != nil {
log.Fatal(err)
return false
}
if ok := r.sendMail(); ok {
log.Printf("Email has been sent to %s\n", r.to)
return true
} else {
log.Printf("Failed to send the email to %s\n", r.to)
return false
}
}
@@ -18,7 +18,7 @@ var idCollection *mongo.Collection = database.OpenCollection(database.Client, "c
var table = [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}
func ValidateID(id string) bool { // true: valid id, false: id already in use
func ValidateID(id string, userType int) bool { // true: valid id, false: id already in use
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
var foundID models.Id
err := idCollection.FindOne(ctx, bson.M{"cid": id}).Decode(&foundID)
@@ -26,6 +26,7 @@ func ValidateID(id string) bool { // true: valid id, false: id already in use
if err != nil { // If there is no id found create new ID object to be stored and return true (unless insert error then try again)
var newID models.Id
newID.CID = id
newID.ParentType = userType
newID.ID = primitive.NewObjectID()
_, insertErr := idCollection.InsertOne(context.Background(), newID)
if insertErr != nil {
File diff suppressed because it is too large. Load diff
+3 -4
View File
@@ -3,18 +3,17 @@ module github.com/SowinskiBraeden/school-management-api
go 1.16
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gofiber/fiber/v2 v2.26.0
github.com/golang-jwt/jwt v3.2.1+incompatible
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/uuid v1.3.0
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef
github.com/joho/godotenv v1.3.0
github.com/stretchr/testify v1.7.0 // indirect
github.com/valyala/fasthttp v1.34.0 // indirect
go.mongodb.org/mongo-driver v1.7.2
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)
+17 -10
View File
@@ -1,11 +1,10 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E=
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
@@ -34,6 +33,8 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/gofiber/fiber/v2 v2.26.0 h1:Awnfqp3fqbZzV3wZWMRJ6Xo2U8X0Ls68M7tXjx52NcM=
github.com/gofiber/fiber/v2 v2.26.0/go.mod h1:7efVWcBOZi1PyMWznnbitjnARPA7nYZxmQXJVod0bo0=
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -50,8 +51,9 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.13.4 h1:0zhec2I8zGnjWcKyLl6i3gPqKANCCn5e9xmviEEeX6s=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -86,8 +88,9 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.32.0 h1:keswgWzyKyNIIjz2a7JmCYHOOIkRp6HMx9oTV6QrZWY=
github.com/valyala/fasthttp v1.32.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4=
github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
@@ -105,12 +108,14 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -128,10 +133,12 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"github.com/gofiber/fiber/v2/middleware/cors"
)
const version string = "\nv0.8.8-Alpha"
const version string = "\nv1.0.0-Beta"
func main() {
fmt.Println(version)
+3 -1
View File
@@ -2,6 +2,7 @@ package models
import (
"math/rand"
"os"
"strings"
"time"
"unicode"
@@ -25,7 +26,8 @@ type Admin struct {
}
func (a *Admin) GenerateSchoolEmail() string {
var email string = strings.ToLower(a.LastName) + "_" + strings.ToLower(string(a.FirstName[0])) + "@surreyschools.ca"
addr := os.Getenv("SYSTEM_EMAIL_ADDRESS")
var email string = strings.ToLower(a.LastName) + "_" + strings.ToLower(string(a.FirstName[0])) + addr
// Add check to see if email already exists
return email
}
+2 -1
View File
@@ -16,5 +16,6 @@ import (
type Id struct {
ID primitive.ObjectID `bson:"_id"`
CID string `bson:"cid"` // custom id for admin, teahcer or student
CID string `json:"cid"` // custom id for admin, teahcer or student
ParentType int `json:"parenttype"` // A number representing the user (1: student, 2: teacher, 3: admin)
}
+7 -4
View File
@@ -3,6 +3,7 @@ package models
import (
"context"
"math/rand"
"os"
"strconv"
"strings"
"time"
@@ -48,6 +49,7 @@ type Student struct {
SchoolEmail string `json:"schoolemail"`
Password string `json:"-" validate:"min=10,max=32"`
AccountDisabled bool `bson:"accountdisabled"`
Alerted bool `bson:"alerted"`
TempPassword bool `json:"temppassword"`
Attempts int `json:"attempts"` // login attempts max 5
HashHistory []string `json:"-"` // List of old hashed passwords (not including auto generated passwords)
@@ -80,15 +82,16 @@ func (s *Student) EmailExists(email string) bool {
}
func (s *Student) GenerateSchoolEmail(offset int, lastEmail string) string {
var email string = strings.ToLower(string(s.PersonalData.FirstName[0])) + "." + strings.ToLower(s.PersonalData.LastName) + "@surreyschools.ca"
addr := os.Getenv("SYSTEM_EMAIL_ADDRESS")
var email string = strings.ToLower(string(s.PersonalData.FirstName[0])) + "." + strings.ToLower(s.PersonalData.LastName) + addr
if offset > 0 && offset < len([]rune(s.PersonalData.FirstName))-1 {
email = lastEmail[:offset] + strings.ToLower(string(s.PersonalData.FirstName[offset])) + lastEmail[offset:]
email = lastEmail[:offset] + strings.ToLower(string(s.PersonalData.FirstName[offset])) + lastEmail[offset:] + addr
}
if offset == len([]rune(s.PersonalData.FirstName))-1 {
email = strings.ToLower(s.PersonalData.FirstName) + "." + strings.ToLower(s.PersonalData.LastName) + "@surreyschools.ca"
email = strings.ToLower(s.PersonalData.FirstName) + "." + strings.ToLower(s.PersonalData.LastName) + addr
}
if offset > len([]rune(s.PersonalData.FirstName))-1 {
email = strings.ToLower(s.PersonalData.FirstName) + "." + strings.ToLower(s.PersonalData.LastName) + strconv.Itoa(offset-len([]rune(s.PersonalData.FirstName))) + "@surreyschools.ca"
email = strings.ToLower(s.PersonalData.FirstName) + "." + strings.ToLower(s.PersonalData.LastName) + strconv.Itoa(offset-len([]rune(s.PersonalData.FirstName))) + addr
}
return email
}
+6 -4
View File
@@ -3,6 +3,7 @@ package models
import (
"context"
"math/rand"
"os"
"strconv"
"strings"
"time"
@@ -79,15 +80,16 @@ func (t *Teacher) EmailExists(email string) bool {
}
func (t *Teacher) GenerateSchoolEmail(offset int, lastEmail string) string {
var email string = strings.ToLower(t.PersonalData.LastName) + "_" + strings.ToLower(string(t.PersonalData.FirstName[0])) + "@surreyschools.ca"
addr := os.Getenv("SYSTEM_EMAIL_ADDRESS")
var email string = strings.ToLower(t.PersonalData.LastName) + "_" + strings.ToLower(string(t.PersonalData.FirstName[0])) + addr
if offset > 0 && offset < len([]rune(t.PersonalData.FirstName))-1 {
email = lastEmail[:strings.Index(lastEmail, "_")+offset+1] + strings.ToLower(string(t.PersonalData.FirstName[offset])) + "@surreyschools.ca"
email = lastEmail[:strings.Index(lastEmail, "_")+offset+1] + strings.ToLower(string(t.PersonalData.FirstName[offset])) + addr
}
if offset == len([]rune(t.PersonalData.FirstName))-1 {
email = strings.ToLower(t.PersonalData.LastName) + "_" + strings.ToLower(t.PersonalData.FirstName) + "@surreyschools.ca"
email = strings.ToLower(t.PersonalData.LastName) + "_" + strings.ToLower(t.PersonalData.FirstName) + addr
}
if offset > len([]rune(t.PersonalData.FirstName))-1 {
email = strings.ToLower(t.PersonalData.LastName) + "_" + strings.ToLower(t.PersonalData.FirstName) + strconv.Itoa(offset-len([]rune(t.PersonalData.FirstName))) + "@surreyschools.ca"
email = strings.ToLower(t.PersonalData.LastName) + "_" + strings.ToLower(t.PersonalData.FirstName) + strconv.Itoa(offset-len([]rune(t.PersonalData.FirstName))) + addr
}
return email
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+7 -2
View File
@@ -72,10 +72,15 @@ func Setup(app *fiber.App) {
app.Post(routerPrefix+"/admin/create", controllers.CreateAdmin)
app.Post(routerPrefix+"/admin/login", controllers.AdminLogin)
// Admin Update Handler
app.Post(routerPrefix+"/admin/updateName", controllers.UpdateAdminName)
app.Post(routerPrefix+"/admin/updateEmail", controllers.UpdateAdminEmail)
app.Post(routerPrefix+"/admin/updatePassword", controllers.UpdateAdminPassword)
// General Command Handling
app.Post(routerPrefix+"/admin/updateLockerCombo", controllers.UpdateLockerCombo)
app.Post(routerPrefix+"/admin/renableStudent", controllers.RemoveStudentsDisabled)
app.Post(routerPrefix+"/admin/renableTeacher", controllers.RemoveTeachersDisabled)
app.Post(routerPrefix+"/admin/enableStudent", controllers.RemoveStudentsDisabled)
app.Post(routerPrefix+"/admin/enableTeacher", controllers.RemoveTeachersDisabled)
// Delete Handler
app.Post(routerPrefix+"/remove/student", controllers.RemoveStudent)
+83
View File
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Account Disabled</title>
<style type="text/css">
body{
margin: 0 auto;
padding: 0;
min-width: 100%;
font-family: sans-serif;
}
table{
margin: 50px 0 50px 0;
}
.header{
height: 40px;
text-align: center;
text-transform: uppercase;
font-size: 24px;
font-weight: bold;
}
.content{
height: 100px;
font-size: 18px;
line-height: 30px;
}
.subscribe{
height: 70px;
text-align: center;
}
.button{
text-align: center;
font-size: 18px;
font-family: sans-serif;
font-weight: bold;
padding: 0 30px 0 30px;
}
.button a{
color: #FFFFFF;
text-decoration: none;
}
.buttonwrapper{
margin: 0 auto;
}
.footer{
text-transform: uppercase;
text-align: center;
height: 40px;
font-size: 14px;
font-style: italic;
}
.footer a{
color: #000000;
text-decoration: none;
font-style: normal;
}
</style>
</head>
<body bgcolor="#009587">
<table bgcolor="#FFFFFF" width="100%" border="0" cellspacing="0" cellpadding="0">
<tr class="header">
<td style="padding: 40px;">
Uh Oh! Account Disabled!
</td>
</tr>
<tr class="content">
<td style="padding:10px;">
<p>
Hi <b>{{ .username }}</b>, <br/>
Your account has been disabled due to too many incorrect login attempts, please contact an admin to enable your account.
</p>
</td>
</tr>
<tr class="footer">
<td style="padding: 40px;">
This is an automated system email // DO NOT REPLY
</td>
</tr>
</table>
</body>
</html>
+95
View File
@@ -0,0 +1,95 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Password has been changed</title>
<style type="text/css">
body{
margin: 0 auto;
padding: 0;
min-width: 100%;
font-family: sans-serif;
}
table{
margin: 50px 0 50px 0;
}
.header{
height: 40px;
text-align: center;
text-transform: uppercase;
font-size: 24px;
font-weight: bold;
}
.content{
height: 100px;
font-size: 18px;
line-height: 30px;
}
.subscribe{
height: 70px;
text-align: center;
}
.button{
text-align: center;
font-size: 18px;
font-family: sans-serif;
font-weight: bold;
padding: 0 30px 0 30px;
}
.button a{
color: #FFFFFF;
text-decoration: none;
}
.buttonwrapper{
margin: 0 auto;
}
.footer{
text-transform: uppercase;
text-align: center;
height: 40px;
font-size: 14px;
font-style: italic;
}
.footer a{
color: #000000;
text-decoration: none;
font-style: normal;
}
</style>
</head>
<body bgcolor="#009587">
<table bgcolor="#FFFFFF" width="100%" border="0" cellspacing="0" cellpadding="0">
<tr class="header">
<td style="padding: 40px;">
Password Change!
</td>
</tr>
<tr class="content">
<td style="padding:10px;">
<p>
Hi <b>{{ .username }}</b>, <br/>
Your password has been changed by the system.
</p>
</td>
</tr>
<tr class="subscribe">
<td style="padding: 20px 0 0 0;">
<h3>Your new password is:</h3>
<table bgcolor="#009587" border="0" cellspacing="0" cellpadding="0" class="buttonwrapper">
<tr>
<td class="button" height="45">
{{ .password }}
</td>
</tr>
</table>
</td>
</tr>
<tr class="footer">
<td style="padding: 40px;">
This is an automated system email // DO NOT REPLY
</td>
</tr>
</table>
</body>
</html>
+83
View File
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Password has been changed</title>
<style type="text/css">
body{
margin: 0 auto;
padding: 0;
min-width: 100%;
font-family: sans-serif;
}
table{
margin: 50px 0 50px 0;
}
.header{
height: 40px;
text-align: center;
text-transform: uppercase;
font-size: 24px;
font-weight: bold;
}
.content{
height: 100px;
font-size: 18px;
line-height: 30px;
}
.subscribe{
height: 70px;
text-align: center;
}
.button{
text-align: center;
font-size: 18px;
font-family: sans-serif;
font-weight: bold;
padding: 0 30px 0 30px;
}
.button a{
color: #FFFFFF;
text-decoration: none;
}
.buttonwrapper{
margin: 0 auto;
}
.footer{
text-transform: uppercase;
text-align: center;
height: 40px;
font-size: 14px;
font-style: italic;
}
.footer a{
color: #000000;
text-decoration: none;
font-style: normal;
}
</style>
</head>
<body bgcolor="#009587">
<table bgcolor="#FFFFFF" width="100%" border="0" cellspacing="0" cellpadding="0">
<tr class="header">
<td style="padding: 40px;">
Password Change!
</td>
</tr>
<tr class="content">
<td style="padding:10px;">
<p>
Hi <b>{{ .username }}</b>, <br/>
Your password has been changed by you. If you did not change your password, contact an admin immediatley!
</p>
</td>
</tr>
<tr class="footer">
<td style="padding: 40px;">
This is an automated system email // DO NOT REPLY
</td>
</tr>
</table>
</body>
</html>
+1 -11
View File
@@ -1,13 +1,3 @@
# Testing
Hello there! If you are in this part of the codespace, I have cleaned it up compared to the
original codespace. The algorithms have been moved out of `tinker.py` into their own python
script from version 1 to 3. These are in the `/scheduleGenerator` folder. I suggest to look
at the latest work in `v3.py` where the algorithm has come a long way. Using real 2018 course
selection data from my school I am able to test the script to its full potential compared to
V1 and V2.
V3 has a entirely different approach from V1 and V2, that you can read about at the top of
the function in `v3.py`. The function is broken up into 6 steps, each step is labeled within
the function with a comment, giving a brief explination of what that step contributes to the
algorithm.
If you would like to view this code more neatly, it has been moved to its own [repository](https://github.com/SowinskiBraeden/schedule-generator).