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 @@
+
+
+ We've got your application!
+
+
+
+
+
+
+
+
+
+ Hey there! 👋
+
+
+
+ You've been invited to create an account on our sponsor
+ dashboard. 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.
+
+
+
+ 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 https://discord.gg/XVSdW9pc
+ and contact your coordinator to set up your server roles.
+
+
+
+ Get access to the sponsor dashboard by clicking on the button below.
+
+
+
+
+ Create your account
+
+
+
+
+ If you have any questions, feel free to reach out to your coordinator.
+
+
+
+ McHacks Team
+
+ mchacks.ca
+
+
+
+
+
+
+
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 f94ffdeb..48d823f2 100644
--- a/src/controllers/account.controller.ts
+++ b/src/controllers/account.controller.ts
@@ -29,6 +29,7 @@ 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")
@@ -56,20 +57,25 @@ export class AccountController {
);
if (inviter) {
- const invitation = await this.invitationService.save({email, accountType, inviter});
+ const invitation = await this.invitationService.save({
+ email,
+ accountType,
+ inviter
+ });
await this.mailer.send(
{
to: invitation.email,
- subject: "Account Confirmation Instructions",
+ subject: "Account Creation Instructions",
html: join(
__dirname,
- "../assets/email/AccountConfirmation.mjml"
+ "../assets/email/AccountInvitation.mjml"
)
},
{
- link: this.accountConfirmationService.generateLink(
- "confirm",
- this.invitationService.generateToken(invitation)
+ link: this.invitationService.generateLink(
+ "account/create",
+ this.invitationService.generateToken(invitation),
+ accountType
)
},
(error?: any) => {
@@ -86,7 +92,7 @@ export class AccountController {
});
} else {
return response.status(404).json({
- message: ErrorConstants.ACCOUNT_404_MESSAGE,
+ message: ErrorConstants.ACCOUNT_404_MESSAGE
});
}
}
@@ -186,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);
}
}
@@ -212,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
diff --git a/src/controllers/search.controller.ts b/src/controllers/search.controller.ts
index ae0d84f6..69c847dc 100644
--- a/src/controllers/search.controller.ts
+++ b/src/controllers/search.controller.ts
@@ -25,7 +25,10 @@ export class SearchController {
@Query("model") model: string,
@Query("q") filters: string
) {
- const result = await this.searchService.executeQuery(model, JSON.parse(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 c92687f4..4ab2664d 100644
--- a/src/controllers/setting.controller.ts
+++ b/src/controllers/setting.controller.ts
@@ -21,7 +21,7 @@ import { Validator } from "@app/middlewares/validator.middleware";
@autoInjectable()
@Controller("/settings")
export class SettingController {
- constructor(private readonly settingService: SettingService) { }
+ constructor(private readonly settingService: SettingService) {}
@Get("/")
async getAll(@Response() response: ExpressResponse) {
@@ -55,17 +55,17 @@ export class SettingController {
return result
? response.status(200).send({
- message: SuccessConstants.SETTINGS_POST,
- data: result
- })
+ message: SuccessConstants.SETTINGS_POST,
+ data: result
+ })
: response.status(422).send({
- message: ErrorConstants.SETTINGS_422_MESSAGE
- });
+ message: ErrorConstants.SETTINGS_422_MESSAGE
+ });
}
@Patch("", [
EnsureAuthenticated,
- EnsureAuthorization([AuthorizationLevel.Staff]),
+ EnsureAuthorization([AuthorizationLevel.Staff])
])
async update(
@Response() response: ExpressResponse,
@@ -74,7 +74,7 @@ export class SettingController {
await this.settingService.update(settings);
return response.status(200).json({
message: SuccessConstants.SETTINGS_PATCH
- })
+ });
}
@Patch("/:identifier", [
@@ -91,14 +91,14 @@ export class SettingController {
return result
? response.status(200).json({
- message: SuccessConstants.SETTINGS_PATCH,
- data: result
- })
+ message: SuccessConstants.SETTINGS_PATCH,
+ data: result
+ })
: response.status(404).json({
- message: ErrorConstants.SETTINGS_404_MESSAGE,
- data: {
- identifier: identifier
- }
- });
+ message: ErrorConstants.SETTINGS_404_MESSAGE,
+ data: {
+ identifier: identifier
+ }
+ });
}
}
diff --git a/src/controllers/sponsor.controller.ts b/src/controllers/sponsor.controller.ts
index 605ad572..8789f82e 100644
--- a/src/controllers/sponsor.controller.ts
+++ b/src/controllers/sponsor.controller.ts
@@ -14,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 { Request as ExpressRequest, 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";
@@ -22,7 +25,7 @@ import { Validator } from "@app/middlewares/validator.middleware";
@autoInjectable()
@Controller("/sponsor")
export class SponsorController {
- constructor(private readonly sponsorService: SponsorService) { }
+ constructor(private readonly sponsorService: SponsorService) {}
@Get("/self", [
EnsureAuthenticated,
@@ -33,22 +36,22 @@ export class SponsorController {
])
async getSelf(
@Request() request: ExpressRequest,
- @Response() response: ExpressResponse,
+ @Response() response: ExpressResponse
) {
const sponsor:
| Sponsor
| undefined = await this.sponsorService.findByIdentifier(
- //@ts-ignore
- request.user?.identifier
- );
+ //@ts-ignore
+ request.user?.identifier
+ );
return sponsor
? response.status(200).json({
- message: SuccessConstants.SPONSOR_READ,
- data: sponsor
- })
+ message: SuccessConstants.SPONSOR_READ,
+ data: sponsor
+ })
: response.status(404).json({
- message: ErrorConstants.SPONSOR_404_MESSAGE
- });
+ message: ErrorConstants.SPONSOR_404_MESSAGE
+ });
}
@Get("/:identifier", [
@@ -65,17 +68,17 @@ export class SponsorController {
const sponsor:
| Sponsor
| undefined = await this.sponsorService.findByIdentifier(
- identifier
- );
+ identifier
+ );
return sponsor
? response.status(200).json({
- message: SuccessConstants.SPONSOR_READ,
- data: sponsor
- })
+ message: SuccessConstants.SPONSOR_READ,
+ data: sponsor
+ })
: response.status(404).json({
- message: ErrorConstants.SPONSOR_404_MESSAGE
- });
+ message: ErrorConstants.SPONSOR_404_MESSAGE
+ });
}
@Post("/", [
@@ -91,12 +94,12 @@ export class SponsorController {
return result
? response.status(200).send({
- message: SuccessConstants.SPONSOR_CREATE,
- data: result
- })
+ message: SuccessConstants.SPONSOR_CREATE,
+ data: result
+ })
: response.status(422).send({
- message: ErrorConstants.ACCOUNT_DUPLICATE_422_MESSAGE
- });
+ message: ErrorConstants.ACCOUNT_DUPLICATE_422_MESSAGE
+ });
}
@Patch("/:identifier", [
@@ -116,14 +119,14 @@ export class SponsorController {
return result
? response.status(200).json({
- message: SuccessConstants.SPONSOR_UPDATE,
- data: result
- })
+ message: SuccessConstants.SPONSOR_UPDATE,
+ data: result
+ })
: response.status(404).json({
- message: ErrorConstants.SPONSOR_404_MESSAGE,
- data: {
- identifier: identifier
- }
- });
+ message: ErrorConstants.SPONSOR_404_MESSAGE,
+ data: {
+ identifier: identifier
+ }
+ });
}
}
diff --git a/src/models/invitation.model.ts b/src/models/invitation.model.ts
index 3e94b48e..6f13d04b 100644
--- a/src/models/invitation.model.ts
+++ b/src/models/invitation.model.ts
@@ -5,18 +5,18 @@ import { UserType } from "@constants/general.constant";
@Entity()
class Invitation {
- @PrimaryColumn()
- @IsEmail()
- email: string;
+ @PrimaryColumn()
+ @IsEmail()
+ email: string;
- @Column({
- enum: UserType,
- default: UserType.Hacker
- })
- accountType: string;
+ @Column({
+ enum: UserType,
+ default: UserType.Hacker
+ })
+ accountType: string;
- @ManyToOne(() => Account)
- inviter: Account;
+ @ManyToOne(() => Account)
+ inviter: Account;
}
-export default Invitation;
\ No newline at end of file
+export default Invitation;
diff --git a/src/services/hacker.service.ts b/src/services/hacker.service.ts
index 0b2c24a4..6b952f43 100644
--- a/src/services/hacker.service.ts
+++ b/src/services/hacker.service.ts
@@ -50,7 +50,8 @@ export class HackerService {
.createQueryBuilder()
.update()
.set({
- application: () => `jsonb_set(application, '${path}', '"${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
index 63370e67..f506daad 100644
--- a/src/services/invitation.service.ts
+++ b/src/services/invitation.service.ts
@@ -1,30 +1,50 @@
import Invitation from "@app/models/invitation.model";
import { autoInjectable, singleton } from "tsyringe";
-import { getRepository, Repository } from "typeorm";
+import { DeleteResult, getRepository, Repository } from "typeorm";
import jwt from "jsonwebtoken";
@autoInjectable()
@singleton()
export class InvitationService {
- private readonly invitationRepository: Repository;
+ private readonly invitationRepository: Repository;
- constructor() {
- this.invitationRepository = getRepository(Invitation);
- }
+ constructor() {
+ this.invitationRepository = getRepository(Invitation);
+ }
- public async find() {
- return await this.invitationRepository.find();
- }
+ public async find() {
+ return await this.invitationRepository.find();
+ }
- public async save(invitation: Invitation) {
- return await this.invitationRepository.save(invitation);
- }
+ public async save(invitation: Invitation) {
+ return await this.invitationRepository.save(invitation);
+ }
- public generateLink(route: string, token: string): string {
- return `${process.env.FRONTEND_ADDRESS}/${route}?token=${token}`;
-}
+ public async delete(email: string): Promise {
+ return await this.invitationRepository.delete(email);
+ }
+
+ public async findByIdentifier(
+ email: string
+ ): Promise {
+ return await this.invitationRepository.findOne(email);
+ }
- public generateToken(invitation: Invitation) {
- return jwt.sign(invitation, process.env.JWT_ ?? "default", { expiresIn: "1 week" });
- }
-}
\ No newline at end of file
+ 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 0adac2ed..96d043a2 100644
--- a/src/services/search.service.ts
+++ b/src/services/search.service.ts
@@ -28,8 +28,17 @@ export class SearchService {
}
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]}'`;
+ const path = param.split(".");
+ return (
+ path[0] +
+ "->" +
+ path
+ .slice(1, path.length - 1)
+ .map((s) => `'${s}'`)
+ .join("->") +
+ "->>" +
+ `'${path[path.length - 1]}'`
+ );
}
public async executeQuery(
@@ -47,7 +56,11 @@ export class SearchService {
switch (operation.toUpperCase()) {
case Operation.Equal:
case Operation.In:
- builder.andWhere(`${param} ${operation} ${this.parseValueList(value as string[])}`);
+ builder.andWhere(
+ `${param} ${operation} ${this.parseValueList(
+ value as string[]
+ )}`
+ );
break;
case Operation.Like:
builder.andWhere(`${param} ${operation} %:value%`, {
diff --git a/src/services/setting.service.ts b/src/services/setting.service.ts
index 10bfc5c9..718d875c 100644
--- a/src/services/setting.service.ts
+++ b/src/services/setting.service.ts
@@ -18,12 +18,16 @@ export class SettingService {
} else if (value == "false") {
value = false;
}
- return { [key]: value }
+ return { [key]: value };
}
- public flatten(settings: Array): { [setting: string]: string | number | boolean } {
+ public flatten(
+ settings: Array
+ ): { [setting: string]: string | number | boolean } {
const flattened = {};
- settings.forEach((setting) => Object.assign(flattened, this.flattenOne(setting)))
+ settings.forEach((setting) =>
+ Object.assign(flattened, this.flattenOne(setting))
+ );
return flattened;
}
@@ -47,7 +51,9 @@ export class SettingService {
return await this.settingRepository.save(setting);
}
- public async update(settings: { [setting: string]: string | number | boolean }) {
+ 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);
@@ -55,7 +61,7 @@ export class SettingService {
setting.value = value.toString();
await this.settingRepository.save(setting);
} else {
- await this.settingRepository.save({key, value});
+ await this.settingRepository.save({ key, value });
}
}
}