diff --git a/.gitignore b/.gitignore
index d6778586..a772488d 100755
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 node_modules
 
 # All Environment Files
+*.env
 .env.*
 
 # Google Cloud Platform Credentials
diff --git a/.husky/commit-msg b/.husky/commit-msg
index c9ff16ad..72e16c61 100755
--- a/.husky/commit-msg
+++ b/.husky/commit-msg
@@ -1 +1 @@
-npx --no -- commitlint --edit "\${1}"
+npx --no -- commitlint --edit
diff --git a/src/assets/email/AccountInvitation.mjml b/src/assets/email/AccountInvitation.mjml
new file mode 100644
index 00000000..b4bcd59c
--- /dev/null
+++ b/src/assets/email/AccountInvitation.mjml
@@ -0,0 +1,60 @@
+<mjml>
+  <mj-head>
+    <mj-title>We've got your application!</mj-title>
+    <mj-font name="Hind" href="http://fonts.googleapis.com/css?family=Hind:400" />
+  </mj-head>
+  <mj-body background-color="#F9F9F9">
+    <mj-include path="assets/email/Header.mjml" />
+    <!-- START BODY -->
+    <mj-section background-color="#FFFFFF" border="5px solid #E9E9E9">
+      <mj-column>
+        <mj-spacer />
+        <mj-text font-family="Hind" line-height="150%" font-size="14px" color="#4D4D4D">
+          Hey there! 👋
+        </mj-text>
+
+        <mj-text font-family="Hind" line-height="150%" font-size="14px" color="#4D4D4D">
+        You've been invited to create an account on our <a href="https://app.mchacks.ca/"
+        style="-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;color:#F2463A;text-decoration:none;">sponsor
+        dashboard</a>. On the sponsor dashboard you can find important
+        information about check-in, mentoring, judging, workshops, Discord, and
+        the schedule for the weekend. On the sponsor dashboard you will be able
+        to look up hacker applications and information. Additionally, if you
+        have resume access you will be able to download hacker resumes through
+        the dashboard.
+        </mj-text>
+
+        <mj-text font-family="Hind" line-height="150%" font-size="14px" color="#4D4D4D">
+          Sponsor check-in starts at 6:00 pm on Saturday, January 28th on the
+            McHacks Discord server and opening ceremonies will begin at 7:00 pm. Be
+            sure to get set up on the McHacks Discord server at <a
+                href="https://discord.gg/XVSdW9pc"
+                style="-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;color:#F2463A;text-decoration:none;">https://discord.gg/XVSdW9pc</a>
+            and contact your coordinator to set up your server roles.
+        </mj-text>
+
+        <mj-text font-family="Hind" line-height="150%" font-size="14px" color="#4D4D4D">
+        Get access to the sponsor dashboard by clicking on the button below.
+        </mj-text>
+
+        <mj-spacer />
+        <mj-button href="{{{ link }}}" font-family="Hind" background-color="#F2463A" color="white" border-radius="40px">
+          Create your account
+        </mj-button>
+        <mj-spacer />
+
+        <mj-text font-family="Hind" line-height="150%" font-size="14px" color="#4D4D4D">
+        If you have any questions, feel free to reach out to your coordinator.
+        </mj-text>
+
+        <mj-text font-family="Hind" line-height="150%" font-size="14px" color="#4D4D4D">
+          McHacks Team
+          <br />
+          <a href="https://mchacks.ca" style="color: #F2463A; text-decoration: none;">mchacks.ca</a>
+        </mj-text>
+      </mj-column>
+    </mj-section>
+    <!-- END BODY -->
+    <mj-include path="assets/email/Footer.mjml" />
+  </mj-body>
+</mjml>
diff --git a/src/constants/authorization-level.constant.ts b/src/constants/authorization-level.constant.ts
index 791ed425..7ac13322 100644
--- a/src/constants/authorization-level.constant.ts
+++ b/src/constants/authorization-level.constant.ts
@@ -1,8 +1,8 @@
 export enum AuthorizationLevel {
-  Staff = "Staff",
-  Sponsor = "Sponsor",
-  Volunteer = "Volunteer",
-  Hacker = "Hacker",
-  Account = "Account",
-  None = "None",
+    Staff = "Staff",
+    Sponsor = "Sponsor",
+    Volunteer = "Volunteer",
+    Hacker = "Hacker",
+    Account = "Account",
+    None = "None"
 }
diff --git a/src/constants/error.constant.ts b/src/constants/error.constant.ts
index 89a38941..f3228b30 100644
--- a/src/constants/error.constant.ts
+++ b/src/constants/error.constant.ts
@@ -13,7 +13,7 @@ const SPONSOR_ID_409_MESSAGE = "Conflict with sponsor accountId link";
 const VOLUNTEER_ID_409_MESSAGE = "Conflict with volunteer accountId link";
 const HACKER_ID_409_MESSAGE = "Conflict with hacker accountId link";
 const TEAM_MEMBER_409_MESSAGE =
-  "Conflict with team member being in another team";
+    "Conflict with team member being in another team";
 const TEAM_NAME_409_MESSAGE = "Conflict with team name already in use";
 const HACKER_STATUS_409_MESSAGE = "Conflict with hacker status";
 const TEAM_SIZE_409_MESSAGE = "Team full";
@@ -24,7 +24,7 @@ const VALIDATION_422_MESSAGE = "Validation failed";
 const ACCOUNT_DUPLICATE_422_MESSAGE = "Account already exists";
 const ROLE_DUPLICATE_422_MESSAGE = "Role already exists";
 const SETTINGS_422_MESSAGE =
-  "openTime must be before closeTime, and closeTime must be before confirmTime";
+    "openTime must be before closeTime, and closeTime must be before confirmTime";
 
 const ACCOUNT_TOKEN_401_MESSAGE = "Invalid token for account";
 const AUTH_401_MESSAGE = "Invalid Authentication";
@@ -49,46 +49,46 @@ const ROLE_CREATE_500_MESSAGE = "Error while creating role";
 const TRAVEL_CREATE_500_MESSAGE = "Error while creating travel";
 
 export {
-  ACCOUNT_404_MESSAGE,
-  HACKER_404_MESSAGE,
-  TEAM_404_MESSAGE,
-  RESUME_404_MESSAGE,
-  ACCOUNT_TYPE_409_MESSAGE,
-  ACCOUNT_EMAIL_409_MESSAGE,
-  SPONSOR_ID_409_MESSAGE,
-  VOLUNTEER_ID_409_MESSAGE,
-  TEAM_MEMBER_409_MESSAGE,
-  TEAM_MEMBER_422_MESSAGE,
-  VALIDATION_422_MESSAGE,
-  ACCOUNT_TOKEN_401_MESSAGE,
-  AUTH_401_MESSAGE,
-  AUTH_403_MESSAGE,
-  ACCOUNT_403_MESSAGE,
-  TEAM_UPDATE_500_MESSAGE,
-  HACKER_UPDATE_500_MESSAGE,
-  HACKER_ID_409_MESSAGE,
-  ACCOUNT_UPDATE_500_MESSAGE,
-  HACKER_CREATE_500_MESSAGE,
-  SPONSOR_404_MESSAGE,
-  SPONSOR_CREATE_500_MESSAGE,
-  TEAM_CREATE_500_MESSAGE,
-  VOLUNTEER_CREATE_500_MESSAGE,
-  EMAIL_500_MESSAGE,
-  GENERIC_500_MESSAGE,
-  ACCOUNT_DUPLICATE_422_MESSAGE,
-  LOGIN_500_MESSAGE,
-  HACKER_STATUS_409_MESSAGE,
-  TEAM_SIZE_409_MESSAGE,
-  ROLE_DUPLICATE_422_MESSAGE,
-  SETTINGS_422_MESSAGE,
-  ROLE_CREATE_500_MESSAGE,
-  TEAM_NAME_409_MESSAGE,
-  TEAM_JOIN_SAME_409_MESSAGE,
-  TEAM_READ_500_MESSAGE,
-  VOLUNTEER_404_MESSAGE,
-  SPONSOR_UPDATE_500_MESSAGE,
-  SETTINGS_404_MESSAGE,
-  SETTINGS_403_MESSAGE,
-  TRAVEL_404_MESSAGE,
-  TRAVEL_CREATE_500_MESSAGE,
+    ACCOUNT_404_MESSAGE,
+    HACKER_404_MESSAGE,
+    TEAM_404_MESSAGE,
+    RESUME_404_MESSAGE,
+    ACCOUNT_TYPE_409_MESSAGE,
+    ACCOUNT_EMAIL_409_MESSAGE,
+    SPONSOR_ID_409_MESSAGE,
+    VOLUNTEER_ID_409_MESSAGE,
+    TEAM_MEMBER_409_MESSAGE,
+    TEAM_MEMBER_422_MESSAGE,
+    VALIDATION_422_MESSAGE,
+    ACCOUNT_TOKEN_401_MESSAGE,
+    AUTH_401_MESSAGE,
+    AUTH_403_MESSAGE,
+    ACCOUNT_403_MESSAGE,
+    TEAM_UPDATE_500_MESSAGE,
+    HACKER_UPDATE_500_MESSAGE,
+    HACKER_ID_409_MESSAGE,
+    ACCOUNT_UPDATE_500_MESSAGE,
+    HACKER_CREATE_500_MESSAGE,
+    SPONSOR_404_MESSAGE,
+    SPONSOR_CREATE_500_MESSAGE,
+    TEAM_CREATE_500_MESSAGE,
+    VOLUNTEER_CREATE_500_MESSAGE,
+    EMAIL_500_MESSAGE,
+    GENERIC_500_MESSAGE,
+    ACCOUNT_DUPLICATE_422_MESSAGE,
+    LOGIN_500_MESSAGE,
+    HACKER_STATUS_409_MESSAGE,
+    TEAM_SIZE_409_MESSAGE,
+    ROLE_DUPLICATE_422_MESSAGE,
+    SETTINGS_422_MESSAGE,
+    ROLE_CREATE_500_MESSAGE,
+    TEAM_NAME_409_MESSAGE,
+    TEAM_JOIN_SAME_409_MESSAGE,
+    TEAM_READ_500_MESSAGE,
+    VOLUNTEER_404_MESSAGE,
+    SPONSOR_UPDATE_500_MESSAGE,
+    SETTINGS_404_MESSAGE,
+    SETTINGS_403_MESSAGE,
+    TRAVEL_404_MESSAGE,
+    TRAVEL_CREATE_500_MESSAGE
 };
diff --git a/src/controllers/account.controller.ts b/src/controllers/account.controller.ts
index be8086f3..48d823f2 100644
--- a/src/controllers/account.controller.ts
+++ b/src/controllers/account.controller.ts
@@ -28,6 +28,8 @@ import { join } from "path";
 import { Validator } from "@middlewares/validator.middleware";
 import AccountConfirmation from "@models/account-confirmation-token.model";
 import * as jwt from "jsonwebtoken";
+import { InvitationService } from "@app/services/invitation.service";
+import Invitation from "@app/models/invitation.model";
 
 @autoInjectable()
 @Controller("/account")
@@ -35,9 +37,79 @@ export class AccountController {
     constructor(
         private readonly accountService: AccountService,
         private readonly accountConfirmationService: AccountConfirmationService,
+        private readonly invitationService: InvitationService,
         private readonly mailer: EmailService
     ) {}
 
+    @Post("/invite", [
+        EnsureAuthenticated,
+        EnsureAuthorization([AuthorizationLevel.Staff])
+    ])
+    async createWithInvite(
+        @Request() request: ExpressRequest,
+        @Response() response: ExpressResponse,
+        @Body("email") email: string,
+        @Body("accountType") accountType: string
+    ) {
+        const inviter = await this.accountService.findByIdentifier(
+            //@ts-ignore
+            request.user?.identifier
+        );
+
+        if (inviter) {
+            const invitation = await this.invitationService.save({
+                email,
+                accountType,
+                inviter
+            });
+            await this.mailer.send(
+                {
+                    to: invitation.email,
+                    subject: "Account Creation Instructions",
+                    html: join(
+                        __dirname,
+                        "../assets/email/AccountInvitation.mjml"
+                    )
+                },
+                {
+                    link: this.invitationService.generateLink(
+                        "account/create",
+                        this.invitationService.generateToken(invitation),
+                        accountType
+                    )
+                },
+                (error?: any) => {
+                    if (error)
+                        response.status(500).send({
+                            message: ErrorConstants.EMAIL_500_MESSAGE,
+                            data: error
+                        });
+                }
+            );
+            return response.status(200).send({
+                message: SuccessConstants.ACCOUNT_INVITE,
+                data: {}
+            });
+        } else {
+            return response.status(404).json({
+                message: ErrorConstants.ACCOUNT_404_MESSAGE
+            });
+        }
+    }
+
+    @Get("/invite", [
+        EnsureAuthenticated,
+        EnsureAuthorization([AuthorizationLevel.Staff])
+    ])
+    async getInvited(@Response() response: ExpressResponse) {
+        const result = await this.invitationService.find();
+
+        response.status(200).json({
+            message: SuccessConstants.ACCOUNT_READ,
+            data: result
+        });
+    }
+
     @Get("/", [
         EnsureAuthenticated,
         EnsureAuthorization([
@@ -120,25 +192,24 @@ export class AccountController {
     async create(
         @Response() response: ExpressResponse,
         @Body() account: Account,
-        @Headers("X-Invite-Token") token?: string
+        // @Params("token") token?: string,
+        // @Params("accountType") accType?: string,
+        @Headers("token") token?: string
     ) {
         if (token) {
-            const data = jwt.verify(
-                token,
-                process.env.JWT_CONFIRM_ACC_SECRET!
-            ) as {
-                identifier: number;
+            const data = jwt.verify(token, process.env.JWT_INVITE_SECRET!) as {
+                invitation: Invitation;
             };
 
-            const result = await this.accountConfirmationService.findByIdentifier(
-                data.identifier
+            const result = await this.invitationService.findByIdentifier(
+                data.email
             );
 
             if (result) {
                 account.confirmed = true;
                 account.accountType = result.accountType;
 
-                this.accountConfirmationService.delete(result.identifier);
+                this.invitationService.delete(data.email);
             }
         }
 
@@ -146,7 +217,7 @@ export class AccountController {
 
         if (result) {
             const model = await this.accountConfirmationService.save({
-                accountType: GeneralConstants.HACKER,
+                accountType: result.accountType,
                 email: result.email,
                 confirmationType: GeneralConstants.CONFIRMATION_TYPE_ORGANIC,
                 account: result
@@ -202,6 +273,7 @@ export class AccountController {
     ) {
         //TODO - Implement resend e-mail confirmation and verification.
         //TODO - A thrifty user can update their password from here and it would not be hashed, we should attempt to block.
+        delete update.password;
         const result = await this.accountService.update(identifier, update);
 
         return result
@@ -216,64 +288,4 @@ export class AccountController {
                   }
               });
     }
-
-    @Post("/invite", [
-        EnsureAuthenticated,
-        EnsureAuthorization([AuthorizationLevel.Staff])
-    ])
-    async createWithInvite(
-        @Response() response: ExpressResponse,
-        @Body("email") email: string,
-        @Body("accountType") accountType: string
-    ) {
-        const model = await this.accountConfirmationService.save({
-            email: email,
-            accountType: accountType
-        });
-
-        await this.mailer.send(
-            {
-                to: model.email,
-                subject: "Account Confirmation Instructions",
-                html: join(
-                    __dirname,
-                    "../assets/email/AccountConfirmation.mjml"
-                )
-            },
-            {
-                link: this.accountConfirmationService.generateLink(
-                    "confirm",
-                    this.accountConfirmationService.generateToken(
-                        model.identifier,
-                        model.account!.identifier
-                    )
-                )
-            },
-            (error?: any) => {
-                if (error)
-                    response.status(500).send({
-                        message: ErrorConstants.EMAIL_500_MESSAGE,
-                        data: error
-                    });
-            }
-        );
-
-        return response.status(200).send({
-            message: SuccessConstants.ACCOUNT_INVITE,
-            data: {}
-        });
-    }
-
-    @Get("/invites", [
-        EnsureAuthenticated,
-        EnsureAuthorization([AuthorizationLevel.Staff])
-    ])
-    async getInvited(@Response() response: ExpressResponse) {
-        const result: Array<AccountConfirmation> = await this.accountConfirmationService.find();
-
-        response.status(200).json({
-            message: SuccessConstants.ACCOUNT_READ,
-            data: result
-        });
-    }
 }
diff --git a/src/controllers/hacker.controller.ts b/src/controllers/hacker.controller.ts
index 2b1da149..454c35d2 100644
--- a/src/controllers/hacker.controller.ts
+++ b/src/controllers/hacker.controller.ts
@@ -23,13 +23,18 @@ import {
 import { StorageService } from "@services/storage.service";
 import { upload } from "@middlewares/multer.middleware";
 import { Validator } from "@app/middlewares/validator.middleware";
+import { HackerStatus } from "@app/constants/general.constant";
+import { AccountService } from "@app/services/account.service";
+import Account from "@app/models/account.model";
+import { QueryFailedError } from "typeorm";
 
 @autoInjectable()
 @Controller("/hacker")
 export class HackerController {
     constructor(
         private readonly hackerService: HackerService,
-        private readonly storageService: StorageService
+        private readonly storageService: StorageService,
+        private readonly accountService: AccountService
     ) {}
 
     @Get("/self", [
@@ -91,7 +96,7 @@ export class HackerController {
         @Body() hacker: Hacker
     ) {
         //TODO - Check if applications are open when hacker is created.
-        //TODO - Fix bug where Hacker status is None as it is passed into the API. (Maybe override the status variable somehow?)
+        hacker.status = HackerStatus.Applied;
         const result: Hacker = await this.hackerService.save(hacker);
 
         return result
@@ -117,7 +122,9 @@ export class HackerController {
         @Params("identifier") identifier: number,
         @Body() update: Partial<Hacker>
     ) {
-        const result = await this.hackerService.update(identifier, update);
+        // Project only application to prevent overposting
+        const toUpdate: Partial<Hacker> = { application: update.application };
+        const result = await this.hackerService.update(identifier, toUpdate);
 
         return result
             ? response.status(200).json({
@@ -189,8 +196,8 @@ export class HackerController {
             //TODO - Implement affectedRows > 0 success check.
             const result = await this.hackerService.updateApplicationField(
                 identifier,
-                "general.URL.resume",
-                request.file
+                "{general,URL,resume}",
+                fileName
             );
 
             response.status(200).send({
@@ -199,6 +206,30 @@ export class HackerController {
             });
         }
     }
+
+    @Patch("/status/:identifier", [
+        EnsureAuthenticated,
+        EnsureAuthorization([AuthorizationLevel.Staff])
+    ])
+    async updateStatus(
+        @Params("identifier") identifier: number,
+        @Body("status") status: string,
+        @Response() response: ExpressResponse
+    ) {
+        const result = await this.hackerService.update(identifier, { status });
+
+        return result
+            ? response.status(200).json({
+                  message: SuccessConstants.HACKER_UPDATE,
+                  data: result
+              })
+            : response.status(404).json({
+                  message: ErrorConstants.HACKER_404_MESSAGE,
+                  data: {
+                      identifier: identifier
+                  }
+              });
+    }
 }
 
 //TODO - Implement statistics features, batch accept/application change features, and status change emails.
diff --git a/src/controllers/search.controller.ts b/src/controllers/search.controller.ts
index 1ae95300..69c847dc 100644
--- a/src/controllers/search.controller.ts
+++ b/src/controllers/search.controller.ts
@@ -23,9 +23,12 @@ export class SearchController {
     async execute(
         @Response() response: ExpressResponse,
         @Query("model") model: string,
-        @Body("filters") filters: Array<Filter>
+        @Query("q") filters: string
     ) {
-        const result = await this.searchService.executeQuery(model, filters);
+        const result = await this.searchService.executeQuery(
+            model,
+            JSON.parse(filters)
+        );
 
         response.status(200).send({
             message:
diff --git a/src/controllers/setting.controller.ts b/src/controllers/setting.controller.ts
index 4fb9c137..4ab2664d 100644
--- a/src/controllers/setting.controller.ts
+++ b/src/controllers/setting.controller.ts
@@ -63,17 +63,31 @@ export class SettingController {
               });
     }
 
+    @Patch("", [
+        EnsureAuthenticated,
+        EnsureAuthorization([AuthorizationLevel.Staff])
+    ])
+    async update(
+        @Response() response: ExpressResponse,
+        @Body() settings: { [setting: string]: string | number | boolean }
+    ) {
+        await this.settingService.update(settings);
+        return response.status(200).json({
+            message: SuccessConstants.SETTINGS_PATCH
+        });
+    }
+
     @Patch("/:identifier", [
         EnsureAuthenticated,
         EnsureAuthorization([AuthorizationLevel.Staff]),
         Validator(Setting)
     ])
-    async update(
+    async updateOne(
         @Response() response: ExpressResponse,
         @Params("identifier") identifier: number,
         @Body() setting: Partial<Setting>
     ) {
-        const result = await this.settingService.update(identifier, setting);
+        const result = await this.settingService.updateOne(identifier, setting);
 
         return result
             ? response.status(200).json({
diff --git a/src/controllers/sponsor.controller.ts b/src/controllers/sponsor.controller.ts
index 4f9a5581..8789f82e 100644
--- a/src/controllers/sponsor.controller.ts
+++ b/src/controllers/sponsor.controller.ts
@@ -5,6 +5,7 @@ import {
     Params,
     Patch,
     Post,
+    Request,
     Response
 } from "@decorators/express";
 import { autoInjectable } from "tsyringe";
@@ -13,7 +14,10 @@ import { EnsureAuthenticated } from "@middlewares/authenticated.middleware";
 import { EnsureAuthorization } from "@middlewares/authorization.middleware";
 import Sponsor from "@models/sponsor.model";
 import { SponsorService } from "@services/sponsor.service";
-import { Response as ExpressResponse } from "express";
+import {
+    Request as ExpressRequest,
+    Response as ExpressResponse
+} from "express";
 import * as SuccessConstants from "@constants/success.constant";
 import * as ErrorConstants from "@constants/error.constant";
 import { Validator } from "@app/middlewares/validator.middleware";
@@ -23,6 +27,33 @@ import { Validator } from "@app/middlewares/validator.middleware";
 export class SponsorController {
     constructor(private readonly sponsorService: SponsorService) {}
 
+    @Get("/self", [
+        EnsureAuthenticated,
+        EnsureAuthorization([
+            AuthorizationLevel.Staff,
+            AuthorizationLevel.Sponsor
+        ])
+    ])
+    async getSelf(
+        @Request() request: ExpressRequest,
+        @Response() response: ExpressResponse
+    ) {
+        const sponsor:
+            | Sponsor
+            | undefined = await this.sponsorService.findByIdentifier(
+            //@ts-ignore
+            request.user?.identifier
+        );
+        return sponsor
+            ? response.status(200).json({
+                  message: SuccessConstants.SPONSOR_READ,
+                  data: sponsor
+              })
+            : response.status(404).json({
+                  message: ErrorConstants.SPONSOR_404_MESSAGE
+              });
+    }
+
     @Get("/:identifier", [
         EnsureAuthenticated,
         EnsureAuthorization([
diff --git a/src/middlewares/validator.middleware.ts b/src/middlewares/validator.middleware.ts
index 2fc9a50b..6647f5f0 100644
--- a/src/middlewares/validator.middleware.ts
+++ b/src/middlewares/validator.middleware.ts
@@ -1,6 +1,6 @@
 import { Middleware } from "@decorators/express";
-import { plainToClass } from "class-transformer";
-import { validateOrReject } from "class-validator";
+import { plainToInstance } from "class-transformer";
+import { validate, validateOrReject } from "class-validator";
 import { Request, Response, NextFunction } from "express";
 import { ParamsDictionary } from "express-serve-static-core";
 import { ParsedQs } from "qs";
@@ -20,7 +20,7 @@ export function Validator(model: any): any {
             next: NextFunction
         ): Promise<void> {
             await validateOrReject(
-                plainToClass(model, request.body),
+                plainToInstance(model, request.body),
                 request.method === "PATCH"
                     ? { skipMissingProperties: true }
                     : {}
diff --git a/src/models/account-confirmation-token.model.ts b/src/models/account-confirmation-token.model.ts
index d6ca5543..a5fd29e5 100644
--- a/src/models/account-confirmation-token.model.ts
+++ b/src/models/account-confirmation-token.model.ts
@@ -4,14 +4,15 @@ import {
     Column,
     JoinColumn,
     OneToOne,
-    PrimaryGeneratedColumn
+    PrimaryGeneratedColumn,
+    PrimaryColumn
 } from "typeorm";
 import Account from "@models/account.model";
 import * as GeneralConstants from "@constants/general.constant";
 
 @Entity()
 class AccountConfirmation {
-    @PrimaryGeneratedColumn()
+    @PrimaryColumn()
     readonly identifier: number;
 
     @OneToOne(() => Account)
diff --git a/src/models/account.model.ts b/src/models/account.model.ts
index 016e2583..1fe6e253 100644
--- a/src/models/account.model.ts
+++ b/src/models/account.model.ts
@@ -55,11 +55,11 @@ class Account {
     @IsEnum(UserType)
     accountType: string;
 
-    @Column("date", { nullable: false })
+    @Column("timestamp", { nullable: false })
     birthDate: Date;
 
     @Column()
-    @IsPhoneNumber()
+    // @IsPhoneNumber()
     phoneNumber: string;
 
     toJSON() {
diff --git a/src/models/hacker.model.ts b/src/models/hacker.model.ts
index 0de1df30..da8a7d70 100644
--- a/src/models/hacker.model.ts
+++ b/src/models/hacker.model.ts
@@ -1,12 +1,22 @@
 import { HackerStatus } from "@constants/general.constant";
-import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from "typeorm";
+import {
+    Column,
+    Entity,
+    JoinColumn,
+    ManyToOne,
+    OneToOne,
+    PrimaryColumn
+} from "typeorm";
 import Account from "@models/account.model";
 import { ApplicationSchema } from "@models/application.model";
 import Team from "@models/team.model";
 
 @Entity()
 class Hacker {
-    @OneToOne(() => Account, { primary: true, cascade: true })
+    @PrimaryColumn()
+    identifier: number;
+
+    @OneToOne(() => Account, { cascade: false })
     @JoinColumn({ name: "identifier" })
     account: Account;
 
diff --git a/src/models/invitation.model.ts b/src/models/invitation.model.ts
new file mode 100644
index 00000000..6f13d04b
--- /dev/null
+++ b/src/models/invitation.model.ts
@@ -0,0 +1,22 @@
+import { IsEmail } from "class-validator";
+import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm";
+import Account from "./account.model";
+import { UserType } from "@constants/general.constant";
+
+@Entity()
+class Invitation {
+    @PrimaryColumn()
+    @IsEmail()
+    email: string;
+
+    @Column({
+        enum: UserType,
+        default: UserType.Hacker
+    })
+    accountType: string;
+
+    @ManyToOne(() => Account)
+    inviter: Account;
+}
+
+export default Invitation;
diff --git a/src/models/team.model.ts b/src/models/team.model.ts
index fa21247d..67284aa7 100644
--- a/src/models/team.model.ts
+++ b/src/models/team.model.ts
@@ -26,13 +26,11 @@ class Team {
     @JoinColumn()
     members: Array<Hacker>;
 
-    @Column({ nullable: true })
-    @IsString()
-    submission?: string;
+    @Column({ type: String, nullable: true })
+    submission?: string | null;
 
-    @Column({ nullable: true })
-    @IsString()
-    project?: string;
+    @Column({ type: String, nullable: true })
+    project?: string | null;
 }
 
 export default Team;
diff --git a/src/services/account-confirmation.service.ts b/src/services/account-confirmation.service.ts
index ef4a1193..941ad850 100644
--- a/src/services/account-confirmation.service.ts
+++ b/src/services/account-confirmation.service.ts
@@ -49,7 +49,7 @@ export class AccountConfirmationService {
     }
 
     public generateLink(route: string, token: string): string {
-        return `${process.env.FRONTEND_ADDRESS_DEV}/${route}?token=${token}`;
+        return `${process.env.FRONTEND_ADDRESS}/account/${route}?token=${token}`;
     }
 
     public generateToken(identifier: number, account: number): string {
diff --git a/src/services/hacker.service.ts b/src/services/hacker.service.ts
index 3a9f4a86..6b952f43 100644
--- a/src/services/hacker.service.ts
+++ b/src/services/hacker.service.ts
@@ -43,14 +43,15 @@ export class HackerService {
 
     public async updateApplicationField(
         identifier: number,
-        key: string,
+        path: string,
         value: any
     ): Promise<UpdateResult> {
         return await this.hackerRepository
             .createQueryBuilder()
             .update()
             .set({
-                application: () => `jsonb_set(application,${key},${value})`
+                application: () =>
+                    `jsonb_set(application, '${path}', '"${value}"')`
             })
             .where("identifier = :identifier", {
                 identifier: identifier
diff --git a/src/services/invitation.service.ts b/src/services/invitation.service.ts
new file mode 100644
index 00000000..f506daad
--- /dev/null
+++ b/src/services/invitation.service.ts
@@ -0,0 +1,50 @@
+import Invitation from "@app/models/invitation.model";
+import { autoInjectable, singleton } from "tsyringe";
+import { DeleteResult, getRepository, Repository } from "typeorm";
+import jwt from "jsonwebtoken";
+
+@autoInjectable()
+@singleton()
+export class InvitationService {
+    private readonly invitationRepository: Repository<Invitation>;
+
+    constructor() {
+        this.invitationRepository = getRepository(Invitation);
+    }
+
+    public async find() {
+        return await this.invitationRepository.find();
+    }
+
+    public async save(invitation: Invitation) {
+        return await this.invitationRepository.save(invitation);
+    }
+
+    public async delete(email: string): Promise<DeleteResult> {
+        return await this.invitationRepository.delete(email);
+    }
+
+    public async findByIdentifier(
+        email: string
+    ): Promise<Invitation | undefined> {
+        return await this.invitationRepository.findOne(email);
+    }
+
+    public generateLink(
+        route: string,
+        token: string,
+        accountType: string
+    ): string {
+        return `${process.env.FRONTEND_ADDRESS}/${route}?token=${token}&accountType=${accountType}`;
+    }
+
+    public generateToken(invitation: Invitation) {
+        return jwt.sign(
+            invitation,
+            process.env.JWT_INVITE_SECRET ?? "default",
+            {
+                expiresIn: "1 week"
+            }
+        );
+    }
+}
diff --git a/src/services/search.service.ts b/src/services/search.service.ts
index 9fc5d432..96d043a2 100644
--- a/src/services/search.service.ts
+++ b/src/services/search.service.ts
@@ -2,9 +2,9 @@ import { singleton } from "tsyringe";
 import { Connection, getConnection } from "typeorm";
 
 export interface Filter {
-    parameter: string;
+    param: string;
     operation: Operation;
-    value: string;
+    value: string | string[];
 }
 
 enum Operation {
@@ -23,6 +23,24 @@ export class SearchService {
         this.connection = getConnection();
     }
 
+    public parseValueList(values: string[]) {
+        return "(" + values.map((value) => `'${value}'`) + ")";
+    }
+
+    public parseParam(param: string) {
+        const path = param.split(".");
+        return (
+            path[0] +
+            "->" +
+            path
+                .slice(1, path.length - 1)
+                .map((s) => `'${s}'`)
+                .join("->") +
+            "->>" +
+            `'${path[path.length - 1]}'`
+        );
+    }
+
     public async executeQuery(
         model: string,
         query: Array<Filter>
@@ -31,34 +49,34 @@ export class SearchService {
         const builder = this.connection
             .getRepository(metadata)
             .createQueryBuilder(model)
-            .loadAllRelationIds();
+            .leftJoinAndSelect("hacker.account", "account");
 
-        query.forEach(({ parameter, operation, value }: Filter) => {
-            switch (operation) {
+        query.forEach(({ param, operation, value }: Filter) => {
+            param = this.parseParam(param);
+            switch (operation.toUpperCase()) {
                 case Operation.Equal:
                 case Operation.In:
-                    builder.andWhere(`:parameter :operation :value`, {
-                        parameter: parameter,
-                        operation: operation,
-                        value: value
-                    });
+                    builder.andWhere(
+                        `${param} ${operation} ${this.parseValueList(
+                            value as string[]
+                        )}`
+                    );
                     break;
                 case Operation.Like:
-                    builder.andWhere(`:parameter :operation %:value%`, {
-                        parameter: parameter,
-                        operation: operation,
-                        value: value
+                    builder.andWhere(`${param} ${operation} %:value%`, {
+                        param,
+                        operation,
+                        value
                     });
                     break;
                 case Operation.Limit:
-                    builder.skip(Number.parseInt(value));
+                    builder.skip(Number.parseInt(value as string));
                     break;
                 case Operation.Skip:
-                    builder.skip(Number.parseInt(value));
+                    builder.skip(Number.parseInt(value as string));
                     break;
             }
         });
-
         return builder.getMany();
     }
 }
diff --git a/src/services/setting.service.ts b/src/services/setting.service.ts
index e011aaba..718d875c 100644
--- a/src/services/setting.service.ts
+++ b/src/services/setting.service.ts
@@ -8,8 +8,31 @@ export class SettingService {
         this.settingRepository = getRepository(Setting);
     }
 
-    public async find(): Promise<Array<Setting>> {
-        return await this.settingRepository.find();
+    public flattenOne(setting: Setting) {
+        const key = setting.key;
+        let value: string | number | boolean = setting.value;
+        if (!isNaN(+value)) {
+            value = +value;
+        } else if (value == "true") {
+            value = true;
+        } else if (value == "false") {
+            value = false;
+        }
+        return { [key]: value };
+    }
+
+    public flatten(
+        settings: Array<Setting>
+    ): { [setting: string]: string | number | boolean } {
+        const flattened = {};
+        settings.forEach((setting) =>
+            Object.assign(flattened, this.flattenOne(setting))
+        );
+        return flattened;
+    }
+
+    public async find(): Promise<any> {
+        return this.flatten(await this.settingRepository.find());
     }
 
     public async findByIdentifier(
@@ -28,7 +51,22 @@ export class SettingService {
         return await this.settingRepository.save(setting);
     }
 
-    public async update(
+    public async update(settings: {
+        [setting: string]: string | number | boolean;
+    }) {
+        for (let [key, value] of Object.entries(settings)) {
+            value = value.toString();
+            const setting = await this.findByKey(key);
+            if (setting) {
+                setting.value = value.toString();
+                await this.settingRepository.save(setting);
+            } else {
+                await this.settingRepository.save({ key, value });
+            }
+        }
+    }
+
+    public async updateOne(
         identifier: number,
         setting: Partial<Setting>
     ): Promise<UpdateResult> {
diff --git a/src/services/storage.service.ts b/src/services/storage.service.ts
index 338df912..325354c7 100644
--- a/src/services/storage.service.ts
+++ b/src/services/storage.service.ts
@@ -6,8 +6,8 @@ import { LoggerService } from "@services/logger.service";
 @autoInjectable()
 export class StorageService {
     bucketName: string | undefined;
-    storage: any;
-    bucket: any;
+    storage: GStorage.Storage;
+    bucket: GStorage.Bucket;
 
     constructor(loggerService: LoggerService) {
         this.bucketName = process.env.BUCKET_NAME || "";
@@ -16,8 +16,7 @@ export class StorageService {
         } catch (error) {
             loggerService.getLogger().error(error);
         }
-        if (process.env.NODE_ENV !== "production")
-            this.bucket = this.storage.bucket(this.bucketName);
+        this.bucket = this.storage.bucket(this.bucketName);
     }
 
     /**
@@ -50,14 +49,16 @@ export class StorageService {
      * @param {string} filename path to file in bucket
      * @returns {Promise<[Buffer]>} the file data that was returned
      */
-    download(filename: string): Promise<Buffer> {
+    download(filename: string): Promise<[Buffer]> {
         const file = this.bucket.file(filename);
-        return new Promise((resolve, reject) => {
-            file.exists().then((doesExist: boolean) => {
+        return new Promise<[Buffer]>((resolve, reject) => {
+            file.exists().then((doesExist: [boolean]) => {
                 if (doesExist) {
                     file.download()
-                        .then(resolve)
-                        .catch(reject);
+                        .then((res) => resolve(res))
+                        .catch(() =>
+                            reject("error occured when downloading from bucket")
+                        );
                 } else {
                     reject("file does not exist");
                 }
@@ -77,9 +78,9 @@ export class StorageService {
     /**
      *
      * @param {*} filename the file that you want to check exists
-     * @returns {Promise<[Boolean]>}
+     * @returns {Promise<[boolean]>}
      */
-    exists(filename: string): boolean {
+    exists(filename: string): Promise<[boolean]> {
         const file = this.bucket.file(filename);
         return file.exists();
     }
diff --git a/src/services/team.service.ts b/src/services/team.service.ts
index ed261074..1715cc78 100644
--- a/src/services/team.service.ts
+++ b/src/services/team.service.ts
@@ -56,11 +56,26 @@ export class TeamService {
     }
 
     public async removeMember(hacker: Hacker): Promise<void> {
+        // hacker.team does return a number (it's the identifier).
+        // However, the underlying model dictates that team attribute of Hacker should be Team.
+        // I'm not sure why? I didn't want to change that incase it breaked something else.
+        // This throws a parsing error. But it will work.
+        const team = await this.findByIdentifier(hacker.team);
+
         await this.teamRepository
             .createQueryBuilder("team")
             .relation(Hacker, "team")
             .of(hacker)
             .set({ team: null });
+
+        // If the person we're removing is the last one left on the team. Clean up teams as well.
+        if (team && team.members.length == 1) {
+            await this.teamRepository
+                .createQueryBuilder("team")
+                .delete()
+                .where("name = :name", { name: team.name })
+                .execute();
+        }
     }
 
     public async update(