Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions lib/services/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type AccessToken = {
role: string;
name: string;
};
function generateAccessToken(user: User) {
function generateAccessToken(user: User): string {
return jwt.sign({
exp: Math.floor(Date.now()/1000) + (60*20), // In seconds, 20m expiration
iss: 'mustachebash',
Expand All @@ -49,7 +49,7 @@ function generateAccessToken(user: User) {
config.jwt.secret);
}

export function validateAccessToken(accessToken: string) {
export function validateAccessToken(accessToken: string): AccessToken {
const tokenPayload = jwt.verify(accessToken, config.jwt.secret, {issuer: 'mustachebash'});

if(
Expand All @@ -66,7 +66,7 @@ type RefreshToken = {
jti: string;
sub: string;
};
function generateRefreshToken(user: User, jti: string) {
function generateRefreshToken(user: User, jti: string): string {
return jwt.sign({
exp: Math.floor(Date.now()/1000) + (60*60*24*30), // In seconds, 30d expiration
iss: 'mustachebash',
Expand All @@ -77,7 +77,7 @@ function generateRefreshToken(user: User, jti: string) {
config.jwt.secret);
}

function validateRefreshToken(refreshToken: string) {
function validateRefreshToken(refreshToken: string): RefreshToken {
const tokenPayload = jwt.verify(refreshToken, config.jwt.secret, {issuer: 'mustachebash', audience: 'mustachebash-refresh'});

if(
Expand All @@ -91,7 +91,7 @@ function validateRefreshToken(refreshToken: string) {
return tokenPayload as RefreshToken;
}

export async function authenticateGoogleUser(token: string, {userAgent, ip}: {userAgent: string, ip: string}) {
export async function authenticateGoogleUser(token: string, {userAgent, ip}: {userAgent: string; ip: string;}): Promise<{accessToken: string; refreshToken: string}> {
if(!token) throw new AuthServiceError('Missing token', 'UNAUTHORIZED');

let payload: TokenPayload | undefined, googleUserId: string | null;
Expand Down Expand Up @@ -192,15 +192,15 @@ export async function authenticateGoogleUser(token: string, {userAgent, ip}: {us
return {accessToken, refreshToken};
}

export async function refreshAccessToken(refreshToken: string, {userAgent, ip}: {userAgent: string, ip: string}) {
export async function refreshAccessToken(refreshToken: string, {userAgent, ip}: {userAgent: string, ip: string}): Promise<string> {
let sub: string, jti: string;
try {
({ sub, jti } = validateRefreshToken(refreshToken));
} catch(e) {
throw new AuthServiceError('Invalid refresh token', 'UNAUTHORIZED');
}

let user;
let user: User;
try {
[user] = await sql<User[]>`
SELECT id, display_name, role, sub_claim
Expand All @@ -211,9 +211,9 @@ export async function refreshAccessToken(refreshToken: string, {userAgent, ip}:
throw new AuthServiceError('Failed to query for user', 'DB_ERROR', e);
}

let refreshTokenData;
let refreshTokenData: {userId: string} | undefined;
try {
[refreshTokenData] = await sql`
[refreshTokenData] = await sql<{userId: string}[]>`
SELECT user_id
FROM refresh_tokens
WHERE id = ${jti}
Expand All @@ -240,7 +240,7 @@ export async function refreshAccessToken(refreshToken: string, {userAgent, ip}:
return generateAccessToken(user);
}

export function checkScope(userRole: string, scopeRequired: string) {
export function checkScope(userRole: string, scopeRequired: string): boolean {
const roles = [
'root',
'god',
Expand All @@ -255,10 +255,17 @@ export function checkScope(userRole: string, scopeRequired: string) {
return ~userLevel && userLevel <= roles.indexOf(scopeRequired);
}

export async function getUsers() {
let users;
type UserRecord = User & {
status: string;
created: Date;
updated: Date;
username: string;
};

export async function getUsers(): Promise<UserRecord[]> {
let users: UserRecord[];
try {
users = await sql`
users = await sql<UserRecord[]>`
SELECT id, username, display_name, role, status, created, updated
FROM users
`;
Expand All @@ -269,10 +276,10 @@ export async function getUsers() {
return users;
}

export async function getUser(id: string) {
let user;
export async function getUser(id: string): Promise<UserRecord> {
let user: UserRecord;
try {
[user] = await sql`
[user] = await sql<UserRecord[]>`
SELECT id, username, display_name, role, status, created, updated
FROM users
WHERE id = ${id}
Expand Down
45 changes: 31 additions & 14 deletions lib/services/customers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class CustomerServiceError extends Error {
code: string;
context?: unknown;

constructor(message = 'An unknown error occured', code = 'UNKNOWN', context) {
constructor(message = 'An unknown error occured', code = 'UNKNOWN', context?: unknown) {
super(message);

this.name = this.constructor.name;
Expand All @@ -32,12 +32,30 @@ const customerColumns = [
'meta'
];


export async function createCustomer({ firstName, lastName, email, meta }) {
export type Customer = {
id: string;
email: string;
firstName: string;
lastName: string;
created: Date;
updated: Date;
updatedBy: string | null;
meta: Record<string, unknown>;
};

type CustomerInput = Pick<Customer, 'firstName' | 'lastName' | 'email'> & {
meta?: Record<string, unknown>;
};

type CustomerUpdate = Partial<Pick<Customer, 'firstName' | 'lastName' | 'email' | 'meta'>> & {
updatedBy?: string;
};

export async function createCustomer({ firstName, lastName, email, meta }: CustomerInput): Promise<Customer> {
if(!firstName || !lastName || !email) throw new CustomerServiceError('Missing customer data', 'INVALID');
if(!/.+@.+\..{2,}/.test(email)) throw new CustomerServiceError('Invalid email', 'INVALID');

const customer = {
const customer: Omit<Customer, 'created' | 'updated' | 'updatedBy'> = {
id: uuidV4(),
firstName,
lastName,
Expand All @@ -48,7 +66,7 @@ export async function createCustomer({ firstName, lastName, email, meta }) {
};

try {
const [createdCustomer] = (await sql`
const [createdCustomer] = (await sql<Customer[]>`
INSERT INTO customers ${sql(customer)}
RETURNING ${sql(customerColumns)}
`);
Expand All @@ -59,9 +77,9 @@ export async function createCustomer({ firstName, lastName, email, meta }) {
}
}

export async function getCustomers() {
export async function getCustomers(): Promise<Customer[]> {
try {
const customers = await sql`
const customers = await sql<Customer[]>`
SELECT ${sql(customerColumns)}
FROM customers
`;
Expand All @@ -72,10 +90,10 @@ export async function getCustomers() {
}
}

export async function getCustomer(id) {
let customer;
export async function getCustomer(id: string): Promise<Customer> {
let customer: Customer;
try {
[customer] = (await sql`
[customer] = (await sql<Customer[]>`
SELECT ${sql(customerColumns)}
FROM customers
WHERE id = ${id}
Expand All @@ -89,7 +107,7 @@ export async function getCustomer(id) {
return customer;
}

export async function updateCustomer(id, updates) {
export async function updateCustomer(id: string, updates: CustomerUpdate): Promise<Customer> {
for(const u in updates) {
// Update whitelist
if(![
Expand All @@ -103,9 +121,9 @@ export async function updateCustomer(id, updates) {

if(Object.keys(updates).length === 1 && updates.updatedBy) throw new CustomerServiceError('Invalid customer data', 'INVALID');

let customer;
let customer: Customer;
try {
[customer] = (await sql`
[customer] = (await sql<Customer[]>`
UPDATE customers
SET ${sql(updates)}, updated = now()
WHERE id = ${id}
Expand All @@ -119,4 +137,3 @@ export async function updateCustomer(id, updates) {

return customer;
}

13 changes: 10 additions & 3 deletions lib/services/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ import MailChimpClient from 'mailchimp-api-v3';
const mailgun = MailgunJs({apiKey: config.mailgun.apiKey, domain: config.mailgun.domain});

const mailchimp = new MailChimpClient(config.mailchimp.apiKey),
md5 = string => crypto.createHash('md5').update(string).digest('hex');
md5 = (string: string) => crypto.createHash('md5').update(string).digest('hex');

type EmailSubscriber = {
email: string;
firstName: string;
lastName: string;
tags?: string[];
};

/* eslint-disable max-len */
/**
Expand All @@ -25,7 +32,7 @@ const mailchimp = new MailChimpClient(config.mailchimp.apiKey),
* @param {Array} [tags=[] string }] tags to apply to member
* @return {Promise}
*/
export async function upsertEmailSubscriber(listId, { email, firstName, lastName, tags = [] }) {
export async function upsertEmailSubscriber(listId: string, { email, firstName, lastName, tags = [] }: EmailSubscriber): Promise<void> {
const memberHash = md5(email.toLowerCase());

try {
Expand All @@ -50,7 +57,7 @@ export async function upsertEmailSubscriber(listId, { email, firstName, lastName
}
}

export function sendReceipt(guestFirstName, guestLastName, guestEmail, confirmation, orderId, orderToken, amount) {
export function sendReceipt(guestFirstName: string, guestLastName: string, guestEmail: string, confirmation: string, orderId: string, orderToken: string, amount: number): void {
mailgun.messages().send({
from: 'Mustache Bash Tickets <contact@mustachebash.com>',
to: guestFirstName + ' ' + guestLastName + ' <' + guestEmail + '> ',
Expand Down
Loading
Loading